// This code contributed by Nadi Sarrar.

#include "zeek/analyzer/protocol/bittorrent/BitTorrent.h"

#include "zeek/analyzer/protocol/bittorrent/events.bif.h"
#include "zeek/analyzer/protocol/tcp/TCP_Reassembler.h"

namespace zeek::analyzer::bittorrent
	{

BitTorrent_Analyzer::BitTorrent_Analyzer(Connection* c)
	: analyzer::tcp::TCP_ApplicationAnalyzer("BITTORRENT", c)
	{
	interp = new binpac::BitTorrent::BitTorrent_Conn(this);
	stop_orig = stop_resp = false;
	stream_len_orig = stream_len_resp = 0;
	}

BitTorrent_Analyzer::~BitTorrent_Analyzer()
	{
	delete interp;
	}

void BitTorrent_Analyzer::Done()
	{
	analyzer::tcp::TCP_ApplicationAnalyzer::Done();

	interp->FlowEOF(true);
	interp->FlowEOF(false);
	}

void BitTorrent_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
	{
	uint64_t& this_stream_len = orig ? stream_len_orig : stream_len_resp;
	bool& this_stop = orig ? stop_orig : stop_resp;

	analyzer::tcp::TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);

	assert(TCP());

	if ( TCP()->IsPartial() )
		// punt on partial.
		return;

	if ( this_stop )
		return;

	this_stream_len += len;

	try
		{
		interp->NewData(orig, data, data + len);
		}
	catch ( binpac::Exception const& e )
		{
		const char except[] = "binpac exception: invalid handshake";
		if ( ! strncmp(e.c_msg(), except, strlen(except)) )
			// Does not look like bittorrent - silently
			// drop the connection.
			Parent()->RemoveChildAnalyzer(this);
		else
			{
			DeliverWeird(
				util::fmt("Stopping BitTorrent analysis: protocol violation (%s)", e.c_msg()),
				orig);
			this_stop = true;
			if ( stop_orig && stop_resp )
				AnalyzerViolation("BitTorrent: content gap and/or protocol violation");
			}
		}
	}

void BitTorrent_Analyzer::Undelivered(uint64_t seq, int len, bool orig)
	{
	analyzer::tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);

	// TODO: Code commented out for now. I think that shoving data that
	// is definitely wrong into the parser seems like a really bad idea.
	// The way it's currently tracking the next message offset isn't
	// compatible with new 64bit int support in binpac either.

	// uint64_t entry_offset = orig ?
	//	*interp->upflow()->next_message_offset() :
	//	*interp->downflow()->next_message_offset();
	// uint64_t& this_stream_len = orig ? stream_len_orig : stream_len_resp;
	// bool& this_stop = orig ? stop_orig : stop_resp;
	//
	// this_stream_len += len;
	//
	// if ( entry_offset < this_stream_len )
	//	{ // entry point is somewhere in the gap
	//	DeliverWeird("Stopping BitTorrent analysis: cannot recover from content gap", orig);
	//	this_stop = true;
	//	if ( stop_orig && stop_resp )
	//		AnalyzerViolation("BitTorrent: content gap and/or protocol violation");
	//	}
	// else
	//	{ // fill the gap
	//	try
	//		{
	//		u_char gap[len];
	//		memset(gap, 0, len);
	//		interp->NewData(orig, gap, gap + len);
	//		}
	//	catch ( binpac::Exception const &e )
	//		{
	//		DeliverWeird("Stopping BitTorrent analysis: filling content gap failed", orig);
	//		this_stop = true;
	//		if ( stop_orig && stop_resp )
	//			AnalyzerViolation("BitTorrent: content gap and/or protocol violation");
	//		}
	//	}
	}

void BitTorrent_Analyzer::EndpointEOF(bool is_orig)
	{
	analyzer::tcp::TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
	interp->FlowEOF(is_orig);
	}

void BitTorrent_Analyzer::DeliverWeird(const char* msg, bool orig)
	{
	if ( bittorrent_peer_weird )

		// TODO: why does bittorrent have a different set of weirds?
		EnqueueConnEvent(bittorrent_peer_weird, ConnVal(), val_mgr->Bool(orig),
		                 make_intrusive<StringVal>(msg));
	}

	} // namespace zeek::analyzer::bittorrent
