// See the file "COPYING" in the main distribution directory for copyright.

#pragma once

#include "zeek/Conn.h"
#include "zeek/IPAddr.h"
#include "zeek/analyzer/Analyzer.h"
#include "zeek/analyzer/protocol/tcp/TCP_Endpoint.h"
#include "zeek/analyzer/protocol/tcp/TCP_Flags.h"
#include "zeek/packet_analysis/protocol/tcp/TCPSessionAdapter.h"

namespace zeek::analyzer::pia
	{
class PIA_TCP;
	}

namespace zeek::analyzer::tcp
	{

class TCP_Endpoint;
class TCP_Reassembler;
class TCP_ApplicationAnalyzer;

/**
 * An abstract base class for analyzers for a protocol running on top
 * of TCP.
 */
class TCP_ApplicationAnalyzer : public analyzer::Analyzer
	{
public:
	TCP_ApplicationAnalyzer(const char* name, Connection* conn) : Analyzer(name, conn), tcp(nullptr)
		{
		}

	explicit TCP_ApplicationAnalyzer(Connection* conn) : Analyzer(conn), tcp(nullptr) { }

	~TCP_ApplicationAnalyzer() override { }

	// This may be nil if we are not directly associated with a TCP
	// analyzer (e.g., we're part of a tunnel decapsulation pipeline).
	packet_analysis::TCP::TCPSessionAdapter* TCP();

	void SetTCP(packet_analysis::TCP::TCPSessionAdapter* arg_tcp) { tcp = arg_tcp; }

	// The given endpoint's data delivery is complete.
	virtual void EndpointEOF(bool is_orig);

	// Called whenever an end enters TCP_ENDPOINT_CLOSED or
	// TCP_ENDPOINT_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(analyzer::tcp::TCP_Endpoint* endpoint,
	                              analyzer::tcp::TCP_Endpoint* peer, bool gen_event);
	virtual void ConnectionFinished(bool half_finished);
	virtual void ConnectionReset();

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

	void DeliverPacket(int len, const u_char* data, bool orig, uint64_t seq, const IP_Hdr* ip,
	                   int caplen) override;
	void Init() override;

	// This suppresses violations if the TCP connection wasn't
	// fully established.
	void AnalyzerViolation(const char* reason, const char* data = nullptr, int len = 0,
	                       zeek::Tag tag = zeek::Tag()) override;

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

private:
	packet_analysis::TCP::TCPSessionAdapter* tcp;
	};

class TCP_SupportAnalyzer : public analyzer::SupportAnalyzer
	{
public:
	TCP_SupportAnalyzer(const char* name, Connection* conn, bool arg_orig)
		: analyzer::SupportAnalyzer(name, conn, arg_orig)
		{
		}

	~TCP_SupportAnalyzer() override { }

	// These are passed on from TCPSessionAdapter.
	virtual void EndpointEOF(bool is_orig) { }
	virtual void ConnectionClosed(TCP_Endpoint* endpoint, TCP_Endpoint* peer, bool gen_event) { }
	virtual void ConnectionFinished(bool half_finished) { }
	virtual void ConnectionReset() { }
	virtual void PacketWithRST() { }
	};

class TCPStats_Endpoint
	{
public:
	explicit TCPStats_Endpoint(TCP_Endpoint* endp);

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

	RecordVal* BuildStats();

protected:
	TCP_Endpoint* endp;
	int num_pkts;
	int num_rxmit;
	int num_rxmit_bytes;
	int num_in_order;
	int num_OO;
	int num_repl;
	uint64_t max_top_seq;
	int last_id;
	int endian_type;
	};

class TCPStats_Analyzer : public tcp::TCP_ApplicationAnalyzer
	{
public:
	explicit TCPStats_Analyzer(Connection* c);
	~TCPStats_Analyzer() override;

	void Init() override;
	void Done() override;

	static analyzer::Analyzer* Instantiate(Connection* conn) { return new TCPStats_Analyzer(conn); }

protected:
	void DeliverPacket(int len, const u_char* data, bool is_orig, uint64_t seq, const IP_Hdr* ip,
	                   int caplen) override;

	TCPStats_Endpoint* orig_stats;
	TCPStats_Endpoint* resp_stats;
	};

	} // namespace zeek::analyzer::tcp
