
##! Functions and events regarding broker communication mechanisms.

%%{
#include "zeek/broker/Manager.h"
%%}

module Broker;

## Generated when something changes in the Broker sub-system.
event Broker::status%(endpoint: EndpointInfo, msg: string%);

## Generated when a new peering has been established.
event Broker::peer_added%(endpoint: EndpointInfo, msg: string%);

## Generated when an existing peer has been removed.
event Broker::peer_removed%(endpoint: EndpointInfo, msg: string%);

## Generated when an existing peering has been lost.
event Broker::peer_lost%(endpoint: EndpointInfo, msg: string%);

## Generated when a new Broker endpoint appeared.
event Broker::endpoint_discovered%(endpoint: EndpointInfo, msg: string%);

## Generated when the last path to a Broker endpoint has been lost.
event Broker::endpoint_unreachable%(endpoint: EndpointInfo, msg: string%);

## Generated when an error occurs in the Broker sub-system.
event Broker::error%(code: ErrorCode, msg: string%);

## Enumerates the possible error types.
enum ErrorCode %{
	NO_ERROR                         =   0,
	UNSPECIFIED                      =   1,
	PEER_INCOMPATIBLE                =   2,
	PEER_INVALID                     =   3,
	PEER_UNAVAILABLE                 =   4,
	PEER_DISCONNECT_DURING_HANDSHAKE =   5,
	PEER_TIMEOUT                     =   6,
	MASTER_EXISTS                    =   7,
	NO_SUCH_MASTER                   =   8,
	NO_SUCH_KEY                      =   9,
	REQUEST_TIMEOUT                  =  10,
	TYPE_CLASH                       =  11,
	INVALID_DATA                     =  12,
	BACKEND_FAILURE                  =  13,
	STALE_DATA                       =  14,
	CANNOT_OPEN_FILE                 =  15,
	CANNOT_WRITE_FILE                =  16,
	INVALID_TOPIC_KEY                =  17,
	END_OF_FILE                      =  18,
	INVALID_TAG                      =  19,
	INVALID_STATUS                   =  20,
	CAF_ERROR                        = 100,
%}

enum PeerStatus %{
	INITIALIZING,
	CONNECTING,
	CONNECTED,
	PEERED,
	DISCONNECTED,
	RECONNECTING,
%}

function Broker::__listen%(a: string, p: port%): port
	%{
	zeek::Broker::Manager::ScriptScopeGuard ssg;

	if ( ! p->IsTCP() )
		{
		zeek::emit_builtin_error("listen port must use tcp");
		return zeek::val_mgr->Port(0, TRANSPORT_UNKNOWN);
		}

	auto rval = broker_mgr->Listen(a->Len() ? a->CheckString() : "", p->Port());
	return zeek::val_mgr->Port(rval, TRANSPORT_TCP);
	%}

function Broker::__peer%(a: string, p: port, retry: interval%): bool
	%{
	zeek::Broker::Manager::ScriptScopeGuard ssg;

	if ( ! p->IsTCP() )
		{
		zeek::emit_builtin_error("remote connection port must use tcp");
		return zeek::val_mgr->False();
		}

	broker_mgr->Peer(a->CheckString(), p->Port(), retry);
	return zeek::val_mgr->True();
	%}

function Broker::__unpeer%(a: string, p: port%): bool
	%{
	zeek::Broker::Manager::ScriptScopeGuard ssg;

	if ( ! p->IsTCP() )
		{
		zeek::emit_builtin_error("remote connection port must use tcp");
		return zeek::val_mgr->False();
		}

	broker_mgr->Unpeer(a->CheckString(), p->Port());
	return zeek::val_mgr->True();
	%}

function Broker::__peers%(%): PeerInfos
	%{
	zeek::Broker::Manager::ScriptScopeGuard ssg;
	auto rval = zeek::make_intrusive<zeek::VectorVal>(zeek::id::find_type<VectorType>("Broker::PeerInfos"));
	auto i = 0;

	for ( auto& p : broker_mgr->Peers() )
		{
		const auto& pi = zeek::id::find_type<RecordType>("Broker::PeerInfo");
		const auto& ei = zeek::id::find_type<RecordType>("Broker::EndpointInfo");
		const auto& ni = zeek::id::find_type<RecordType>("Broker::NetworkInfo");
		auto peer_info = zeek::make_intrusive<zeek::RecordVal>(pi);
		auto endpoint_info = zeek::make_intrusive<zeek::RecordVal>(ei);
		auto network_info = zeek::make_intrusive<zeek::RecordVal>(ni);
		auto n = p.peer.network;

		if ( n )
			{
			network_info->Assign(0, zeek::make_intrusive<zeek::StringVal>(n->address));
			network_info->Assign(1, zeek::val_mgr->Port(n->port, TRANSPORT_TCP));
			}
		else
			{
			network_info->Assign(0, zeek::make_intrusive<zeek::StringVal>("0.0.0.0"));
			network_info->Assign(1, zeek::val_mgr->Port(0, TRANSPORT_TCP));
			}

		endpoint_info->Assign(0, to_string(p.peer.node));
		endpoint_info->Assign(1, std::move(network_info));

		auto ps = (BifEnum::Broker::PeerStatus)p.status;
		peer_info->Assign(0, std::move(endpoint_info));
		peer_info->Assign(1, zeek::BifType::Enum::Broker::PeerStatus->GetEnumVal(ps));

		rval->Assign(i, std::move(peer_info));
		++i;
		}

	return rval;
	%}

function Broker::__node_id%(%): string
	%{
	zeek::Broker::Manager::ScriptScopeGuard ssg;
	return zeek::make_intrusive<zeek::StringVal>(broker_mgr->NodeID());
	%}

function Broker::__set_metrics_export_interval%(value: interval%): bool
	%{
	// This BIF may run prior to broker::Manager::InitPostScript. In this case,
	// broker_mgr is still null but we can safely ignore this event because the
	// Manager is going to initialize Broker using the most recent value of the
	// corresponding option.
	zeek::Broker::Manager::ScriptScopeGuard ssg;
	if ( broker_mgr )
		broker_mgr->SetMetricsExportInterval(value);
	return zeek::val_mgr->True();
	%}

function Broker::__set_metrics_export_topic%(value: string%): bool
	%{
	zeek::Broker::Manager::ScriptScopeGuard ssg;
	if ( broker_mgr )
		broker_mgr->SetMetricsExportTopic(value->ToStdString());
	return zeek::val_mgr->True();
	%}

function Broker::__set_metrics_export_endpoint_name%(value: string%): bool
	%{
	zeek::Broker::Manager::ScriptScopeGuard ssg;
	if ( broker_mgr )
		broker_mgr->SetMetricsExportEndpointName(value->ToStdString());
	return zeek::val_mgr->True();
	%}

function Broker::__set_metrics_export_prefixes%(filter: string_vec%): bool
	%{
	zeek::Broker::Manager::ScriptScopeGuard ssg;
	if ( broker_mgr )
		{
		std::vector<std::string> slist;
		auto* vval = filter->AsVectorVal();
		for ( unsigned index = 0; index < vval->Size(); ++index )
			slist.emplace_back(vval->StringValAt(index)->ToStdString());
		broker_mgr->SetMetricsExportPrefixes(std::move(slist));
		}
	return zeek::val_mgr->True();
	%}
