##! A collection of built-in functions that implement a variety of things
##! such as general programming algorithms, string processing, math functions,
##! introspection, type conversion, file/directory manipulation, packet
##! filtering, interprocess communication and controlling protocol analyzer
##! behavior.
##!
##! You'll find most of Zeek's built-in functions that aren't protocol-specific
##! in this file.

%%{ // C segment
#include <math.h>
#include <vector>
#include <algorithm>
#include <cmath>
#include <sys/stat.h>
#include <cstdio>
#include <time.h>

#include "zeek/digest.h"
#include "zeek/Reporter.h"
#include "zeek/IPAddr.h"
#include "zeek/util.h"
#include "zeek/file_analysis/Manager.h"
#include "zeek/iosource/Manager.h"
#include "zeek/iosource/Packet.h"
#include "zeek/iosource/PktSrc.h"
#include "zeek/iosource/PktDumper.h"
#include "zeek/IntrusivePtr.h"
#include "zeek/input.h"
#include "zeek/Hash.h"
#include "zeek/packet_analysis/Manager.h"

using namespace std;

zeek::TableType* var_sizes;

static zeek::iosource::PktDumper* addl_pkt_dumper = nullptr;

bro_int_t parse_int(const char*& fmt)
	{
	bro_int_t k = 0;
	while ( isdigit(*fmt) )
		{
		k = k * 10 + (*fmt - '0');
		++fmt;
		}

	return k;
	}

static zeek::TypeTag ok_d_fmt[] = {
	zeek::TYPE_BOOL, zeek::TYPE_ENUM, zeek::TYPE_INT, zeek::TYPE_COUNT, zeek::TYPE_PORT,
	zeek::TYPE_SUBNET,
	zeek::TYPE_ERROR
};
static zeek::TypeTag ok_f_fmt[] = {
	zeek::TYPE_DOUBLE, zeek::TYPE_TIME, zeek::TYPE_INTERVAL,
	zeek::TYPE_ERROR
};

static int check_fmt_type(zeek::TypeTag t, zeek::TypeTag ok[])
	{
	for ( int i = 0; ok[i] != zeek::TYPE_ERROR; ++i )
		if ( ok[i] == t )
			return 1;

	return 0;
	}

static void do_fmt(const char*& fmt, zeek::Val* v, zeek::ODesc* d)
	{
	if ( ! v )
		// Help with error propagation in compiled code.
		return;

	zeek::TypeTag t = v->GetType()->Tag();
	zeek::InternalTypeTag it = v->GetType()->InternalType();

	bool zero_pad = false;
	bool left_just = false;
	int field_width = -1;

	// Left-align, if requested.
	if ( *fmt == '-' )
		{
		left_just = true;
		++fmt;
		}

	// Parse field width, if given.
	if ( isdigit(*fmt) )
		{
		// If field width starts with zero, do zero-padding.
		if ( *fmt == '0' )
			{
			zero_pad = true;
			++fmt;
			}

		field_width = parse_int(fmt);
		}

	int precision = -1;
	if ( *fmt == '.' )
		{
		++fmt;
		precision = parse_int(fmt);
		}

	if ( field_width > 128 || precision > 128 )
		{
		zeek::emit_builtin_error("excessive field width or precision");
		return;
		}

	// Create the numerical format string.
	char num_fmt[64];
	num_fmt[0] = '\0';

	if ( field_width >= 0 )
		{
		// Like sprintf(), ignore '0' if '-' is given.
		const char* align = left_just ? "-" : (zero_pad ? "0" : "");
		snprintf(num_fmt, sizeof(num_fmt), "%s%d", align, field_width);
		}

	if ( precision >= 0 )
		snprintf(num_fmt + strlen(num_fmt),
			sizeof(num_fmt) - strlen(num_fmt), ".%d", precision);

	char fmt_buf[512];
	char out_buf[512];

	zeek::ODesc s;
	s.SetStyle(zeek::RAW_STYLE);

	if ( precision >= 0 && *fmt != 'e' && *fmt != 'f' && *fmt != 'g' )
		zeek::emit_builtin_error("precision specified for non-floating point");

	switch ( *fmt ) {
	case 'D':
	case 'T':	// ISO Timestamp with microsecond precision.
		{
		if ( t != zeek::TYPE_TIME )
			{
			zeek::emit_builtin_error("bad type for Date/Time format", v);
			break;
			}

		time_t time = time_t(v->InternalDouble());
		struct tm t;

		bool is_time_fmt = *fmt == 'T';

		if ( ! localtime_r(&time, &t) )
			s.AddSP("<problem getting time>");

		if ( ! strftime(out_buf, sizeof(out_buf),
				is_time_fmt ?
					"%Y-%m-%d-%H:%M" : "%Y-%m-%d-%H:%M:%S",
				&t) )
			s.AddSP("<bad time>");

		else
			{
			s.Add(out_buf);

			if ( is_time_fmt )
				{
				double secs = v->CoerceToUnsigned() % 60;

				secs += v->InternalDouble();
				secs -= v->CoerceToUnsigned();

				snprintf(out_buf, sizeof(out_buf),
					":%012.9f", secs);
				s.Add(out_buf);
				}
			}
		}
		break;

	case 'd':
	case 'x':
		{
		if ( *fmt == 'x' && it == zeek::TYPE_INTERNAL_ADDR )
			{
			// Deficiency: we don't support num_fmt in this case.
			// This makes only a very slight difference, so not
			// clear it would e worth the hassle.

			snprintf(out_buf, sizeof(out_buf), "%s",
			         v->AsAddr().AsHexString().c_str());
			}

		else if ( ! check_fmt_type(t, ok_d_fmt) )
			{
			zeek::emit_builtin_error("bad type for %d/%x format", v);
			break;
			}

		else if ( it == zeek::TYPE_INTERNAL_UNSIGNED )
			{
			bro_uint_t u = v->CoerceToUnsigned();

			if ( v->GetType()->IsNetworkOrder() )
				{
				if ( v->GetType()->Tag() == zeek::TYPE_PORT )
					u = v->AsPortVal()->Port();
				else
					u = ntohl(uint32_t(u));
				}

			snprintf(fmt_buf, sizeof(fmt_buf), "%%%s%s", num_fmt,
					*fmt == 'd' ? "llu" : "llx");
			snprintf(out_buf, sizeof(out_buf), fmt_buf, u);
			}

		else
			{
			snprintf(fmt_buf, sizeof(fmt_buf), "%%%s%s", num_fmt,
					*fmt == 'd' ? "lld" : "llx");
			snprintf(out_buf, sizeof(out_buf), fmt_buf,
					v->CoerceToInt());
			}

		s.Add(out_buf);
		}
		break;

	case 's':
		v->Describe(&s);
		break;

	case 'e':
	case 'f':
	case 'g':
		{
		if ( ! check_fmt_type(t, ok_f_fmt) )
			{
			zeek::emit_builtin_error("bad type for floating-point format", v);
			break;
			}

		snprintf(fmt_buf, sizeof(fmt_buf), "%%%s%c", num_fmt, *fmt);
		snprintf(out_buf, sizeof(out_buf), fmt_buf, v->CoerceToDouble());
		s.Add(out_buf);
		}
		break;

	default:
		zeek::emit_builtin_error("bad format");
	}

	// Left-padding with whitespace, if any.
	if ( field_width > 0 && ! left_just )
		{
		int sl = strlen(s.Description());
		while ( ++sl <= field_width )
			d->Add(" ");
		}

	d->AddN((const char*)(s.Bytes()), s.Len());

	// Right-padding with whitespace, if any.
	if ( field_width > 0 && left_just )
		{
		int sl = s.Len();
		while ( ++sl <= field_width )
			d->Add(" ");
		}

	++fmt;
	}

static int next_fmt(const char*& fmt, const zeek::Args* args, zeek::ODesc* d, int& n)
	{
	const char* fp = fmt;

	// Skip up to next format indicator.
	while ( *fp && *fp != '%' )
		++fp;

	d->AddN(fmt, fp - fmt);

	if ( *fp == '\0' )
		// No more to do.
		return 0;

	fmt = fp + 1;
	if ( *fmt == '%' )
		{
		// "%%" -> '%'
		d->Add("%");
		++fmt;
		return next_fmt(fmt, args, d, n);
		}

	if ( ++n >= static_cast<int>(args->size()) )
		return 0;

	do_fmt(fmt, (*args)[n].get(), d);

	return *fmt != '\0';
	}
%%}

# ===========================================================================
#
#                                    Core
#
# ===========================================================================

## Returns the current wall-clock time.
##
## In general, you should use :zeek:id:`network_time` instead
## unless you are using Zeek for non-networking uses (such as general
## scripting; not particularly recommended), because otherwise your script
## may behave very differently on live traffic versus played-back traffic
## from a save file.
##
## Returns: The wall-clock time.
##
## .. zeek:see:: network_time set_network_time
function current_time%(%): time
	%{
	return zeek::make_intrusive<zeek::TimeVal>(zeek::util::current_time());
	%}

## Returns the timestamp of the last packet processed. This function returns
## the timestamp of the most recently read packet, whether read from a
## live network interface or from a save file.
##
## Returns: The timestamp of the packet processed.
##
## .. zeek:see:: current_time set_network_time
function network_time%(%): time
	%{
	return zeek::make_intrusive<zeek::TimeVal>(zeek::run_state::network_time);
	%}

## Sets the timestamp associated with the last packet processed. Used for
## event replaying.
##
## nt: The time to which to set "network time".
##
## Returns: The timestamp of the packet processed.
##
## .. zeek:see:: current_time network_time
function set_network_time%(nt: time%): bool
	%{
	zeek::run_state::network_time = nt;
	return zeek::val_mgr->True();
	%}

## Returns a system environment variable.
##
## var: The name of the variable whose value to request.
##
## Returns: The system environment variable identified by *var*, or an empty
##          string if it is not defined.
##
## .. zeek:see:: setenv
function getenv%(var: string%): string
	%{
	const char* env_val = getenv(var->CheckString());
	if ( ! env_val )
		env_val = "";	// ###
	return zeek::make_intrusive<zeek::StringVal>(env_val);
	%}

## Sets a system environment variable.
##
## var: The name of the variable.
##
## val: The (new) value of the variable *var*.
##
## Returns: True on success.
##
## .. zeek:see:: getenv
function setenv%(var: string, val: string%): bool
	%{
	int result = setenv(var->AsString()->CheckString(),
	                    val->AsString()->CheckString(), 1);

	if ( result < 0 )
		return zeek::val_mgr->False();
	return zeek::val_mgr->True();
	%}

## Shuts down the Zeek process immediately.
##
## code: The exit code to return with.
##
## .. zeek:see:: terminate
function exit%(code: int%): any
	%{
	exit(code);
	return nullptr;
	%}

## Gracefully shut down Zeek by terminating outstanding processing.
##
## Returns: True after successful termination and false when Zeek is still in
##          the process of shutting down.
##
## .. zeek:see:: exit zeek_is_terminating
function terminate%(%): bool
	%{
	if ( zeek::run_state::terminating )
		return zeek::val_mgr->False();

	zeek::util::detail::terminate_processing();
	return zeek::val_mgr->True();
	%}

%%{
// Turns the table into environment variables (if 'set' is true) or removes
// all environment variables previously generated from this table (if 'set'
// is false).
static bool prepare_environment(zeek::TableVal* tbl, bool set)
	{
	auto idxs = tbl->ToPureListVal();

	for ( int i = 0; i < idxs->Length(); ++i )
		{
		const auto& key = idxs->Idx(i);
		const auto& val = tbl->Find(key);

		if ( key->GetType()->Tag() != zeek::TYPE_STRING ||
		     val->GetType()->Tag() != zeek::TYPE_STRING )
			{
			zeek::emit_builtin_error("system_env() needs a table[string] of string");
			return false;
			}

		char* tmp = zeek::util::copy_string(key->AsString()->CheckString());
		zeek::util::to_upper(tmp);
		std::string var1 = zeek::util::fmt("ZEEK_ARG_%s", tmp);
		std::string var2 = zeek::util::fmt("BRO_ARG_%s", tmp); // legacy support
		delete [] tmp;

		if ( set )
			{
			setenv(var1.data(), val->AsString()->CheckString(), 1);
			setenv(var2.data(), val->AsString()->CheckString(), 1);
			}
		else
			{
			unsetenv(var1.data());
			unsetenv(var2.data());
			}
		}

	return true;
	}

static int do_system(const char* s)
	{
	const char* system_fmt = "(%s) 1>&2 &";	// output to stderr
	char* cmd = new char[strlen(system_fmt) + strlen(s) + 1];

	sprintf(cmd, system_fmt, s);
	int status = system(cmd);
	delete [] cmd;

	return status;
	}
%%}

## Invokes a command via the ``system`` function of the OS.
## The command runs in the background with ``stdout`` redirecting to
## ``stderr``. Here is a usage example:
## ``system(fmt("rm %s", safe_shell_quote(sniffed_data)));``
##
## str: The command to execute.
##
## Returns: The return value from the OS ``system`` function.
##
## .. zeek:see:: system_env safe_shell_quote piped_exec
##
## .. note::
##
##      Note that this corresponds to the status of backgrounding the
##      given command, not to the exit status of the command itself. A
##      value of 127 corresponds to a failure to execute ``sh``, and -1
##      to an internal system failure.
function system%(str: string%): int
	%{
	int result = do_system(str->CheckString());
	return zeek::val_mgr->Int(result);
	%}

## Invokes a command via the ``system`` function of the OS with a prepared
## environment. The function is essentially the same as :zeek:id:`system`,
## but changes the environment before invoking the command.
##
## str: The command to execute.
##
## env: A :zeek:type:`table` with the environment variables in the form
##      of key-value pairs. Each specified environment variable name
##      will be automatically prepended with ``ZEEK_ARG_``.
##
## Returns: The return value from the OS ``system`` function.
##
## .. zeek:see:: system safe_shell_quote piped_exec
function system_env%(str: string, env: table_string_of_string%): int
	%{
	if ( env->GetType()->Tag() != zeek::TYPE_TABLE )
		{
		zeek::emit_builtin_error("system_env() requires a table argument");
		return zeek::val_mgr->Int(-1);
		}

	if ( ! prepare_environment(env->AsTableVal(), true) )
		return zeek::val_mgr->Int(-1);

	int result = do_system(str->CheckString());

	prepare_environment(env->AsTableVal(), false);

	return zeek::val_mgr->Int(result);
	%}

## Opens a program with ``popen`` and writes a given string to the returned
## stream to send it to the opened process's stdin.
##
## program: The program to execute.
##
## to_write: Data to pipe to the opened program's process via ``stdin``.
##
## Returns: True on success.
##
## .. zeek:see:: system system_env
function piped_exec%(program: string, to_write: string%): bool
	%{
	const char* prog = program->CheckString();

	FILE* f = popen(prog, "w");
	if ( ! f )
		{
		zeek::reporter->Error("Failed to popen %s", prog);
		return zeek::val_mgr->False();
		}

	const u_char* input_data = to_write->Bytes();
	int input_data_len = to_write->Len();

	int bytes_written = fwrite(input_data, 1, input_data_len, f);

	pclose(f);

	if ( bytes_written != input_data_len )
		{
		zeek::reporter->Error("Failed to write all given data to %s", prog);
		return zeek::val_mgr->False();
		}

	return zeek::val_mgr->True();
	%}

%%{
#include "zeek/OpaqueVal.h"
%%}

## Computes the MD5 hash value of the provided list of arguments.
##
## Returns: The MD5 hash value of the concatenated arguments.
##
## .. zeek:see:: md5_hmac md5_hash_init md5_hash_update md5_hash_finish
##    sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
##    sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
##
## .. note::
##
##      This function performs a one-shot computation of its arguments.
##      For incremental hash computation, see :zeek:id:`md5_hash_init` and
##      friends.
function md5_hash%(...%): string
	%{
	unsigned char digest[MD5_DIGEST_LENGTH];
	MD5Val::digest(@ARG@, digest);
	return zeek::make_intrusive<zeek::StringVal>(zeek::detail::md5_digest_print(digest));
	%}

## Computes the SHA1 hash value of the provided list of arguments.
##
## Returns: The SHA1 hash value of the concatenated arguments.
##
## .. zeek:see:: md5_hash md5_hmac md5_hash_init md5_hash_update md5_hash_finish
##    sha1_hash_init sha1_hash_update sha1_hash_finish
##    sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
##
## .. note::
##
##      This function performs a one-shot computation of its arguments.
##      For incremental hash computation, see :zeek:id:`sha1_hash_init` and
##      friends.
function sha1_hash%(...%): string
	%{
	unsigned char digest[SHA_DIGEST_LENGTH];
	SHA1Val::digest(@ARG@, digest);
	return zeek::make_intrusive<zeek::StringVal>(zeek::detail::sha1_digest_print(digest));
	%}

## Computes the SHA256 hash value of the provided list of arguments.
##
## Returns: The SHA256 hash value of the concatenated arguments.
##
## .. zeek:see:: md5_hash md5_hmac md5_hash_init md5_hash_update md5_hash_finish
##    sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
##    sha256_hash_init sha256_hash_update sha256_hash_finish
##
## .. note::
##
##      This function performs a one-shot computation of its arguments.
##      For incremental hash computation, see :zeek:id:`sha256_hash_init` and
##      friends.
function sha256_hash%(...%): string
	%{
	unsigned char digest[SHA256_DIGEST_LENGTH];
	SHA256Val::digest(@ARG@, digest);
	return zeek::make_intrusive<zeek::StringVal>(zeek::detail::sha256_digest_print(digest));
	%}

## Computes an HMAC-MD5 hash value of the provided list of arguments. The HMAC
## secret key is generated from available entropy when Zeek starts up, or it can
## be specified for repeatability using the ``-K`` command line flag.
##
## Returns: The HMAC-MD5 hash value of the concatenated arguments.
##
## .. zeek:see:: md5_hash md5_hash_init md5_hash_update md5_hash_finish
##    sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
##    sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function md5_hmac%(...%): string
	%{
	unsigned char hmac[MD5_DIGEST_LENGTH];
	MD5Val::hmac(@ARG@, zeek::detail::KeyedHash::shared_hmac_md5_key, hmac);
	return zeek::make_intrusive<zeek::StringVal>(zeek::detail::md5_digest_print(hmac));
	%}

## Constructs an MD5 handle to enable incremental hash computation. You can
## feed data to the returned opaque value with :zeek:id:`md5_hash_update` and
## eventually need to call :zeek:id:`md5_hash_finish` to finish the computation
## and get the hash digest.
##
## For example, when computing incremental MD5 values of transferred files in
## multiple concurrent HTTP connections, one keeps an optional handle in the
## HTTP session record. Then, one would call
## ``c$http$md5_handle = md5_hash_init()`` once before invoking
## ``md5_hash_update(c$http$md5_handle, some_more_data)`` in the
## :zeek:id:`http_entity_data` event handler. When all data has arrived, a call
## to :zeek:id:`md5_hash_finish` returns the final hash value.
##
## Returns: The opaque handle associated with this hash computation.
##
## .. zeek:see:: md5_hmac md5_hash md5_hash_update md5_hash_finish
##    sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
##    sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function md5_hash_init%(%): opaque of md5
	%{
	auto digest = zeek::make_intrusive<zeek::MD5Val>();
	digest->Init();
	return digest;
	%}

## Constructs an SHA1 handle to enable incremental hash computation. You can
## feed data to the returned opaque value with :zeek:id:`sha1_hash_update` and
## finally need to call :zeek:id:`sha1_hash_finish` to finish the computation
## and get the hash digest.
##
## For example, when computing incremental SHA1 values of transferred files in
## multiple concurrent HTTP connections, one keeps an optional handle in the
## HTTP session record. Then, one would call
## ``c$http$sha1_handle = sha1_hash_init()`` once before invoking
## ``sha1_hash_update(c$http$sha1_handle, some_more_data)`` in the
## :zeek:id:`http_entity_data` event handler. When all data has arrived, a call
## to :zeek:id:`sha1_hash_finish` returns the final hash value.
##
## Returns: The opaque handle associated with this hash computation.
##
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
##    sha1_hash sha1_hash_update sha1_hash_finish
##    sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function sha1_hash_init%(%): opaque of sha1
	%{
	auto digest = zeek::make_intrusive<zeek::SHA1Val>();
	digest->Init();
	return digest;
	%}

## Constructs an SHA256 handle to enable incremental hash computation. You can
## feed data to the returned opaque value with :zeek:id:`sha256_hash_update` and
## finally need to call :zeek:id:`sha256_hash_finish` to finish the computation
## and get the hash digest.
##
## For example, when computing incremental SHA256 values of transferred files in
## multiple concurrent HTTP connections, one keeps an optional handle in the
## HTTP session record. Then, one would call
## ``c$http$sha256_handle = sha256_hash_init()`` once before invoking
## ``sha256_hash_update(c$http$sha256_handle, some_more_data)`` in the
## :zeek:id:`http_entity_data` event handler. When all data has arrived, a call
## to :zeek:id:`sha256_hash_finish` returns the final hash value.
##
## Returns: The opaque handle associated with this hash computation.
##
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
##    sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
##    sha256_hash sha256_hash_update sha256_hash_finish
function sha256_hash_init%(%): opaque of sha256
	%{
	auto digest = zeek::make_intrusive<zeek::SHA256Val>();
	digest->Init();
	return digest;
	%}

## Updates the MD5 value associated with a given index. It is required to
## call :zeek:id:`md5_hash_init` once before calling this
## function.
##
## handle: The opaque handle associated with this hash computation.
##
## data: The data to add to the hash computation.
##
## Returns: True on success.
##
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_finish
##    sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
##    sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function md5_hash_update%(handle: opaque of md5, data: string%): bool
	%{
	bool rc = static_cast<HashVal*>(handle)->Feed(data->Bytes(), data->Len());
	return zeek::val_mgr->Bool(rc);
	%}

## Updates the SHA1 value associated with a given index. It is required to
## call :zeek:id:`sha1_hash_init` once before calling this
## function.
##
## handle: The opaque handle associated with this hash computation.
##
## data: The data to add to the hash computation.
##
## Returns: True on success.
##
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
##    sha1_hash sha1_hash_init sha1_hash_finish
##    sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function sha1_hash_update%(handle: opaque of sha1, data: string%): bool
	%{
	bool rc = static_cast<HashVal*>(handle)->Feed(data->Bytes(), data->Len());
	return zeek::val_mgr->Bool(rc);
	%}

## Updates the SHA256 value associated with a given index. It is required to
## call :zeek:id:`sha256_hash_init` once before calling this
## function.
##
## handle: The opaque handle associated with this hash computation.
##
## data: The data to add to the hash computation.
##
## Returns: True on success.
##
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
##    sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
##    sha256_hash sha256_hash_init sha256_hash_finish
function sha256_hash_update%(handle: opaque of sha256, data: string%): bool
	%{
	bool rc = static_cast<HashVal*>(handle)->Feed(data->Bytes(), data->Len());
	return zeek::val_mgr->Bool(rc);
	%}

## Returns the final MD5 digest of an incremental hash computation.
##
## handle: The opaque handle associated with this hash computation.
##
## Returns: The hash value associated with the computation of *handle*.
##
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_update
##    sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
##    sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function md5_hash_finish%(handle: opaque of md5%): string
	%{
	return static_cast<HashVal*>(handle)->Get();
	%}

## Returns the final SHA1 digest of an incremental hash computation.
##
## handle: The opaque handle associated with this hash computation.
##
## Returns: The hash value associated with the computation of *handle*.
##
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
##    sha1_hash sha1_hash_init sha1_hash_update
##    sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function sha1_hash_finish%(handle: opaque of sha1%): string
	%{
	return static_cast<HashVal*>(handle)->Get();
	%}

## Returns the final SHA256 digest of an incremental hash computation.
##
## handle: The opaque handle associated with this hash computation.
##
## Returns: The hash value associated with the computation of *handle*.
##
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
##    sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
##    sha256_hash sha256_hash_init sha256_hash_update
function sha256_hash_finish%(handle: opaque of sha256%): string
	%{
	return static_cast<HashVal*>(handle)->Get();
	%}

## Initializes and returns a new paraglob.
##
## v: Vector of patterns to initialize the paraglob with.
##
## Returns: A new, compiled, paraglob with the patterns in *v*
##
## .. zeek:see::paraglob_match paraglob_equals paraglob_add
function paraglob_init%(v: any%) : opaque of paraglob
	%{
	if ( v->GetType()->Tag() != zeek::TYPE_VECTOR ||
	     v->GetType()->Yield()->Tag() != zeek::TYPE_STRING )
		{
		// reporter->Error will throw an exception.
		zeek::reporter->Error("paraglob requires a vector of strings for initialization.");
		return nullptr;
		}

	std::vector<std::string> patterns;
	VectorVal* vv = v->AsVectorVal();
	for ( unsigned int i = 0; i < vv->Size(); ++i )
		{
		auto s = vv->StringAt(i);
		patterns.push_back(std::string(reinterpret_cast<const char*>(s->Bytes()), s->Len()));
		}

	try
		{
		std::unique_ptr<paraglob::Paraglob> p (new paraglob::Paraglob(patterns));
		return zeek::make_intrusive<zeek::ParaglobVal>(std::move(p));
		}
	// Thrown if paraglob fails to add a pattern.
	catch (const paraglob::add_error& e)
		{
		zeek::reporter->Error("Paraglob failed to add pattern: %s", e.what());
		return nullptr;
		}
	%}

## Gets all the patterns inside the handle associated with an input string.
##
## handle: A compiled paraglob.
##
## match: string to match against the paraglob.
##
## Returns: A vector of strings matching the input string.
##
## ## .. zeek:see::paraglob_add paraglob_equals paraglob_init
function paraglob_match%(handle: opaque of paraglob, match: string%): string_vec
	%{
	return static_cast<ParaglobVal*>(handle)->Get(match);
	%}

## Compares two paraglobs for equality.
##
## p_one: A compiled paraglob.
##
## p_two: A compiled paraglob.
##
## Returns: True if both paraglobs contain the same patterns, false otherwise.
##
## ## .. zeek:see::paraglob_add paraglob_match paraglob_init
function paraglob_equals%(p_one: opaque of paraglob, p_two: opaque of paraglob%) : bool
	%{
	return zeek::val_mgr->Bool(
		*(static_cast<ParaglobVal*>(p_one)) ==  *(static_cast<ParaglobVal*>(p_two))
	);
	%}

## Returns 32-bit digest of arbitrary input values using FNV-1a hash algorithm.
## See `<https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function>`_.
##
## input: The desired input value to hash.
##
## Returns: The hashed value.
##
## .. zeek:see:: hrw_weight
function fnv1a32%(input: any%): count
	%{
	zeek::ODesc desc(DESC_BINARY);
	input->Describe(&desc);
	auto bytes = desc.Bytes();

	uint32_t offset32 = 2166136261;
	uint32_t prime32 = 16777619;
	uint32_t rval = offset32;

	for ( auto i = 0; i < desc.Len(); ++i )
		{
		rval ^= (uint32_t) bytes[i];
		rval *= prime32;
		}

	return zeek::val_mgr->Count(rval);
	%}

## Calculates a weight value for use in a Rendezvous Hashing algorithm.
## See `<https://en.wikipedia.org/wiki/Rendezvous_hashing>`_.
## The weight function used is the one recommended in the original
## paper: `<http://www.eecs.umich.edu/techreports/cse/96/CSE-TR-316-96.pdf>`_.
##
## key_digest: A 32-bit digest of a key.  E.g. use :zeek:see:`fnv1a32` to
##             produce this.
##
## site_id: A 32-bit site/node identifier.
##
## Returns: The weight value for the key/site pair.
##
## .. zeek:see:: fnv1a32
function hrw_weight%(key_digest: count, site_id: count%): count
	%{
	uint32_t d = key_digest;
	d &= 0x7fffffff; // 31-bit digest
	uint32_t si = site_id;
	auto a = 1103515245;
	auto b = 12345;
	auto m = 2147483648; // 2**31

	uint32_t rval = (a * ((a * si + b) ^ d) + b) % m;

	return zeek::val_mgr->Count(rval);
	%}

## Generates a random number.
##
## max: The maximum value of the random number.
##
## Returns: a random positive integer in the interval *[0, max)*.
##
## .. zeek:see:: srand
##
## .. note::
##
##      This function is a wrapper about the function ``random``
##      provided by the OS.
function rand%(max: count%): count
	%{
	auto result = bro_uint_t(double(max) * double(zeek::util::detail::random_number()) / (zeek::util::detail::max_random() + 1.0));
	return zeek::val_mgr->Count(result);
	%}

## Sets the seed for subsequent :zeek:id:`rand` calls.
##
## seed: The seed for the PRNG.
##
## .. zeek:see:: rand
##
## .. note::
##
##      This function is a wrapper about the function ``srandom``
##      provided by the OS.
function srand%(seed: count%): any
	%{
	zeek::util::detail::seed_random(seed);
	return nullptr;
	%}

%%{
#include <syslog.h>
%%}

## Send a string to syslog.
##
## s: The string to log via syslog
function syslog%(s: string%): any
	%{
	zeek::reporter->Syslog("%s", s->CheckString());
	return nullptr;
	%}

## Determines the MIME type of a piece of data using Zeek's file magic
## signatures.
##
## data: The data to find the MIME type for.
##
## return_mime: Deprecated argument; does nothing, except emit a warning
##              when false.
##
## Returns: The MIME type of *data*, or "<unknown>" if there was an error
##          or no match.  This is the strongest signature match.
##
## .. zeek:see:: file_magic
function identify_data%(data: string, return_mime: bool &default=T%): string
	%{
	if ( ! return_mime )
		zeek::reporter->Warning("identify_data() builtin-function only returns MIME types, but verbose file info requested");

	string strongest_match = zeek::file_mgr->DetectMIME(data->Bytes(), data->Len());

	if ( strongest_match.empty() )
		return zeek::make_intrusive<zeek::StringVal>("<unknown>");

	return zeek::make_intrusive<zeek::StringVal>(strongest_match);
	%}

## Determines the MIME type of a piece of data using Zeek's file magic
## signatures.
##
## data: The data for which to find matching MIME types.
##
## Returns: All matching signatures, in order of strength.
##
## .. zeek:see:: identify_data
function file_magic%(data: string%): mime_matches
	%{
	zeek::detail::RuleMatcher::MIME_Matches matches;
	zeek::file_mgr->DetectMIME(data->Bytes(), data->Len(), &matches);
	return file_analysis::GenMIMEMatchesVal(matches);
	%}

## Performs an entropy test on the given data.
## See http://www.fourmilab.ch/random.
##
## data: The data to compute the entropy for.
##
## Returns: The result of the entropy test, which contains the following
##          fields.
##
##              - ``entropy``: The information density expressed as a number of
##                bits per character.
##
##              - ``chi_square``: The chi-square test value expressed as an
##                absolute number and a percentage which indicates how
##                frequently a truly random sequence would exceed the value
##                calculated, i.e., the degree to which the sequence tested is
##                suspected of being non-random.
##
##                If the percentage is greater than 99% or less than 1%, the
##                sequence is almost certainly not random. If the percentage is
##                between 99% and 95% or between 1% and 5%, the sequence is
##                suspect. Percentages between 90\% and 95\% and 5\% and 10\%
##                indicate the sequence is "almost suspect."
##
##              - ``mean``: The arithmetic mean of all the bytes. If the data
##                are close to random, it should be around 127.5.
##
##              - ``monte_carlo_pi``: Each successive sequence of six bytes is
##                used as 24-bit *x* and *y* coordinates within a square. If
##                the distance of the randomly-generated point is less than the
##                radius of a circle inscribed within the square, the six-byte
##                sequence is considered a "hit." The percentage of hits can
##                be used to calculate the value of pi. For very large streams
##                the value will approach the correct value of pi if the
##                sequence is close to random.
##
##              - ``serial_correlation``: This quantity measures the extent to
##                which each byte in the file depends upon the previous byte.
##                For random sequences this value will be close to zero.
##
## .. zeek:see:: entropy_test_init entropy_test_add entropy_test_finish
function find_entropy%(data: string%): entropy_test_result
	%{
	double montepi, scc, ent, mean, chisq;
	montepi = scc = ent = mean = chisq = 0.0;
	EntropyVal e;
	e.Feed(data->Bytes(), data->Len());
	e.Get(&ent, &chisq, &mean, &montepi, &scc);

	static auto entropy_test_result = zeek::id::find_type<zeek::RecordType>("entropy_test_result");
	auto ent_result = zeek::make_intrusive<zeek::RecordVal>(entropy_test_result);
	ent_result->Assign(0, ent);
	ent_result->Assign(1, chisq);
	ent_result->Assign(2, mean);
	ent_result->Assign(3, montepi);
	ent_result->Assign(4, scc);
	return ent_result;
	%}

## Initializes data structures for incremental entropy calculation.
##
## Returns: An opaque handle to be used in subsequent operations.
##
## .. zeek:see:: find_entropy entropy_test_add entropy_test_finish
function entropy_test_init%(%): opaque of entropy
	%{
	return zeek::make_intrusive<zeek::EntropyVal>();
	%}

## Adds data to an incremental entropy calculation.
##
## handle: The opaque handle representing the entropy calculation state.
##
## data: The data to add to the entropy calculation.
##
## Returns: True on success.
##
## .. zeek:see:: find_entropy entropy_test_add entropy_test_finish
function entropy_test_add%(handle: opaque of entropy, data: string%): bool
	%{
	bool status = static_cast<EntropyVal*>(handle)->Feed(data->Bytes(),
	                                                     data->Len());
	return zeek::val_mgr->Bool(status);
	%}

## Finishes an incremental entropy calculation. Before using this function,
## one needs to obtain an opaque handle with :zeek:id:`entropy_test_init` and
## add data to it via :zeek:id:`entropy_test_add`.
##
## handle: The opaque handle representing the entropy calculation state.
##
## Returns: The result of the entropy test. See :zeek:id:`find_entropy` for a
##          description of the individual components.
##
## .. zeek:see:: find_entropy entropy_test_init entropy_test_add
function entropy_test_finish%(handle: opaque of entropy%): entropy_test_result
	%{
	double montepi, scc, ent, mean, chisq;
	montepi = scc = ent = mean = chisq = 0.0;
	static_cast<EntropyVal*>(handle)->Get(&ent, &chisq, &mean, &montepi, &scc);

	static auto entropy_test_result = zeek::id::find_type<zeek::RecordType>("entropy_test_result");
	auto ent_result = zeek::make_intrusive<zeek::RecordVal>(entropy_test_result);
	ent_result->Assign(0, ent);
	ent_result->Assign(1, chisq);
	ent_result->Assign(2, mean);
	ent_result->Assign(3, montepi);
	ent_result->Assign(4, scc);
	return ent_result;
	%}

## Creates an identifier that is unique with high probability.
##
## prefix: A custom string prepended to the result.
##
## Returns: A string identifier that is unique.
##
## .. zeek:see:: unique_id_from
function unique_id%(prefix: string%) : string
	%{
	char tmp[20];
	uint64_t uid = zeek::util::calculate_unique_id(UID_POOL_DEFAULT_SCRIPT);
	return zeek::make_intrusive<zeek::StringVal>(
		zeek::util::uitoa_n(uid, tmp, sizeof(tmp), 62, prefix->CheckString()));
	%}

## Creates an identifier that is unique with high probability.
##
## pool: A seed for determinism.
##
## prefix: A custom string prepended to the result.
##
## Returns: A string identifier that is unique.
##
## .. zeek:see:: unique_id
function unique_id_from%(pool: int, prefix: string%) : string
	%{
	pool += UID_POOL_CUSTOM_SCRIPT; // Make sure we don't conflict with internal pool.

	char tmp[20];
	uint64_t uid = zeek::util::calculate_unique_id(pool);
	return zeek::make_intrusive<zeek::StringVal>(
	       zeek::util::uitoa_n(uid, tmp, sizeof(tmp), 62, prefix->CheckString()));
	%}

# ===========================================================================
#
#                             Generic Programming
#
# ===========================================================================

## Removes all elements from a set or table.
##
## v: The set or table
function clear_table%(v: any%): any
	%{
	if ( v->GetType()->Tag() == zeek::TYPE_TABLE )
		v->AsTableVal()->RemoveAll();
	else
		zeek::emit_builtin_error("clear_table() requires a table/set argument");

	return nullptr;
	%}

## Gets all subnets that contain a given subnet from a set/table[subnet].
##
## search: the subnet to search for.
##
## t: the set[subnet] or table[subnet].
##
## Returns: All the keys of the set or table that cover the subnet searched for.
function matching_subnets%(search: subnet, t: any%): subnet_vec
	%{
	if ( t->GetType()->Tag() != zeek::TYPE_TABLE || ! t->GetType()->AsTableType()->IsSubNetIndex() )
		{
		zeek::reporter->Error("matching_subnets needs to be called on a set[subnet]/table[subnet].");
		return nullptr;
		}

	return t->AsTableVal()->LookupSubnets(search);
	%}

## For a set[subnet]/table[subnet], create a new table that contains all entries
## that contain a given subnet.
##
## search: the subnet to search for.
##
## t: the set[subnet] or table[subnet].
##
## Returns: A new table that contains all the entries that cover the subnet searched for.
function filter_subnet_table%(search: subnet, t: any%): any
	%{
	if ( t->GetType()->Tag() != zeek::TYPE_TABLE || ! t->GetType()->AsTableType()->IsSubNetIndex() )
		{
		zeek::reporter->Error("filter_subnet_table needs to be called on a set[subnet]/table[subnet].");
		return nullptr;
		}

	return t->AsTableVal()->LookupSubnetValues(search);
	%}

## Checks if a specific subnet is a member of a set/table[subnet].
## In contrast to the ``in`` operator, this performs an exact match, not
## a longest prefix match.
##
## search: the subnet to search for.
##
## t: the set[subnet] or table[subnet].
##
## Returns: True if the exact subnet is a member, false otherwise.
function check_subnet%(search: subnet, t: any%): bool
	%{
	if ( t->GetType()->Tag() != zeek::TYPE_TABLE || ! t->GetType()->AsTableType()->IsSubNetIndex() )
		{
		zeek::reporter->Error("check_subnet needs to be called on a set[subnet]/table[subnet].");
		return nullptr;
		}

	const zeek::detail::PrefixTable* pt = t->AsTableVal()->Subnets();
	if ( ! pt )
		{
		zeek::reporter->Error("check_subnet encountered nonexisting prefix table.");
		return nullptr;
		}

	void* res = pt->Lookup(search, true);

	return zeek::val_mgr->Bool(res != nullptr);
	%}

## Checks whether two objects reference the same internal object. This function
## uses equality comparison of C++ raw pointer values to determine if the two
## objects are the same.
##
## o1: The first object.
##
## o2: The second object.
##
## Returns: True if *o1* and *o2* are equal.
function same_object%(o1: any, o2: any%): bool
	%{
	return zeek::val_mgr->Bool(o1 == o2);
	%}

## Returns the number of bytes that a value occupies in memory.
##
## v: The value
##
## Returns: The number of bytes that *v* occupies.
function val_size%(v: any%): count &deprecated="Remove in v5.1. MemoryAllocation() is deprecated and will be removed."
	%{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
	return zeek::val_mgr->Count(v->MemoryAllocation());
#pragma GCC diagnostic pop
	%}

## Resizes a vector.
##
## aggr: The vector instance.
##
## newsize: The new size of *aggr*.
##
## Returns: The old size of *aggr*, or 0 if *aggr* is not a :zeek:type:`vector`.
function resize%(aggr: any, newsize: count%) : count
	%{
	if ( aggr->GetType()->Tag() != zeek::TYPE_VECTOR )
		{
		zeek::emit_builtin_error("resize() operates on vectors");
		return nullptr;
		}

	return zeek::val_mgr->Count(aggr->AsVectorVal()->Resize(newsize));
	%}

## Tests whether a boolean vector (``vector of bool``) has *any* true
## element.
##
## v: The boolean vector instance.
##
## Returns: True if any element in *v* is true.
##
## .. zeek:see:: all_set
function any_set%(v: any%) : bool
	%{
	if ( v->GetType()->Tag() != zeek::TYPE_VECTOR ||
	     v->GetType()->Yield()->Tag() != zeek::TYPE_BOOL )
		{
		zeek::emit_builtin_error("any_set() requires vector of bool");
		return zeek::val_mgr->False();
		}

	VectorVal* vv = v->AsVectorVal();
	for ( unsigned int i = 0; i < vv->Size(); ++i )
		if ( vv->BoolAt(i) )
			return zeek::val_mgr->True();

	return zeek::val_mgr->False();
	%}

## Tests whether *all* elements of a boolean vector (``vector of bool``) are
## true.
##
## v: The boolean vector instance.
##
## Returns: True iff all elements in *v* are true or there are no elements.
##
## .. zeek:see:: any_set
##
## .. note::
##
##      Missing elements count as false.
function all_set%(v: any%) : bool
	%{
	if ( v->GetType()->Tag() != zeek::TYPE_VECTOR ||
	     v->GetType()->Yield()->Tag() != zeek::TYPE_BOOL )
		{
		zeek::emit_builtin_error("all_set() requires vector of bool");
		return zeek::val_mgr->False();
		}

	VectorVal* vv = v->AsVectorVal();
	for ( unsigned int i = 0; i < vv->Size(); ++i )
		if ( ! vv->BoolAt(i) )
			return zeek::val_mgr->False();

	return zeek::val_mgr->True();
	%}

## Sorts a vector in place. The second argument is a comparison function that
## takes two arguments: if the vector type is ``vector of T``, then the
## comparison function must be ``function(a: T, b: T): int``, which returns
## a value less than zero if ``a < b`` for some type-specific notion of the
## less-than operator.  The comparison function is optional if the type
## is a numeric type (int, count, double, time, etc.).
##
## v: The vector instance to sort.
##
## Returns: The vector, sorted from minimum to maximum value. If the vector
##          could not be sorted, then the original vector is returned instead.
##
## .. zeek:see:: order
function sort%(v: any, ...%) : any
	%{
	zeek::ValPtr rval{zeek::NewRef{}, v};

	if ( v->GetType()->Tag() != zeek::TYPE_VECTOR )
		{
		zeek::emit_builtin_error("sort() requires vector");
		return rval;
		}

	const auto& elt_type = v->GetType()->Yield();
	zeek::Func* comp = nullptr;

	if ( @ARG@.size() > 2 )
		{
		zeek::emit_builtin_error("sort() called with extraneous argument");
		return rval;
		}

	if ( @ARG@.size() == 2 )
		{
		Val* comp_val = @ARG@[1].get();
		if ( ! IsFunc(comp_val->GetType()->Tag()) )
			{
			zeek::emit_builtin_error("second argument to sort() needs to be comparison function");
			return rval;
			}

		comp = comp_val->AsFunc();
		const auto& comp_type = comp->GetType();

		if ( comp_type->Yield()->Tag() != zeek::TYPE_INT ||
		     ! comp_type->ParamList()->AllMatch(elt_type, 0) ||
		     comp_type->ParamList()->GetTypes().size() != 2 )
			{
			zeek::emit_builtin_error("invalid comparison function in call to sort()");
			return rval;
			}
		}

	if ( ! comp && ! IsIntegral(elt_type->Tag()) &&
	     elt_type->InternalType() != TYPE_INTERNAL_DOUBLE )
		{
		zeek::emit_builtin_error("comparison function required for sort() with non-numeric types");
		return rval;
		}

	auto vv = v->As<zeek::VectorVal*>();
	vv->Sort(comp);

	return rval;
	%}

## Returns the order of the elements in a vector according to some
## comparison function. See :zeek:id:`sort` for details about the comparison
## function.
##
## v: The vector whose order to compute.
##
## Returns: A ``vector of count`` with the indices of the ordered elements.
##          For example, the elements of *v* in order are (assuming ``o``
##          is the vector returned by ``order``):  v[o[0]], v[o[1]], etc.
##
## .. zeek:see:: sort
function order%(v: any, ...%) : index_vec
	%{
	auto err_v = zeek::make_intrusive<zeek::VectorVal>(zeek::id::index_vec);

	if ( v->GetType()->Tag() != zeek::TYPE_VECTOR )
		{
		zeek::emit_builtin_error("order() requires vector");
		return err_v;
		}

	const auto& elt_type = v->GetType()->Yield();
	zeek::Func* comp = nullptr;

	if ( @ARG@.size() > 2 )
		{
		zeek::emit_builtin_error("order() called with extraneous argument");
		return err_v;
		}

	if ( @ARG@.size() == 2 )
		{
		Val* comp_val = @ARG@[1].get();
		if ( ! IsFunc(comp_val->GetType()->Tag()) )
			{
			zeek::emit_builtin_error("second argument to order() needs to be comparison function");
			return zeek::ValPtr{zeek::NewRef{}, v};
			}

		comp = comp_val->AsFunc();

		const auto& comp_type = comp->GetType();
		if ( comp_type->Yield()->Tag() != zeek::TYPE_INT ||
		     ! comp_type->ParamList()->AllMatch(elt_type, 0) ||
		     comp_type->ParamList()->GetTypes().size() != 2 )
			{
			zeek::emit_builtin_error("invalid comparison function in call to order()");
			return zeek::ValPtr{zeek::NewRef{}, v};
			}
		}

	if ( ! comp && ! IsIntegral(elt_type->Tag()) &&
	     elt_type->InternalType() != TYPE_INTERNAL_DOUBLE )
		{
		zeek::emit_builtin_error("comparison function required for order() with non-numeric types");
		return err_v;
		}

	auto vv = v->As<zeek::VectorVal*>();

	return vv->Order(comp);
	%}

# ===========================================================================
#
#                              String Processing
#
# ===========================================================================

## Returns the concatenation of the string representation of its arguments. The
## arguments can be of any type. For example, ``cat("foo", 3, T)`` returns
## ``"foo3T"``.
##
## Returns: A string concatentation of all arguments.
function cat%(...%): string
	%{
	zeek::ODesc d;
	d.SetStyle(RAW_STYLE);

	for ( const auto& a : @ARG@ )
		a->Describe(&d);

	String* s = new String(1, d.TakeBytes(), d.Len());
	s->SetUseFreeToDelete(true);

	return zeek::make_intrusive<zeek::StringVal>(s);
	%}

## Concatenates all arguments, with a separator placed between each one. This
## function is similar to :zeek:id:`cat`, but places a separator between each
## given argument. If any of the variable arguments is an empty string it is
## replaced by a given default string instead.
##
## sep: The separator to place between each argument.
##
## def: The default string to use when an argument is the empty string.
##
## Returns: A concatenation of all arguments with *sep* between each one and
##          empty strings replaced with *def*.
##
## .. zeek:see:: cat string_cat
function cat_sep%(sep: string, def: string, ...%): string
	%{
	zeek::ODesc d;
	d.SetStyle(RAW_STYLE);

	int pre_size = 0;

	for ( auto i = 0u; i < @ARG@.size(); ++i )
		{
		// Skip named parameters.
		if ( i < 2 )
			continue;

		if ( i > 2 )
			d.Add(sep->CheckString(), 0);

		Val* v = @ARG@[i].get();
		if ( v->GetType()->Tag() == zeek::TYPE_STRING && ! v->AsString()->Len() )
			v = def;

		v->Describe(&d);
		}

	String* s = new String(1, d.TakeBytes(), d.Len());
	s->SetUseFreeToDelete(true);

	return zeek::make_intrusive<zeek::StringVal>(s);
	%}

## Produces a formatted string à la ``printf``. The first argument is the
## *format string* and specifies how subsequent arguments are converted for
## output. It is composed of zero or more directives: ordinary characters (not
## ``%``), which are copied unchanged to the output, and conversion
## specifications, each of which fetches zero or more subsequent arguments.
## Conversion specifications begin with ``%`` and the arguments must properly
## correspond to the specifier. After the ``%``, the following characters
## may appear in sequence:
##
##    - ``%``: Literal ``%``
##
##    - ``-``: Left-align field
##
##    - ``[0-9]+``: The field width (< 128)
##
##    - ``.``: Precision of floating point specifiers ``[efg]`` (< 128)
##
##    - ``[DTdxsefg]``: Format specifier
##
##        - ``[DT]``: ISO timestamp with microsecond precision
##
##        - ``d``: Signed/Unsigned integer (using C-style ``%lld``/``%llu``
##                 for ``int``/``count``)
##
##        - ``x``: Unsigned hexadecimal (using C-style ``%llx``);
##                 addresses/ports are converted to host-byte order
##
##        - ``s``: String (byte values less than 32 or greater than 126
##                 will be escaped)
##
##        - ``[efg]``: Double
##
## Returns: Returns the formatted string. Given no arguments, :zeek:id:`fmt`
##          returns an empty string. Given no format string or the wrong
##          number of additional arguments for the given format specifier,
##          :zeek:id:`fmt` generates a run-time error.
##
## .. zeek:see:: cat cat_sep string_cat
function fmt%(...%): string
	%{
	if ( @ARGC@ == 0 )
		return zeek::val_mgr->EmptyString();

	Val* fmt_v = @ARG@[0].get();

	// Type of fmt_v will be string here, check_built_in_call() in Func.cc
	// checks that.

	const char* fmt = fmt_v->AsString()->CheckString();
	zeek::ODesc d;
	d.SetStyle(RAW_STYLE);

	int n = 0;

	while ( next_fmt(fmt, @ARGS@, &d, n) )
		;

	if ( n < static_cast<int>(@ARGC@) - 1 )
		{
		zeek::emit_builtin_error("too many arguments for format", fmt_v);
		return zeek::val_mgr->EmptyString();
		}

	else if ( n >= static_cast<int>(@ARGC@) )
		{
		zeek::emit_builtin_error("too few arguments for format", fmt_v);
		return zeek::val_mgr->EmptyString();
		}

	String* s = new String(1, d.TakeBytes(), d.Len());
	s->SetUseFreeToDelete(true);

	return zeek::make_intrusive<zeek::StringVal>(s);
	%}

## Renders a sequence of values to a string of bytes and outputs them directly
## to ``stdout`` with no additional escape sequences added.  No additional
## newline is added to the end either.
##
## Returns: Always true.
##
## .. zeek:see:: fmt cat cat_sep string_cat to_json
function print_raw%(...%): bool
	%{
	zeek::ODesc d(DESC_READABLE);
	d.SetStyle(RAW_STYLE);
	describe_vals(@ARG@, &d, 0);
	printf("%.*s", d.Len(), d.Description());
	return zeek::val_mgr->Bool(true);
	%}

# ===========================================================================
#
#                                    Math
#
# ===========================================================================

## Computes the greatest integer less than the given :zeek:type:`double` value.
## For example, ``floor(3.14)`` returns ``3.0``, and ``floor(-3.14)``
## returns ``-4.0``.
##
## d: The :zeek:type:`double` to manipulate.
##
## Returns: The next lowest integer of *d* as :zeek:type:`double`.
##
## .. zeek:see:: sqrt exp ln log10
function floor%(d: double%): double
	%{
	return zeek::make_intrusive<zeek::DoubleVal>(floor(d));
	%}

## Computes the square root of a :zeek:type:`double`.
##
## x: The number to compute the square root of.
##
## Returns: The square root of *x*.
##
## .. zeek:see:: floor exp ln log10
function sqrt%(x: double%): double
	%{
	if ( x < 0 )
		{
		zeek::reporter->Error("negative sqrt argument");
		return zeek::make_intrusive<zeek::DoubleVal>(-1.0);
		}

	return zeek::make_intrusive<zeek::DoubleVal>(sqrt(x));
	%}

## Computes the exponential function.
##
## d: The argument to the exponential function.
##
## Returns: *e* to the power of *d*.
##
## .. zeek:see:: floor sqrt ln log10
function exp%(d: double%): double
	%{
	return zeek::make_intrusive<zeek::DoubleVal>(exp(d));
	%}

## Computes the natural logarithm of a number.
##
## d: The argument to the logarithm.
##
## Returns: The natural logarithm of *d*.
##
## .. zeek:see:: exp floor sqrt log10
function ln%(d: double%): double
	%{
	return zeek::make_intrusive<zeek::DoubleVal>(log(d));
	%}

## Computes the common logarithm of a number.
##
## d: The argument to the logarithm.
##
## Returns: The common logarithm of *d*.
##
## .. zeek:see:: exp floor sqrt ln
function log10%(d: double%): double
	%{
	return zeek::make_intrusive<zeek::DoubleVal>(log10(d));
	%}

# ===========================================================================
#
#                                Introspection
#
# ===========================================================================

## Returns the ID of the analyzer which raised the current event.
##
## Returns: The ID of the analyzer which raised the current event, or 0 if
##          none.
function current_analyzer%(%) : count
	%{
	return zeek::val_mgr->Count(zeek::event_mgr.CurrentAnalyzer());
	%}

## Returns Zeek's process ID.
##
## Returns: Zeek's process ID.
function getpid%(%) : count
	%{
	return zeek::val_mgr->Count(getpid());
	%}

%%{
namespace zeek {
extern const char* zeek_version();
} // namespace zeek
%%}

## Returns the Zeek version string.
##
## Returns: Zeek's version, e.g., 2.0-beta-47-debug.
function zeek_version%(%): string
	%{
	return zeek::make_intrusive<zeek::StringVal>(zeek::zeek_version());
	%}

%%{
namespace zeek::run_state::detail {
extern bool bare_mode;
} // namespace zeek::run_state::detail
%%}

## Returns whether Zeek was started in bare mode.
##
## Returns: True if Zeek was started in bare mode, false otherwise.
function bare_mode%(%): bool
	%{
	return zeek::val_mgr->Bool(zeek::run_state::detail::bare_mode);
	%}

## Converts a record type name to a vector of strings, where each element is
## the name of a record field. Nested records are flattened.
##
## rt: The name of the record type.
##
## Returns: A string vector with the field names of *rt*.
function record_type_to_vector%(rt: string%): string_vec
	%{
	auto result = zeek::make_intrusive<zeek::VectorVal>(zeek::id::string_vec);

	zeek::RecordType* type = zeek::id::find_type(rt->CheckString())->AsRecordType();

	for ( int i = 0; i < type->NumFields(); ++i )
		result->Assign(i+1, zeek::make_intrusive<zeek::StringVal>(type->FieldName(i)));

	return result;
	%}

## Returns the type name of an arbitrary Zeek variable.
##
## t: An arbitrary object.
##
## Returns: The type name of *t*.
function type_name%(t: any%): string
	%{
	zeek::ODesc d;
	t->GetType()->Describe(&d);

	String* s = new String(1, d.TakeBytes(), d.Len());
	s->SetUseFreeToDelete(true);

	return zeek::make_intrusive<zeek::StringVal>(s);
	%}

%%{
static std::map<std::string, std::set<std::string>> build_complete_alias_map()
	{
	std::map<std::string, std::set<std::string>> rval;

	for ( const auto& [name, alias_types] : zeek::Type::GetAliasMap() )
		{
		rval[name].emplace(name);

		for ( const auto& alias_type : alias_types )
			{
			const auto& alias_name = alias_type->GetName();
			rval[name].emplace(alias_name);
			rval[alias_name].emplace(name);
			}
		}

	for ( const auto& [name, aliases] : rval )
		{
		std::map<std::string, bool> more;

		for ( const auto& alias_name : aliases )
			if ( alias_name != name )
				more.emplace(alias_name, false);

		for ( ; ; )
			{
			auto prev_size = more.size();

			for ( auto& [alias_name, checked] : more )
				{
				if ( checked ) continue;

				for ( const auto& alias_alias : rval[alias_name] )
					more.emplace(alias_alias, false);

				checked = true;
				}

			if ( prev_size == more.size() )
				break;
			}

		for ( const auto& [alias_name, checked] : more )
			rval[name].emplace(alias_name);
		}

	return rval;
	}
%%}

## Returns all type name aliases of a value or type.
##
## x: An arbitrary value or type.
##
## Returns: The set of all type name aliases of *x* (or the type of *x*
##          if it's a value instead of a type).  For primitive values
##          and types like :zeek:type:`string` or :zeek:type:`count`,
##          this returns an empty set.  For types with user-defined
##          names like :zeek:type:`record` or :zeek:type:`enum`, the
##          returned set contains the original user-defined name for the
##          type along with all aliases.  For other compound types, like
##          :zeek:type:`table`, the returned set is empty unless
##          explicitly requesting aliases for a user-defined type alias
##          or a value that was explicitly created using a type alias
##          (as opposed to originating from an "anonymous" constructor
##          or initializer for that compound type).
function type_aliases%(x: any%): string_set
	%{
	auto rval = make_intrusive<zeek::TableVal>(zeek::id::string_set);

	auto t = x->GetType();

	if ( t->Tag() == TYPE_TYPE )
		t = t->AsTypeType()->GetType();

	const auto& tname = t->GetName();

	if ( tname.empty() )
		return rval;

	static auto all_aliases = build_complete_alias_map();
	auto it = all_aliases.find(tname);

	if ( it == all_aliases.end() )
		return rval;

	for ( const auto& name : it->second )
		rval->Assign(make_intrusive<zeek::StringVal>(name), nullptr);

	return rval;
	%}

## Returns all value names associated with an enum type.
##
## et: An enum type.
##
## Returns: All enum value names associated with enum type *et*.
##          If *et* is not an enum type, an empty set is returned.
function enum_names%(et: any%): string_set
	%{
	auto rval = make_intrusive<zeek::TableVal>(zeek::id::string_set);

	if ( et->GetType()->Tag() != TYPE_TYPE )
		return rval;

	const auto& t = et->GetType()->AsTypeType()->GetType();

	if ( t->Tag() != TYPE_ENUM )
		return rval;

	auto enum_type = t->AsEnumType();

	for ( const auto& [name, i] : enum_type->Names() )
		rval->Assign(make_intrusive<zeek::StringVal>(name), nullptr);

	return rval;
	%}

## Returns: list of command-line arguments (``argv``) used to run Zeek.
function zeek_args%(%): string_vec
	%{
	auto sv = zeek::id::string_vec;
	auto rval = zeek::make_intrusive<zeek::VectorVal>(std::move(sv));

	for ( auto i = 0; i < zeek::detail::zeek_argc; ++i )
		rval->Assign(rval->Size(), zeek::make_intrusive<zeek::StringVal>(zeek::detail::zeek_argv[i]));

	return rval;
	%}

## Checks whether Zeek reads traffic from one or more network interfaces (as
## opposed to from a network trace in a file). Note that this function returns
## true even after Zeek has stopped reading network traffic, for example due to
## receiving a termination signal.
##
## Returns: True if reading traffic from a network interface.
##
## .. zeek:see:: reading_traces packet_source
function reading_live_traffic%(%): bool
	%{
	return zeek::val_mgr->Bool(zeek::run_state::reading_live);
	%}

## Checks whether Zeek reads traffic from a trace file (as opposed to from a
## network interface).
##
## Returns: True if reading traffic from a network trace.
##
## .. zeek:see:: reading_live_traffic packet_source
function reading_traces%(%): bool
	%{
	return zeek::val_mgr->Bool(zeek::run_state::reading_traces);
	%}

## Returns: the packet source being read by Zeek.
##
## .. zeek:see:: reading_live_traffic reading_traces
function packet_source%(%): PacketSource
	%{
	static auto ps_type = zeek::id::find_type<zeek::RecordType>("PacketSource");
	auto ps = zeek::iosource_mgr->GetPktSrc();
	auto r = zeek::make_intrusive<zeek::RecordVal>(ps_type);

	if ( ps )
		{
		r->Assign(0, ps->IsLive());
		r->Assign(1, ps->Path());
		r->Assign(2, ps->LinkType());
		r->Assign(3, ps->Netmask());
		}

	return r;
	%}

## Generates a table of the size of all global variables. The table index is
## the variable name and the value is the variable size in bytes.
##
## Returns: A table that maps variable names to their sizes.
##
## .. zeek:see:: global_ids
function global_sizes%(%): var_sizes &deprecated="Remove in v5.1. MemoryAllocation() is deprecated and will be removed."
	%{
	auto sizes = zeek::make_intrusive<zeek::TableVal>(IntrusivePtr{zeek::NewRef{}, var_sizes});
	const auto& globals = zeek::detail::global_scope()->Vars();

	for ( const auto& global : globals )
		{
		auto& id = global.second;
		if ( id->HasVal() )
			{
			auto id_name = zeek::make_intrusive<zeek::StringVal>(id->Name());
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
			auto id_size = zeek::val_mgr->Count(id->GetVal()->MemoryAllocation());
#pragma GCC diagnostic pop
			sizes->Assign(std::move(id_name), std::move(id_size));
			}
		}

	return sizes;
	%}

## Generates a table of the "footprint" of all global container variables.
## This is (approximately) the number of objects the global contains either
## directly or indirectly.  The number is not meant to be precise, but
## rather comparable: larger footprint correlates with more memory consumption.
## The table index is the variable name and the value is the footprint.
##
## Returns: A table that maps variable names to their footprints.
##
## .. zeek:see:: val_footprint
function global_container_footprints%(%): var_sizes
	%{
	auto sizes = zeek::make_intrusive<zeek::TableVal>(IntrusivePtr{zeek::NewRef{}, var_sizes});
	const auto& globals = zeek::detail::global_scope()->Vars();

	for ( const auto& global : globals )
		{
		auto& id = global.second;
		auto v = id->GetVal();

		if ( ! v || ! IsAggr(v->GetType()) )
			continue;

		auto id_name = zeek::make_intrusive<zeek::StringVal>(id->Name());
		auto fp = zeek::val_mgr->Count(v->Footprint());
		sizes->Assign(std::move(id_name), std::move(fp));
		}

	return sizes;
	%}

## Computes a value's "footprint": the number of objects the value contains
## either directly or indirectly.  The number is not meant to be precise, but
## rather comparable: larger footprint correlates with more memory consumption.
##
## Returns: the footprint.
##
## .. zeek:see:: global_container_footprints
function val_footprint%(v: any%): count
	%{
	return zeek::val_mgr->Count(v->Footprint());
	%}

## Generates a table with information about all global identifiers. The table
## value is a record containing the type name of the identifier, whether it is
## exported, a constant, an enum constant, redefinable, and its value (if it
## has one).
##
## Returns: A table that maps identifier names to information about them.
##
## .. zeek:see:: global_sizes
function global_ids%(%): id_table
	%{
	static auto id_table = zeek::id::find_type<zeek::TableType>("id_table");
	auto ids = zeek::make_intrusive<zeek::TableVal>(id_table);
	const auto& globals = zeek::detail::global_scope()->Vars();

	for ( const auto& global : globals )
		{
		const auto& id = global.second;
		static auto script_id = zeek::id::find_type<zeek::RecordType>("script_id");
		auto rec = zeek::make_intrusive<zeek::RecordVal>(script_id);
		rec->Assign(0, type_name(id->GetType()->Tag()));
		rec->Assign(1, id->IsExport());
		rec->Assign(2, id->IsConst());
		rec->Assign(3, id->IsEnumConst());
		rec->Assign(4, id->IsOption());
		rec->Assign(5, id->IsRedefinable());
		rec->Assign(6, id->GetAttr(zeek::detail::ATTR_BACKEND) != zeek::detail::Attr::nil);

		if ( id->HasVal() )
			rec->Assign(7, id->GetVal());

		auto id_name = zeek::make_intrusive<zeek::StringVal>(id->Name());
		ids->Assign(std::move(id_name), std::move(rec));
		}

	return ids;
	%}

## Returns a set giving the names of all global options.
function global_options%(%): string_set
	%{
	auto options = make_intrusive<zeek::TableVal>(zeek::id::string_set);
	const auto& globals = zeek::detail::global_scope()->Vars();

	for ( const auto& global : globals )
		{
		const auto& id = global.second;
		if ( ! id->IsOption() )
			continue;

		auto id_name = zeek::make_intrusive<zeek::StringVal>(id->Name());
		options->Assign(std::move(id_name), nullptr);
		}

	return options;
	%}

## Returns the value of a global identifier.
##
## id: The global identifier.
##
## Returns: The value of *id*. If *id* does not describe a valid identifier,
##          the string ``"<unknown id>"`` or ``"<no ID value>"`` is returned.
function lookup_ID%(id: string%) : any
	%{
	const auto& i = zeek::detail::global_scope()->Find(id->CheckString());
	if ( ! i )
		return zeek::make_intrusive<zeek::StringVal>("<unknown id>");

	if ( i->IsEnumConst() )
		{
		auto et = i->GetType()->AsEnumType();
		auto idx = et->Lookup(detail::GLOBAL_MODULE_NAME, i->Name());
		return et->GetEnumVal(idx);
		}

	if ( ! i->GetVal() )
		return zeek::make_intrusive<zeek::StringVal>("<no ID value>");

	return i->GetVal();
	%}

## Generates metadata about a record's fields. The returned information
## includes the field name, whether it is logged, its value (if it has one),
## and its default value (if specified).
##
## rec: The record value or type to inspect.
##
## Returns: A table that describes the fields of a record.
function record_fields%(rec: any%): record_field_table
	%{
	static auto record_field_table = zeek::id::find_type<zeek::TableType>("record_field_table");

	if ( rec->GetType()->Tag() == zeek::TYPE_STRING )
		{
		const auto& id = zeek::detail::global_scope()->Find(rec->AsStringVal()->ToStdString());

		if ( ! id || ! id->IsType() || id->GetType()->Tag() != zeek::TYPE_RECORD )
			{
			zeek::reporter->Error("record_fields string argument does not name a record type");
			return zeek::make_intrusive<zeek::TableVal>(record_field_table);
			}

		return id->GetType()->AsRecordType()->GetRecordFieldsVal();
		}

	return rec->GetRecordFields();
	%}

## Enables detailed collection of profiling statistics. Statistics include
## CPU/memory usage, connections, TCP states/reassembler, DNS lookups,
## timers, and script-level state. The script variable :zeek:id:`profiling_file`
## holds the name of the file.
##
## .. zeek:see:: get_conn_stats
##              get_dns_stats
##              get_event_stats
##              get_file_analysis_stats
##              get_gap_stats
##              get_matcher_stats
##              get_net_stats
##              get_proc_stats
##              get_reassembler_stats
##              get_thread_stats
##              get_timer_stats
function do_profiling%(%) : any
	%{
	if ( zeek::detail::profiling_logger )
		zeek::detail::profiling_logger->Log();

	return nullptr;
	%}

## Checks whether a given IP address belongs to a local interface.
##
## ip: The IP address to check.
##
## Returns: True if *ip* belongs to a local interface.
function is_local_interface%(ip: addr%) : bool
	%{
	if ( ip->AsAddr().IsLoopback() )
		return zeek::val_mgr->True();

	list<zeek::IPAddr> addrs;

	char host[MAXHOSTNAMELEN];

	strcpy(host, "localhost");
	gethostname(host, MAXHOSTNAMELEN);
	host[MAXHOSTNAMELEN-1] = '\0';

	struct hostent* ent = gethostbyname2(host, AF_INET);

	if ( ent )
		{
		for ( unsigned int len = 0; ent->h_addr_list[len]; ++len )
			addrs.push_back(zeek::IPAddr(IPv4, (uint32_t*)ent->h_addr_list[len],
			                       zeek::IPAddr::Network));
		}

	ent = gethostbyname2(host, AF_INET6);

	if ( ent )
		{
		for ( unsigned int len = 0; ent->h_addr_list[len]; ++len )
			addrs.push_back(zeek::IPAddr(IPv6, (uint32_t*)ent->h_addr_list[len],
			                       zeek::IPAddr::Network));
		}

	list<zeek::IPAddr>::const_iterator it;
	for ( it = addrs.begin(); it != addrs.end(); ++it )
		{
		if ( *it == ip->AsAddr() )
			return zeek::val_mgr->True();
		}

	return zeek::val_mgr->False();
	%}

## Write rule matcher statistics (DFA states, transitions, memory usage, cache
## hits/misses) to a file.
##
## f: The file to write to.
##
## Returns: True (unconditionally).
##
## .. zeek:see:: get_matcher_stats
function dump_rule_stats%(f: file%): bool
	%{
	if ( zeek::detail::rule_matcher )
		zeek::detail::rule_matcher->DumpStats(f);

	return zeek::val_mgr->True();
	%}

## Checks if Zeek is terminating.
##
## Returns: True if Zeek is in the process of shutting down.
##
## .. zeek:see:: terminate
function zeek_is_terminating%(%): bool
	%{
	return zeek::val_mgr->Bool(zeek::run_state::terminating);
	%}

## Returns the hostname of the machine Zeek runs on.
##
## Returns: The hostname of the machine Zeek runs on.
function gethostname%(%) : string
	%{
	char buffer[MAXHOSTNAMELEN];
	if ( gethostname(buffer, MAXHOSTNAMELEN) < 0 )
		strcpy(buffer, "<unknown>");

	buffer[MAXHOSTNAMELEN-1] = '\0';
	return zeek::make_intrusive<zeek::StringVal>(buffer);
	%}

## Returns whether an address is IPv4 or not.
##
## a: the address to check.
##
## Returns: true if *a* is an IPv4 address, else false.
function is_v4_addr%(a: addr%): bool
	%{
	if ( a->AsAddr().GetFamily() == IPv4 )
		return zeek::val_mgr->True();
	else
		return zeek::val_mgr->False();
	%}

## Returns whether an address is IPv6 or not.
##
## a: the address to check.
##
## Returns: true if *a* is an IPv6 address, else false.
function is_v6_addr%(a: addr%): bool
	%{
	if ( a->AsAddr().GetFamily() == IPv6 )
		return zeek::val_mgr->True();
	else
		return zeek::val_mgr->False();
	%}

## Returns whether a subnet specification is IPv4 or not.
##
## s: the subnet to check.
##
## Returns: true if *s* is an IPv4 subnet, else false.
function is_v4_subnet%(s: subnet%): bool
	%{
	if ( s->AsSubNet().Prefix().GetFamily() == IPv4 )
		return zeek::val_mgr->True();
	else
		return zeek::val_mgr->False();
	%}

## Returns whether a subnet specification is IPv6 or not.
##
## s: the subnet to check.
##
## Returns: true if *s* is an IPv6 subnet, else false.
function is_v6_subnet%(s: subnet%): bool
	%{
	if ( s->AsSubNet().Prefix().GetFamily() == IPv6 )
		return zeek::val_mgr->True();
	else
		return zeek::val_mgr->False();
	%}

## Returns a representation of the call stack as a vector of call stack
## elements, each containing call location information.
##
## Returns: the call stack information, including function, file, and line
##          location information.
function backtrace%(%): Backtrace
	%{
	using zeek::detail::call_stack;
	static auto backtrace_type = id::find_type<VectorType>("Backtrace");
	static auto elem_type = id::find_type<RecordType>("BacktraceElement");
	static auto function_name_idx = elem_type->FieldOffset("function_name");
	static auto function_args_idx = elem_type->FieldOffset("function_args");
	static auto file_location_idx = elem_type->FieldOffset("file_location");
	static auto line_location_idx = elem_type->FieldOffset("line_location");

	auto rval = make_intrusive<VectorVal>(backtrace_type);

	// The body of the following loop can wind up adding items to
	// the call stack (because MakeCallArgumentVector() evaluates
	// default arguments, which can in turn involve calls to script
	// functions), so we work from a copy of the current call stack
	// to prevent problems with iterator invalidation.
	auto cs_copy = call_stack;

	for ( auto it = cs_copy.rbegin(); it != cs_copy.rend(); ++it )
		{
		const auto& ci = *it;
		if ( ! ci.func )
			// This happens for compiled code.
			continue;

		auto elem = make_intrusive<RecordVal>(elem_type);

		const auto& params = ci.func->GetType()->Params();
		auto args = MakeCallArgumentVector(ci.args, params);

		elem->Assign(function_name_idx, ci.func->Name());
		elem->Assign(function_args_idx, std::move(args));

		if ( ci.call )
			{
			auto loc = ci.call->GetLocationInfo();
			elem->Assign(file_location_idx, loc->filename);
			elem->Assign(line_location_idx, loc->first_line);
			}

		rval->Assign(rval->Size(), std::move(elem));
		}

	return rval;
	%}

# ===========================================================================
#
#                                 Conversion
#
# ===========================================================================

## Converts the *data* field of :zeek:type:`ip6_routing` records that have
## *rtype* of 0 into a vector of addresses.
##
## s: The *data* field of an :zeek:type:`ip6_routing` record that has
##    an *rtype* of 0.
##
## Returns: The vector of addresses contained in the routing header data.
function routing0_data_to_addrs%(s: string%): addr_vec
	%{
	auto rval = zeek::make_intrusive<zeek::VectorVal>(zeek::id::find_type<zeek::VectorType>("addr_vec"));

	int len = s->Len();
	const u_char* bytes = s->Bytes();
	bytes += 4; // go past 32-bit reserved field
	len -= 4;

	if ( ( len % 16 ) != 0 )
		zeek::reporter->Warning("Bad ip6_routing data length: %d", s->Len());

	while ( len > 0 )
		{
		zeek::IPAddr a(IPv6, (const uint32_t*) bytes, zeek::IPAddr::Network);
		rval->Assign(rval->Size(), zeek::make_intrusive<zeek::AddrVal>(a));
		bytes += 16;
		len -= 16;
		}

	return rval;
	%}

## Converts an :zeek:type:`addr` to an :zeek:type:`index_vec`.
##
## a: The address to convert into a vector of counts.
##
## Returns: A vector containing the host-order address representation,
##          four elements in size for IPv6 addresses, or one element for IPv4.
##
## .. zeek:see:: counts_to_addr
function addr_to_counts%(a: addr%): index_vec
	%{
	auto rval = zeek::make_intrusive<zeek::VectorVal>(zeek::id::index_vec);
	const uint32_t* bytes;
	int len = a->AsAddr().GetBytes(&bytes);

	for ( int i = 0; i < len; ++i )
		rval->Assign(i, zeek::val_mgr->Count(ntohl(bytes[i])));

	return rval;
	%}

## Converts an :zeek:type:`index_vec` to an :zeek:type:`addr`.
##
## v: The vector containing host-order IP address representation,
##    one element for IPv4 addresses, four elements for IPv6 addresses.
##
## Returns: An IP address.
##
## .. zeek:see:: addr_to_counts
function counts_to_addr%(v: index_vec%): addr
	%{
	auto vv = v->As<zeek::VectorVal*>();

	if ( vv->Size() == 1 )
		{
		return zeek::make_intrusive<zeek::AddrVal>(htonl(vv->CountAt(0)));
		}
	else if ( vv->Size() == 4 )
		{
		uint32_t bytes[4];
		for ( int i = 0; i < 4; ++i )
			bytes[i] = htonl(vv->CountAt(i));
		return zeek::make_intrusive<zeek::AddrVal>(bytes);
		}
	else
		{
		zeek::emit_builtin_error("invalid vector size", @ARG@[0]);
		uint32_t bytes[4];
		memset(bytes, 0, sizeof(bytes));
		return zeek::make_intrusive<zeek::AddrVal>(bytes);
		}
	%}

## Converts an :zeek:type:`enum` to an :zeek:type:`int`.
##
## e: The :zeek:type:`enum` to convert.
##
## Returns: The :zeek:type:`int` value that corresponds to the :zeek:type:`enum`.
function enum_to_int%(e: any%): int
	%{
	if ( e->GetType()->Tag() != zeek::TYPE_ENUM )
		{
		zeek::emit_builtin_error("enum_to_int() requires enum value");
		return zeek::val_mgr->Int(-1);
		}

	return zeek::val_mgr->Int(e->AsEnum());
	%}

## Converts a :zeek:type:`string` to an :zeek:type:`int`.
##
## str: The :zeek:type:`string` to convert.
##
## Returns: The :zeek:type:`string` *str* as :zeek:type:`int`.
##
## .. zeek:see:: to_addr to_port to_subnet
function to_int%(str: string%): int
	%{
	const char* s = str->CheckString();
	char* end_s;

	bro_int_t i = strtoll(s, &end_s, 10);

#if 0
	// Not clear we should complain.  For example, is " 205 "
	// a legal conversion?
	if ( s[0] == '\0' || end_s[0] != '\0' )
		zeek::emit_builtin_error("bad conversion to integer", @ARG@[0]);
#endif

	return zeek::val_mgr->Int(i);
	%}


## Converts a (positive) :zeek:type:`int` to a :zeek:type:`count`.
##
## n: The :zeek:type:`int` to convert.
##
## Returns: The :zeek:type:`int` *n* as unsigned integer, or 0 if *n* < 0.
function int_to_count%(n: int%): count
	%{
	if ( n < 0 )
		{
		zeek::emit_builtin_error("bad conversion to count", @ARG@[0]);
		n = 0;
		}
	return zeek::val_mgr->Count(n);
	%}

## Converts a :zeek:type:`double` to a :zeek:type:`count`.
##
## d: The :zeek:type:`double` to convert.
##
## Returns: The :zeek:type:`double` *d* as unsigned integer, or 0 if *d* < 0.0.
##          The value returned follows typical rounding rules, as implemented
##          by rint().
function double_to_int%(d: double%): int
	%{
	return zeek::val_mgr->Int(bro_int_t(rint(d)));
	%}

## Converts a :zeek:type:`double` to a :zeek:type:`int`.
##
## d: The :zeek:type:`double` to convert.
##
## Returns: The :zeek:type:`double` *d* as signed integer. The value returned
##          follows typical rounding rules, as implemented by rint().
##
## .. zeek:see:: double_to_time
function double_to_count%(d: double%): count
	%{
	if ( d < 0.0 )
		zeek::emit_builtin_error("bad conversion to count", @ARG@[0]);

	return zeek::val_mgr->Count(bro_uint_t(rint(d)));
	%}

## Converts a :zeek:type:`string` to a :zeek:type:`count`.
##
## str: The :zeek:type:`string` to convert.
##
## Returns: The :zeek:type:`string` *str* as unsigned integer, or 0 if *str* has
##          an invalid format.
##
## .. zeek:see:: to_addr to_int to_port to_subnet
function to_count%(str: string%): count
	%{
	const char* s = str->CheckString();
	char* end_s;

	uint64_t u = (uint64_t) strtoull(s, &end_s, 10);

	if ( s[0] == '\0' || end_s[0] != '\0' )
    	{
		zeek::emit_builtin_error("bad conversion to count", @ARG@[0]);
        u = 0;
        }

	return zeek::val_mgr->Count(u);
	%}

## Converts an :zeek:type:`interval` to a :zeek:type:`double`.
##
## i: The :zeek:type:`interval` to convert.
##
## Returns: The :zeek:type:`interval` *i* as :zeek:type:`double`.
##
## .. zeek:see:: double_to_interval
function interval_to_double%(i: interval%): double
	%{
	return zeek::make_intrusive<zeek::DoubleVal>(i);
	%}

## Converts a :zeek:type:`count` to a :zeek:type:`double`.
##
## c: The :zeek:type:`count` to convert.
##
## Returns: The :zeek:type:`count` *c* as :zeek:type:`double`.
##
## .. zeek:see:: int_to_double double_to_count
function count_to_double%(c: count%): double
	%{
	return zeek::make_intrusive<zeek::DoubleVal>(c);
	%}

## Converts an :zeek:type:`int` to a :zeek:type:`double`.
##
## i: The :zeek:type:`int` to convert.
##
## Returns: The :zeek:type:`int` *i* as :zeek:type:`double`.
##
## .. zeek:see:: count_to_double double_to_count
function int_to_double%(i: int%): double
	%{
	return zeek::make_intrusive<zeek::DoubleVal>(i);
	%}

## Converts a :zeek:type:`time` value to a :zeek:type:`double`.
##
## t: The :zeek:type:`time` to convert.
##
## Returns: The :zeek:type:`time` value *t* as :zeek:type:`double`.
##
## .. zeek:see:: double_to_time
function time_to_double%(t: time%): double
	%{
	return zeek::make_intrusive<zeek::DoubleVal>(t);
	%}

## Converts a :zeek:type:`double` value to a :zeek:type:`time`.
##
## d: The :zeek:type:`double` to convert.
##
## Returns: The :zeek:type:`double` value *d* as :zeek:type:`time`.
##
## .. zeek:see:: time_to_double double_to_count
function double_to_time%(d: double%): time
	%{
	return zeek::make_intrusive<zeek::TimeVal>(d);
	%}

## Converts a :zeek:type:`double` to an :zeek:type:`interval`.
##
## d: The :zeek:type:`double` to convert.
##
## Returns: The :zeek:type:`double` *d* as :zeek:type:`interval`.
##
## .. zeek:see:: interval_to_double
function double_to_interval%(d: double%): interval
	%{
	return zeek::make_intrusive<zeek::IntervalVal>(d);
	%}

## Converts a :zeek:type:`port` to a :zeek:type:`count`.
##
## p: The :zeek:type:`port` to convert.
##
## Returns: The :zeek:type:`port` *p* as :zeek:type:`count`.
##
## .. zeek:see:: count_to_port
function port_to_count%(p: port%): count
	%{
	return zeek::val_mgr->Count(p->Port());
	%}

## Converts a :zeek:type:`count` and ``transport_proto`` to a :zeek:type:`port`.
##
## num: The :zeek:type:`port` number.
##
## proto: The transport protocol.
##
## Returns: The :zeek:type:`count` *num* as :zeek:type:`port`.
##
## .. zeek:see:: port_to_count
function count_to_port%(num: count, proto: transport_proto%): port
	%{
	return zeek::val_mgr->Port(num, (TransportProto)proto->AsEnum());
	%}

## Converts a :zeek:type:`string` to an :zeek:type:`addr`.
##
## ip: The :zeek:type:`string` to convert.
##
## Returns: The :zeek:type:`string` *ip* as :zeek:type:`addr`, or the unspecified
##          address ``::`` if the input string does not parse correctly.
##
## .. zeek:see:: to_count to_int to_port count_to_v4_addr raw_bytes_to_v4_addr raw_bytes_to_v6_addr
##    to_subnet
function to_addr%(ip: string%): addr
	%{
	char* s = ip->AsString()->Render();
	zeek::ValPtr ret;
	in6_addr tmp;

	if ( zeek::IPAddr::ConvertString(s, &tmp) )
		ret = zeek::make_intrusive<zeek::AddrVal>(zeek::IPAddr(tmp));
	else
		{
		ret = zeek::make_intrusive<zeek::AddrVal>(zeek::IPAddr());
		zeek::emit_builtin_error("failed converting string to IP address", ip);
		}

	delete [] s;
	return ret;
	%}

## Checks if a string is a valid IPv4 or IPv6 address.
##
## ip: the string to check for valid IP formatting.
##
## Returns: T if the string is a valid IPv4 or IPv6 address format.
function is_valid_ip%(ip: string%): bool
	%{
	char* s = ip->AsString()->Render();
	auto rval = zeek::IPAddr::IsValid(s);
	delete [] s;
	return zeek::val_mgr->Bool(rval);
	%}

## Converts a :zeek:type:`string` to a :zeek:type:`subnet`.
##
## sn: The subnet to convert.
##
## Returns: The *sn* string as a :zeek:type:`subnet`, or the unspecified subnet
##          ``::/0`` if the input string does not parse correctly.
##
## .. zeek:see:: to_count to_int to_port count_to_v4_addr raw_bytes_to_v4_addr raw_bytes_to_v6_addr
##    to_addr
function to_subnet%(sn: string%): subnet
	%{
	char* s = sn->AsString()->Render();
	IPPrefix tmp;

	if ( ! IPPrefix::ConvertString(s, &tmp) )
		zeek::emit_builtin_error("failed converting string to IP prefix", sn);

	auto ret = zeek::make_intrusive<zeek::SubNetVal>(tmp);
	delete [] s;
	return ret;
	%}

## Converts a :zeek:type:`addr` to a :zeek:type:`subnet`.
##
## a: The address to convert.
##
## Returns: The address as a :zeek:type:`subnet`.
##
## .. zeek:see:: to_subnet
function addr_to_subnet%(a: addr%): subnet
	%{
	int width = (a->AsAddr().GetFamily() == IPv4 ? 32 : 128);
	return zeek::make_intrusive<zeek::SubNetVal>(a->AsAddr(), width);
	%}

## Converts a :zeek:type:`subnet` to an :zeek:type:`addr` by
## extracting the prefix.
##
## sn: The subnet to convert.
##
## Returns: The subnet as an :zeek:type:`addr`.
##
## .. zeek:see:: to_subnet
function subnet_to_addr%(sn: subnet%): addr
	%{
	return zeek::make_intrusive<zeek::AddrVal>(sn->Prefix());
	%}

## Returns the width of a :zeek:type:`subnet`.
##
## sn: The subnet.
##
## Returns: The width of the subnet.
##
## .. zeek:see:: to_subnet
function subnet_width%(sn: subnet%): count
	%{
	return zeek::val_mgr->Count(sn->Width());
	%}

## Converts a :zeek:type:`string` to a :zeek:type:`double`.
##
## str: The :zeek:type:`string` to convert.
##
## Returns: The :zeek:type:`string` *str* as double, or 0 if *str* has
##          an invalid format.
##
function to_double%(str: string%): double
	%{
	const char* s = str->CheckString();
	char* end_s;

	double d = strtod(s, &end_s);

	if ( s[0] == '\0' || end_s[0] != '\0' )
		{
		zeek::emit_builtin_error("bad conversion to double", @ARG@[0]);
		d = 0;
		}

	return zeek::make_intrusive<zeek::DoubleVal>(d);
	%}

## Converts a :zeek:type:`count` to an :zeek:type:`addr`.
##
## ip: The :zeek:type:`count` to convert.
##
## Returns: The :zeek:type:`count` *ip* as :zeek:type:`addr`.
##
## .. zeek:see:: raw_bytes_to_v4_addr to_addr to_subnet raw_bytes_to_v6_addr
function count_to_v4_addr%(ip: count%): addr
	%{
	if ( ip > 4294967295LU )
		{
		zeek::emit_builtin_error("conversion of non-IPv4 count to addr", @ARG@[0]);
		return zeek::make_intrusive<zeek::AddrVal>(uint32_t(0));
		}

	return zeek::make_intrusive<zeek::AddrVal>(htonl(uint32_t(ip)));
	%}

## Converts a :zeek:type:`string` of bytes into an IPv4 address. In particular,
## this function interprets the first 4 bytes of the string as an IPv4 address
## in network order.
##
## b: The raw bytes (:zeek:type:`string`) to convert.
##
## Returns: The byte :zeek:type:`string` *b* as :zeek:type:`addr`.
##
## .. zeek:see:: raw_bytes_to_v4_addr to_addr to_subnet
function raw_bytes_to_v4_addr%(b: string%): addr
	%{
	uint32_t a = 0;

	if ( b->Len() < 4 )
		zeek::emit_builtin_error("too short a string as input to raw_bytes_to_v4_addr()");

	else
		{
		const u_char* bp = b->Bytes();
		a = (bp[0] << 24) | (bp[1] << 16) | (bp[2] << 8) | bp[3];
		}

	return zeek::make_intrusive<zeek::AddrVal>(htonl(a));
	%}

## Converts a :zeek:type:`string` of bytes into an IPv6 address. In particular,
## this function interprets the first 16 bytes of the string as an IPv6 address
## in network order.
##
## b: The raw bytes (:zeek:type:`string`) to convert.
##
## Returns: The byte :zeek:type:`string` *b* as :zeek:type:`addr`.
##
## .. zeek:see:: raw_bytes_to_v6_addr to_addr to_subnet
function raw_bytes_to_v6_addr%(x: string%): addr
	%{
	uint32_t bytes[4] = {0, 0, 0, 0};

	if ( x->Len() < 16 )
		zeek::emit_builtin_error("too short a string as input to raw_bytes_to_v6_addr()");

	else
		{
		const u_char* xp = x->Bytes();
		bytes[0] = htonl((xp[0] << 24) | (xp[1] << 16) | (xp[2] << 8) | xp[3]);
		bytes[1] = htonl((xp[0+4] << 24) | (xp[1+4] << 16) | (xp[2+4] << 8) | xp[3+4]);
		bytes[2] = htonl((xp[0+8] << 24) | (xp[1+8] << 16) | (xp[2+8] << 8) | xp[3+8]);
		bytes[3] = htonl((xp[0+12] << 24) | (xp[1+12] << 16) | (xp[2+12] << 8) | xp[3+12]);
		}

	return zeek::make_intrusive<zeek::AddrVal>(bytes);
	%}

## Converts a :zeek:type:`string` to a :zeek:type:`port`.
##
## s: The :zeek:type:`string` to convert.
##
## Returns: A :zeek:type:`port` converted from *s*.
##
## .. zeek:see:: to_addr to_count to_int to_subnet
function to_port%(s: string%): port
    %{
    int port = 0;
    if ( s->Len() < 10 )
        {
        char* slash;
        errno = 0;
        port = strtol(s->CheckString(), &slash, 10);
        if ( ! errno )
            {
            ++slash;
            if ( zeek::util::streq(slash, "tcp") )
                return zeek::val_mgr->Port(port, TRANSPORT_TCP);
            else if ( zeek::util::streq(slash, "udp") )
                return zeek::val_mgr->Port(port, TRANSPORT_UDP);
            else if ( zeek::util::streq(slash, "icmp") )
                return zeek::val_mgr->Port(port, TRANSPORT_ICMP);
            }
        }

    zeek::emit_builtin_error("wrong port format, must be /[0-9]{1,5}\\/(tcp|udp|icmp)/");
    return zeek::val_mgr->Port(port, TRANSPORT_UNKNOWN);
	%}

## Converts a string of bytes (in network byte order) to a :zeek:type:`double`.
##
## s: A string of bytes containing the binary representation of a double value.
##
## Returns: The double value contained in *s*, or 0 if the conversion
##          failed.
##
function bytestring_to_double%(s: string%): double
	%{
	if ( s->Len() != sizeof(double) )
		{
		zeek::emit_builtin_error("bad conversion to double");
		return zeek::make_intrusive<zeek::DoubleVal>(0.0);
		}

	// See #908 for a discussion of portability.
	double d;
	memcpy(&d, s->Bytes(), sizeof(double));
	return zeek::make_intrusive<zeek::DoubleVal>(ntohd(d));
	%}

## Converts a string of bytes to a :zeek:type:`count`.
##
## s: A string of bytes containing the binary representation of the value.
##
## is_le: If true, *s* is assumed to be in little endian format, else it's big endian.
##
## Returns: The value contained in *s*, or 0 if the conversion failed.
##
function bytestring_to_count%(s: string, is_le: bool &default=F%): count
	%{
#ifdef HOST_BIGENDIAN
	static const bool host_bigendian = true;
#else
	static const bool host_bigendian = false;
#endif
	const u_char *p = s->Bytes();
	unsigned int i;

	switch ( s->Len() ) {
	case sizeof(uint8_t):
		{
		uint8_t value = 0;
		memcpy(&value, p, sizeof(uint8_t));
		return zeek::val_mgr->Count(value);
		}

	case sizeof(uint16_t):
		{
		uint16_t value = 0;

		if ( (host_bigendian && is_le) || (! host_bigendian && ! is_le) )
			{
			char buf[sizeof(uint16_t)];
			char *d = &buf[sizeof(uint16_t)-1];

			for ( i = 0; i < sizeof(uint16_t); i++ )
				*d-- = *p++;

			memcpy(&value, buf, sizeof(uint16_t));
			}
		else
			memcpy(&value, p, sizeof(uint16_t));

		return zeek::val_mgr->Count(value);
		}

	case sizeof(uint32_t):
		{
		uint32_t value = 0;

		if ( (host_bigendian && is_le) || (! host_bigendian && ! is_le) )
			{
			char buf[sizeof(uint32_t)];
			char *d = &buf[sizeof(uint32_t)-1];

			for ( i = 0; i < sizeof(uint32_t); i++ )
				*d-- = *p++;

			memcpy(&value, buf, sizeof(uint32_t));
			}
		else
			memcpy(&value, p, sizeof(uint32_t));

		return zeek::val_mgr->Count(value);
		}

	case sizeof(uint64_t):
		{
		uint64_t value = 0;

		if ( (host_bigendian && is_le) || (! host_bigendian && ! is_le) )
			{
			char buf[sizeof(uint64_t)];
			char *d = &buf[sizeof(uint64_t)-1];

			for ( i = 0; i < sizeof(uint64_t); i++ )
				*d-- = *p++;

			memcpy(&value, buf, sizeof(uint64_t));
			}
		else
			memcpy(&value, p, sizeof(uint64_t));

		return zeek::val_mgr->Count(value);
		}
	}

	zeek::emit_builtin_error("unsupported byte length for bytestring_to_count");
	return zeek::val_mgr->Count(0);
	%}

## Converts a reverse pointer name to an address. For example,
## ``1.0.168.192.in-addr.arpa`` to ``192.168.0.1``.
##
## s: The string with the reverse pointer name.
##
## Returns: The IP address corresponding to *s*.
##
## .. zeek:see:: addr_to_ptr_name to_addr
function ptr_name_to_addr%(s: string%): addr
	%{
	if ( s->Len() != 72 )
		{
		int a[4];
		uint32_t addr;
		char ss[13]; // this will contain "in-addr.arpa"

		if ( sscanf(s->CheckString(),
			"%d.%d.%d.%d.%12s",
			a, a+1, a+2, a+3, ss) != 5
			|| strcmp(ss, "in-addr.arpa") != 0 )
			{
			zeek::emit_builtin_error("bad PTR name", @ARG@[0]);
			addr = 0;
			}
		else
			addr = (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];

		return zeek::make_intrusive<zeek::AddrVal>(htonl(addr));
		}
	else
		{
		uint32_t addr6[4];
		uint32_t b[32];
		char ss[9]; // this will contain "ip6.arpa"
		if ( sscanf(s->CheckString(),
			"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
			"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
			"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
			"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x.%8s",
			b+31, b+30, b+29, b+28, b+27, b+26, b+25, b+24,
			b+23, b+22, b+21, b+20, b+19, b+18, b+17, b+16,
			b+15, b+14, b+13, b+12, b+11, b+10, b+9, b+8,
			b+7, b+6, b+5, b+4, b+3, b+2, b+1, b, ss) != 33
			|| strcmp(ss, "ip6.arpa") != 0 )
			{
			zeek::emit_builtin_error("bad PTR name", @ARG@[0]);
			memset(addr6, 0, sizeof addr6);
			}
		else
			{
			for ( unsigned int i = 0; i < 4; ++i )
				{
				uint32_t a = 0;
				for ( unsigned int j = 1; j <= 8; ++j )
					a |= b[8*i+j-1] << (32-j*4);

				addr6[i] = htonl(a);
				}
			}

		return zeek::make_intrusive<zeek::AddrVal>(addr6);
		}
	%}

## Converts an IP address to a reverse pointer name. For example,
## ``192.168.0.1`` to ``1.0.168.192.in-addr.arpa``.
##
## a: The IP address to convert to a reverse pointer name.
##
## Returns: The reverse pointer representation of *a*.
##
## .. zeek:see:: ptr_name_to_addr to_addr
function addr_to_ptr_name%(a: addr%): string
	%{
	return zeek::make_intrusive<zeek::StringVal>(a->AsAddr().PtrName().c_str());
	%}

## Converts a string of bytes into its hexadecimal representation.
## For example, ``"04"`` would be converted to ``"3034"``.
##
## bytestring: The string of bytes.
##
## Returns: The hexadecimal representation of *bytestring*.
##
## .. zeek:see:: hexdump hexstr_to_bytestring
function bytestring_to_hexstr%(bytestring: string%): string
	%{
	bro_uint_t len = bytestring->AsString()->Len();
	const u_char* bytes = bytestring->AsString()->Bytes();
	auto hextr_buf = std::make_unique<char[]>((2 * len) + 1);
	auto hexstr = hextr_buf.get();

	hexstr[0] = 0;
	for ( bro_uint_t i = 0; i < len; ++i )
		snprintf(hexstr + (2 * i), 3, "%.2hhx", bytes[i]);

	return zeek::make_intrusive<zeek::StringVal>(hexstr);
	%}

## Converts a hex-string into its binary representation.
## For example, ``"3034"`` would be converted to ``"04"``.
##
## The input string is assumed to contain an even number of hexadecimal digits
## (0-9, a-f, or A-F), otherwise behavior is undefined.
##
## hexstr: The hexadecimal string representation.
##
## Returns: The binary representation of *hexstr*.
##
## .. zeek:see:: hexdump bytestring_to_hexstr
function hexstr_to_bytestring%(hexstr: string%): string
	%{
	bro_uint_t len = hexstr->AsString()->Len();
	if ( len % 2 != 0 )
		{
		zeek::reporter->Error("Hex string '%s' has invalid length (not divisible by 2)", hexstr->CheckString());
		return zeek::val_mgr->EmptyString();
		}

	const char* bytes = hexstr->AsString()->CheckString();
	int outlen = (len/2);
	auto bytestring_buf = std::make_unique<char[]>(outlen);
	auto bytestring = bytestring_buf.get();
	memset(bytestring, 0, outlen);

	for ( bro_uint_t i = 0; i < len/2; ++i )
		{
		int res = sscanf(bytes + (2*i), "%2hhx", &bytestring[i]);

		if ( res == EOF )
			{
			zeek::reporter->Error("Hex string %s contains invalid input: %s", hexstr->CheckString(), strerror(errno));
			return zeek::val_mgr->EmptyString();
			}

		else if ( res != 1 )
			{
			zeek::reporter->Error("Could not read hex element from input %s", hexstr->CheckString());
			return zeek::val_mgr->EmptyString();
			}

		}

	return zeek::make_intrusive<zeek::StringVal>(outlen, bytestring);
	%}

## Encodes a Base64-encoded string.
##
## s: The string to encode.
##
## a: An optional custom alphabet. The empty string indicates the default
##    alphabet. If given, the string must consist of 64 unique characters.
##
## Returns: The encoded version of *s*.
##
## .. zeek:see:: decode_base64
function encode_base64%(s: string, a: string &default=""%): string
	%{
	String* t = zeek::detail::encode_base64(s->AsString(), a->AsString());
	if ( t )
		return zeek::make_intrusive<zeek::StringVal>(t);
	else
		{
		zeek::reporter->Error("Broker query has an invalid data store");
		return zeek::val_mgr->EmptyString();
		}
	%}

## Decodes a Base64-encoded string.
##
## s: The Base64-encoded string.
##
## a: An optional custom alphabet. The empty string indicates the default
##    alphabet. If given, the string must consist of 64 unique characters.
##
## Returns: The decoded version of *s*.
##
## .. zeek:see:: decode_base64_conn encode_base64
function decode_base64%(s: string, a: string &default=""%): string
	%{
	String* t = zeek::detail::decode_base64(s->AsString(), a->AsString());
	if ( t )
		return zeek::make_intrusive<zeek::StringVal>(t);
	else
		{
		zeek::reporter->Error("error in decoding string %s", s->CheckString());
		return zeek::val_mgr->EmptyString();
		}
	%}

## Decodes a Base64-encoded string that was derived from processing a connection.
## If an error is encountered decoding the string, that will be logged to
## ``weird.log`` with the associated connection.
##
## cid: The identifier of the connection that the encoding originates from.
##
## s: The Base64-encoded string.
##
## a: An optional custom alphabet. The empty string indicates the default
##    alphabet. If given, the string must consist of 64 unique characters.
##
## Returns: The decoded version of *s*.
##
## .. zeek:see:: decode_base64
function decode_base64_conn%(cid: conn_id, s: string, a: string &default=""%): string
	%{
	Connection* conn = session_mgr->FindConnection(cid);
	if ( ! conn )
		{
		zeek::emit_builtin_error("connection ID not a known connection", cid);
		return zeek::val_mgr->EmptyString();
		}

	String* t = zeek::detail::decode_base64(s->AsString(), a->AsString(), conn);
	if ( t )
		return zeek::make_intrusive<zeek::StringVal>(t);
	else
		{
		zeek::reporter->Error("error in decoding string %s", s->CheckString());
		return zeek::val_mgr->EmptyString();
		}
	%}

%%{
struct bro_uuid_t {
	uint32_t time_low;
	uint16_t time_mid;
	uint16_t time_hi_and_version;
	uint8_t clock_seq_hi_and_reserved;
	uint8_t clock_seq_low;
	uint8_t node[6];
};
%%}

## Converts a bytes representation of a UUID into its string form. For example,
## given a string of 16 bytes, it produces an output string in this format:
## ``550e8400-e29b-41d4-a716-446655440000``.
## See `<http://en.wikipedia.org/wiki/Universally_unique_identifier>`_.
##
## uuid: The 16 bytes of the UUID.
##
## Returns: The string representation of *uuid*.
function uuid_to_string%(uuid: string%): string
	%{
	if ( uuid->Len() != 16 )
		return zeek::make_intrusive<zeek::StringVal>("<Invalid UUID>");

	bro_uuid_t* id = (bro_uuid_t*) uuid->Bytes();

	static char s[1024];
	char* sp = s;

	sp += snprintf(sp, s + sizeof(s) - sp,
		"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
		id->time_low, id->time_mid, id->time_hi_and_version,
		id->clock_seq_hi_and_reserved, id->clock_seq_low,
		id->node[0],
		id->node[1],
		id->node[2],
		id->node[3],
		id->node[4],
		id->node[5]);

	return zeek::make_intrusive<zeek::StringVal>(s);
	%}

%%{
char* to_pat_str(int sn, const char* ss)
	{
	const char special_re_char[] = "^$-:\"\\/|*+?.(){}[]";

	char* pat = new char[sn * 4 + 1];
	int pat_len = 0;

	for ( int i = 0; i < sn; ++i )
		{
		if ( ! strchr(special_re_char, ss[i]) )
			pat[pat_len++] = ss[i];
		else
			{
			pat[pat_len++] = '\\';
			pat[pat_len++] = ss[i];
			}
		}
	pat[pat_len] = '\0';
	return pat;
	}
%%}

## Escapes a string so that it becomes a valid :zeek:type:`pattern` and can be
## used with the :zeek:id:`string_to_pattern`. Any character from the set
## ``^$-:"\/|*+?.(){}[]`` is prefixed with a ``\``.
##
## s: The string to escape.
##
## Returns: An escaped version of *s* that has the structure of a valid
##          :zeek:type:`pattern`.
##
## .. zeek:see:: string_to_pattern
##
function convert_for_pattern%(s: string%): string
	%{
	char* t = to_pat_str(s->Len(), (const char*)(s->Bytes()));
	auto ret = zeek::make_intrusive<zeek::StringVal>(t);
	delete [] t;
	return ret;
	%}

## Converts a :zeek:type:`string` into a :zeek:type:`pattern`.
##
## s: The string to convert.
##
## convert: If true, *s* is first passed through the function
##          :zeek:id:`convert_for_pattern` to escape special characters of
##          patterns.
##
## Returns: *s* as :zeek:type:`pattern`.
##
## .. zeek:see:: convert_for_pattern
function string_to_pattern%(s: string, convert: bool%): pattern
	%{
	const char* ss = (const char*) (s->Bytes());
	int sn = s->Len();
	char* pat;

	if ( convert )
		pat = to_pat_str(sn, ss);
	else
		{
		pat = new char[sn+1];
		memcpy(pat, ss, sn);
		pat[sn] = '\0';
		}

	RE_Matcher* re = new RE_Matcher(pat);
	delete [] pat;
	re->Compile();
	return zeek::make_intrusive<zeek::PatternVal>(re);
	%}

## Formats a given time value according to a format string.
##
## fmt: The format string. See ``man strftime`` for the syntax.
##
## d: The time value.
##
## Returns: The time *d* formatted according to *fmt*.
function strftime%(fmt: string, d: time%) : string
	%{
	static char buffer[128];

	time_t timeval = time_t(d);
	struct tm t;

	if ( ! localtime_r(&timeval, &t) ||
	     ! strftime(buffer, 128, fmt->CheckString(), &t) )
		return zeek::make_intrusive<zeek::StringVal>("<strftime error>");

	return zeek::make_intrusive<zeek::StringVal>(buffer);
	%}


## Parse a textual representation of a date/time value into a ``time`` type value.
##
## fmt: The format string used to parse the following *d* argument. See ``man strftime``
##      for the syntax.
##
## d: The string representing the time.
##
## Returns: The time value calculated from parsing *d* with *fmt*.
function strptime%(fmt: string, d: string%) : time
	%{
	const time_t timeval = time_t();
	struct tm t;

	if ( ! localtime_r(&timeval, &t) ||
	     ! strptime(d->CheckString(), fmt->CheckString(), &t) )
		{
		zeek::reporter->Warning("strptime conversion failed: fmt:%s d:%s", fmt->CheckString(), d->CheckString());
		return zeek::make_intrusive<zeek::TimeVal>(0.0);
		}

	double ret = mktime(&t);
	return zeek::make_intrusive<zeek::TimeVal>(ret);
	%}


# ===========================================================================
#
#                           Network Type Processing
#
# ===========================================================================

## Masks an address down to the number of given upper bits. For example,
## ``mask_addr(1.2.3.4, 18)`` returns ``1.2.0.0``.
##
## a: The address to mask.
##
## top_bits_to_keep: The number of top bits to keep in *a*; must be greater
##                   than 0 and less than 33 for IPv4, or 129 for IPv6.
##
## Returns: The address *a* masked down to *top_bits_to_keep* bits.
##
## .. zeek:see:: remask_addr
function mask_addr%(a: addr, top_bits_to_keep: count%): subnet
	%{
	return zeek::make_intrusive<zeek::SubNetVal>(a->AsAddr(), top_bits_to_keep);
	%}

## Takes some top bits (such as a subnet address) from one address and the other
## bits (intra-subnet part) from a second address and merges them to get a new
## address. This is useful for anonymizing at subnet level while preserving
## serial scans.
##
## a1: The address to mask with *top_bits_from_a1*.
##
## a2: The address to take the remaining bits from.
##
## top_bits_from_a1: The number of top bits to keep in *a1*; must be greater
##                   than 0 and less than 129.  This value is always interpreted
##                   relative to the IPv6 bit width (v4-mapped addresses start
##                   at bit number 96).
##
## Returns: The address *a* masked down to *top_bits_to_keep* bits.
##
## .. zeek:see:: mask_addr
function remask_addr%(a1: addr, a2: addr, top_bits_from_a1: count%): addr
	%{
	zeek::IPAddr addr1(a1->AsAddr());
	addr1.Mask(top_bits_from_a1);
	zeek::IPAddr addr2(a2->AsAddr());
	addr2.ReverseMask(top_bits_from_a1);
	return zeek::make_intrusive<zeek::AddrVal>(addr1|addr2);
	%}

## Checks whether a given :zeek:type:`port` has TCP as transport protocol.
##
## p: The :zeek:type:`port` to check.
##
## Returns: True iff *p* is a TCP port.
##
## .. zeek:see:: is_udp_port is_icmp_port
function is_tcp_port%(p: port%): bool
	%{
	return zeek::val_mgr->Bool(p->IsTCP());
	%}

## Checks whether a given :zeek:type:`port` has UDP as transport protocol.
##
## p: The :zeek:type:`port` to check.
##
## Returns: True iff *p* is a UDP port.
##
## .. zeek:see:: is_icmp_port is_tcp_port
function is_udp_port%(p: port%): bool
	%{
	return zeek::val_mgr->Bool(p->IsUDP());
	%}

## Checks whether a given :zeek:type:`port` has ICMP as transport protocol.
##
## p: The :zeek:type:`port` to check.
##
## Returns: True iff *p* is an ICMP port.
##
## .. zeek:see:: is_tcp_port is_udp_port
function is_icmp_port%(p: port%): bool
	%{
	return zeek::val_mgr->Bool(p->IsICMP());
	%}

%%{
static zeek::EnumValPtr map_conn_type(TransportProto tp)
	{
	switch ( tp ) {
	case TRANSPORT_UNKNOWN:
		return zeek::id::transport_proto->GetEnumVal(0);

	case TRANSPORT_TCP:
		return zeek::id::transport_proto->GetEnumVal(1);

	case TRANSPORT_UDP:
		return zeek::id::transport_proto->GetEnumVal(2);

	case TRANSPORT_ICMP:
		return zeek::id::transport_proto->GetEnumVal(3);

	default:
		zeek::reporter->InternalError("bad connection type in map_conn_type()");
	}

	// Cannot be reached;
	assert(false);
	return nullptr; // Make compiler happy.
	}
%%}

## Extracts the transport protocol from a connection.
##
## cid: The connection identifier.
##
## Returns: The transport protocol of the connection identified by *cid*.
##
## .. zeek:see:: get_port_transport_proto
##              get_orig_seq get_resp_seq
function get_conn_transport_proto%(cid: conn_id%): transport_proto
	%{
	Connection* c = session_mgr->FindConnection(cid);
	if ( ! c )
		{
		zeek::emit_builtin_error("unknown connection id in get_conn_transport_proto()", cid);
		return zeek::id::transport_proto->GetEnumVal(0);
		}

	return map_conn_type(c->ConnTransport());
	%}

## Extracts the transport protocol from a :zeek:type:`port`.
##
## p: The port.
##
## Returns: The transport protocol of the port *p*.
##
## .. zeek:see:: get_conn_transport_proto
##              get_orig_seq get_resp_seq
function get_port_transport_proto%(p: port%): transport_proto
	%{
	return map_conn_type(p->PortType());
	%}

## Checks whether a connection is (still) active.
##
## c: The connection id to check.
##
## Returns: True if the connection identified by *c* exists.
##
## .. zeek:see:: lookup_connection
function connection_exists%(c: conn_id%): bool
	%{
	if ( session_mgr->FindConnection(c) )
		return zeek::val_mgr->True();
	else
		return zeek::val_mgr->False();
	%}

## Returns the :zeek:type:`connection` record for a given connection identifier.
##
## cid: The connection ID.
##
## Returns: The :zeek:type:`connection` record for *cid*. If *cid* does not point
##          to an existing connection, the function generates a run-time error
##          and returns a dummy value.
##
## .. zeek:see:: connection_exists
function lookup_connection%(cid: conn_id%): connection
	%{
	Connection* conn = session_mgr->FindConnection(cid);
	if ( conn )
		return conn->GetVal();

	zeek::emit_builtin_error("connection ID not a known connection", cid);

	// Return a dummy connection record.
	auto c = zeek::make_intrusive<zeek::RecordVal>(zeek::id::connection);

	auto id_val = zeek::make_intrusive<zeek::RecordVal>(zeek::id::conn_id);
	id_val->Assign(0, zeek::make_intrusive<zeek::AddrVal>((unsigned int) 0));
	id_val->Assign(1, zeek::val_mgr->Port(ntohs(0), TRANSPORT_UDP));
	id_val->Assign(2, zeek::make_intrusive<zeek::AddrVal>((unsigned int) 0));
	id_val->Assign(3, zeek::val_mgr->Port(ntohs(0), TRANSPORT_UDP));
	c->Assign(0, std::move(id_val));

	auto orig_endp = zeek::make_intrusive<zeek::RecordVal>(zeek::id::endpoint);
	orig_endp->Assign(0, 0);
	orig_endp->Assign(1, 0);

	auto resp_endp = zeek::make_intrusive<zeek::RecordVal>(zeek::id::endpoint);
	resp_endp->Assign(0, 0);
	resp_endp->Assign(1, 0);

	c->Assign(1, std::move(orig_endp));
	c->Assign(2, std::move(resp_endp));

	c->AssignTime(3, zeek::run_state::network_time);
	c->AssignInterval(4, 0.0);
	c->Assign(5, zeek::make_intrusive<zeek::TableVal>(zeek::id::string_set));	// service
	c->Assign(6, zeek::val_mgr->EmptyString());	// history

	return c;
	%}

%%{
const char* conn_id_string(zeek::Val* c)
	{
	auto id_r = c->As<zeek::RecordVal*>()->GetFieldAs<zeek::RecordVal>(0);

	const zeek::IPAddr& orig_h = id_r->GetFieldAs<zeek::AddrVal>(0);
	uint32_t orig_p = id_r->GetFieldAs<zeek::PortVal>(1)->Port();
	const zeek::IPAddr& resp_h = id_r->GetFieldAs<zeek::AddrVal>(2);
	uint32_t resp_p = id_r->GetFieldAs<zeek::PortVal>(3)->Port();

	return zeek::util::fmt("%s/%u -> %s/%u\n", orig_h.AsString().c_str(), orig_p,
	                       resp_h.AsString().c_str(), resp_p);
	}
%%}

## Writes the current packet to a file.
##
## file_name: The name of the file to write the packet to.
##
## Returns: True on success.
##
## .. zeek:see:: dump_packet get_current_packet
function dump_current_packet%(file_name: string%) : bool
	%{
	const Packet* pkt;
	auto* pkt_src = dynamic_cast<zeek::iosource::PktSrc*>(zeek::run_state::detail::current_iosrc);

	if ( ! pkt_src || ! pkt_src->GetCurrentPacket(&pkt) )
		return zeek::val_mgr->False();

	if ( addl_pkt_dumper && addl_pkt_dumper->Path() != file_name->CheckString())
		{
		addl_pkt_dumper->Close();
		addl_pkt_dumper = nullptr;
		}

	if ( ! addl_pkt_dumper )
		addl_pkt_dumper = zeek::iosource_mgr->OpenPktDumper(file_name->CheckString(), true);

	if ( addl_pkt_dumper )
		{
		addl_pkt_dumper->Dump(pkt);
		}

	return zeek::val_mgr->Bool( addl_pkt_dumper && ! addl_pkt_dumper->IsError());
	%}

## Returns the currently processed PCAP packet.
##
## Returns: The currently processed packet, which is a record
##          containing the timestamp, ``snaplen``, and packet data.
##
## .. zeek:see:: dump_current_packet dump_packet
function get_current_packet%(%) : pcap_packet
	%{
	const Packet* p = nullptr;
	zeek::iosource::PktSrc* pkt_src = zeek::run_state::detail::current_packet_source();

	if ( ! pkt_src || ! pkt_src->GetCurrentPacket(&p) )
		p = nullptr;

	return Packet::ToVal(p);
	%}

## Function to get the raw headers of the currently processed packet.
##
## Returns: The :zeek:type:`raw_pkt_hdr` record containing the Layer 2, 3 and
##          4 headers of the currently processed packet.
##
## .. zeek:see:: raw_pkt_hdr get_current_packet
function get_current_packet_header%(%) : raw_pkt_hdr
	%{
	const Packet* p;
	zeek::iosource::PktSrc* pkt_src = zeek::run_state::detail::current_packet_source();

	if ( pkt_src && pkt_src->GetCurrentPacket(&p) )
		{
		return p->ToRawPktHdrVal();
		}

	static auto raw_pkt_hdr_type = zeek::id::find_type<zeek::RecordType>("raw_pkt_hdr");
	auto hdr = zeek::make_intrusive<zeek::RecordVal>(raw_pkt_hdr_type);
	return hdr;
	%}

## Writes a given packet to a file.
##
## pkt: The PCAP packet.
##
## file_name: The name of the file to write *pkt* to.
##
## Returns: True on success
##
## .. zeek:see:: get_current_packet dump_current_packet
function dump_packet%(pkt: pcap_packet, file_name: string%) : bool
	%{
	if ( addl_pkt_dumper && addl_pkt_dumper->Path() != file_name->CheckString())
		{
		addl_pkt_dumper->Close();
		addl_pkt_dumper = nullptr;
		}

	if ( ! addl_pkt_dumper )
		addl_pkt_dumper = zeek::iosource_mgr->OpenPktDumper(file_name->CheckString(), true);

	if ( ! addl_pkt_dumper->IsError() )
		{
		pkt_timeval ts;
		uint32_t caplen, len, link_type;
		u_char *data;

		auto pkt_r = pkt->As<zeek::RecordVal*>();

		ts.tv_sec = pkt_r->GetFieldAs<zeek::CountVal>(0);
		ts.tv_usec = pkt_r->GetFieldAs<zeek::CountVal>(1);
		caplen = pkt_r->GetFieldAs<zeek::CountVal>(2);
		len = pkt_r->GetFieldAs<zeek::CountVal>(3);
		data = pkt_r->GetFieldAs<zeek::StringVal>(4)->Bytes();
		link_type = pkt_r->GetFieldAs<zeek::EnumVal>(5);
		Packet p(link_type, &ts, caplen, len, data, true);

		addl_pkt_dumper->Dump(&p);
		}

	return zeek::val_mgr->Bool(addl_pkt_dumper && ! addl_pkt_dumper->IsError());
	%}

%%{
#include "zeek/DNS_Mgr.h"
#include "zeek/Trigger.h"

class LookupHostCallback : public zeek::detail::DNS_Mgr::LookupCallback {
public:
	LookupHostCallback(zeek::detail::trigger::Trigger* arg_trigger,
                           const void* arg_assoc, bool arg_lookup_name)
		{
		Ref(arg_trigger);
		trigger = arg_trigger;
		assoc = arg_assoc;
		lookup_name = arg_lookup_name;
		}

	~LookupHostCallback()
		{
		Unref(trigger);
		}

	// Overridden from zeek::detail::DNS_Mgr:Lookup:Callback.
	void Resolved(const std::string& name) override
		{
		zeek::Val* result = new zeek::StringVal(name);
		trigger->Cache(assoc, result);
		Unref(result);
		trigger->Release();
		}

	void Resolved(zeek::TableValPtr addrs) override
		{
		// No Ref() for addrs.
		trigger->Cache(assoc, addrs.get());
		trigger->Release();
		}

	void Timeout() override
		{
		if ( lookup_name )
			{
			zeek::Val* result = new zeek::StringVal("<\?\?\?>");
			trigger->Cache(assoc, result);
			Unref(result);
			}

		else
			{
			auto* lv = new zeek::ListVal(zeek::TYPE_ADDR);
			lv->Append(zeek::make_intrusive<zeek::AddrVal>("0.0.0.0"));
			auto result = lv->ToSetVal();
			trigger->Cache(assoc, result.get());
			Unref(lv);
			}

		trigger->Release();
		}

private:
	zeek::detail::trigger::Trigger* trigger;
	const void* assoc;
	bool lookup_name;
};
%%}

## Issues an asynchronous reverse DNS lookup and delays the function result.
## This function can therefore only be called inside a ``when`` condition,
## e.g., ``when ( local host = lookup_addr(10.0.0.1) ) { f(host); }``.
##
## host: The IP address to lookup.
##
## Returns: The DNS name of *host*.
##
## .. zeek:see:: lookup_hostname
function lookup_addr%(host: addr%) : string
	%{
	// FIXME: It should be easy to adapt the function to synchronous
	// lookups if we're reading a trace.
	zeek::detail::trigger::Trigger* trigger = frame->GetTrigger();

	if ( ! trigger)
		{
		zeek::emit_builtin_error("lookup_addr() can only be called inside a when-condition");
		return zeek::make_intrusive<zeek::StringVal>("<error>");
		}

	frame->SetDelayed();
	trigger->Hold();

	zeek::detail::dns_mgr->LookupAddr(host->AsAddr(),
			new LookupHostCallback(trigger, frame->GetTriggerAssoc(), true));
	return nullptr;
	%}

## Issues an asynchronous TEXT DNS lookup and delays the function result.
## This function can therefore only be called inside a ``when`` condition,
## e.g., ``when ( local h = lookup_hostname_txt("www.zeek.org") ) { f(h); }``.
##
## host: The hostname to lookup.
##
## Returns: The DNS TXT record associated with *host*.
##
## .. zeek:see:: lookup_hostname
function lookup_hostname_txt%(host: string%) : string
	%{
	// FIXME: Is should be easy to adapt the function to synchronous
	// lookups if we're reading a trace.
	zeek::detail::trigger::Trigger* trigger = frame->GetTrigger();

	if ( ! trigger)
		{
		zeek::emit_builtin_error("lookup_hostname_txt() can only be called inside a when-condition");
		return zeek::make_intrusive<zeek::StringVal>("<error>");
		}

	frame->SetDelayed();
	trigger->Hold();

	zeek::detail::dns_mgr->Lookup(host->CheckString(), T_TXT,
				 new LookupHostCallback(trigger, frame->GetTriggerAssoc(), true));
	return nullptr;
	%}

## Issues an asynchronous DNS lookup and delays the function result.
## This function can therefore only be called inside a ``when`` condition,
## e.g., ``when ( local h = lookup_hostname("www.zeek.org") ) { f(h); }``.
##
## host: The hostname to lookup.
##
## Returns: A set of DNS A and AAAA records associated with *host*.
##
## .. zeek:see:: lookup_addr
function lookup_hostname%(host: string%) : addr_set
	%{
	// FIXME: Is should be easy to adapt the function to synchronous
	// lookups if we're reading a trace.
	zeek::detail::trigger::Trigger* trigger = frame->GetTrigger();

	if ( ! trigger)
		{
		zeek::emit_builtin_error("lookup_hostname() can only be called inside a when-condition");
		return zeek::make_intrusive<zeek::StringVal>("<error>");
		}

	frame->SetDelayed();
	trigger->Hold();

	zeek::detail::dns_mgr->LookupHost(host->CheckString(),
			new LookupHostCallback(trigger, frame->GetTriggerAssoc(), false));
	return nullptr;
	%}

%%{
#ifdef USE_GEOIP
#include <chrono>

extern "C" {
#include <maxminddb.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
}

static int mmdb_msg_count = 0;
static constexpr int mmdb_msg_limit = 20;
static double mmdb_msg_suppression_time = 0;
static constexpr double mmdb_msg_suppression_duration = 300;

static void report_mmdb_msg(const char* format, ...)
	{
	if ( zeek::run_state::network_time > mmdb_msg_suppression_time + mmdb_msg_suppression_duration )
		{
		mmdb_msg_count = 0;
		mmdb_msg_suppression_time = zeek::run_state::network_time;
		}

	if ( mmdb_msg_count >= mmdb_msg_limit )
		return;

	++mmdb_msg_count;

	va_list al;
	va_start(al, format);
	std::string msg = zeek::util::fmt(format, al);
	va_end(al);

	zeek::reporter->Info("%s", msg.data());
	}

class MMDB {
public:
	MMDB(const char* filename, struct stat info);

	~MMDB();

	MMDB_lookup_result_s Lookup(const struct sockaddr* const sa);
	bool StaleDB();
	const char* Filename();

private:
	MMDB_s mmdb;
	struct stat file_info;
	bool lookup_error;
	std::chrono::time_point<std::chrono::steady_clock> last_check;
};

MMDB::MMDB(const char* filename, struct stat info)
	: file_info(info), lookup_error{false},
	  last_check{std::chrono::steady_clock::now()}
	{
	int status = MMDB_open(filename, MMDB_MODE_MMAP, &mmdb);

	if ( MMDB_SUCCESS != status )
		{
		throw std::runtime_error(MMDB_strerror(status));
		}
	}

MMDB::~MMDB()
	{
	MMDB_close(&mmdb);
	}

MMDB_lookup_result_s MMDB::Lookup(const struct sockaddr* const sa)
	{
	int mmdb_error;
	MMDB_lookup_result_s result = MMDB_lookup_sockaddr(&mmdb, sa, &mmdb_error);

	if ( MMDB_SUCCESS != mmdb_error )
		{
		lookup_error = true;
		throw std::runtime_error(MMDB_strerror(mmdb_error));
		}

	return result;
	}

// Check to see if the Maxmind DB should be closed and reopened.  This will
// happen if there was a lookup error or if the mmap'd file has been replaced
// by an external process.
bool MMDB::StaleDB()
	{
	struct stat buf;
	using Clock = std::chrono::steady_clock;
	std::chrono::time_point<Clock> now = Clock::now();

	if ( lookup_error )
		return true;

	// Only perform stat once per 5 minutes.
	using Min = std::chrono::minutes;
	if ( std::chrono::duration_cast<Min>(now - last_check).count() < 5 )
		return false;

	last_check = now;

	if ( 0 != stat(mmdb.filename, &buf) )
		return true;

	if ( buf.st_ino != file_info.st_ino || buf.st_mtime != file_info.st_mtime )
		{
		report_mmdb_msg("Inode change detected for MaxMind DB [%s]",
		                mmdb.filename);
		return true;
		}

	return false;
	}

const char* MMDB::Filename()
	{
	return mmdb.filename;
	}

std::unique_ptr<MMDB> mmdb_loc;
std::unique_ptr<MMDB> mmdb_asn;
static bool did_mmdb_loc_db_error = false;
static bool did_mmdb_asn_db_error = false;

static bool mmdb_open(const char* filename, bool asn)
	{
	struct stat buf;

	if ( 0 != stat(filename, &buf) )
		{
		return false;
		}

	try
		{
		if ( asn )
			{
			mmdb_asn.reset(new MMDB(filename, buf));
			}
		else
			{
			mmdb_loc.reset(new MMDB(filename, buf));
			}
		}

	catch ( const std::exception& e )
		{
		if ( asn )
			did_mmdb_asn_db_error = false;
		else
			did_mmdb_loc_db_error = false;

		report_mmdb_msg("Failed to open MaxMind DB: %s [%s]", filename,
		                e.what());
		return false;
		}

	return true;
	}

static bool mmdb_open_loc(const char* filename)
	{
	return mmdb_open(filename, false);
	}

static bool mmdb_open_asn(const char* filename)
	{
	return mmdb_open(filename, true);
	}

static void mmdb_check_loc()
	{
	if ( mmdb_loc && mmdb_loc->StaleDB() )
		{
		report_mmdb_msg("Closing stale MaxMind DB [%s]", mmdb_loc->Filename());
		did_mmdb_loc_db_error = false;
		mmdb_loc.release();
		}
	}

static void mmdb_check_asn()
	{
	if ( mmdb_asn && mmdb_asn->StaleDB() )
		{
		report_mmdb_msg("Closing stale MaxMind DB [%s]", mmdb_asn->Filename());
		did_mmdb_asn_db_error = false;
		mmdb_asn.release();
		}
	}

static bool mmdb_lookup(const zeek::IPAddr& addr, MMDB_lookup_result_s& result,
                        bool asn)
	{
	struct sockaddr_storage ss = {0};

	if ( IPv4 == addr.GetFamily() )
		{
		struct sockaddr_in* sa = (struct sockaddr_in*)&ss;
		sa->sin_family = AF_INET;
		addr.CopyIPv4(&sa->sin_addr);
		}

	else
		{
		struct sockaddr_in6* sa = (struct sockaddr_in6*)&ss;
		sa->sin6_family = AF_INET6;
		addr.CopyIPv6(&sa->sin6_addr);
		}

	try
		{
		result = asn ? mmdb_asn->Lookup((struct sockaddr*)&ss)
		             : mmdb_loc->Lookup((struct sockaddr*)&ss);
		}

	catch ( const std::exception& e )
		{
		report_mmdb_msg("MaxMind DB lookup location error [%s]", e.what());
		return false;
		}

	return result.found_entry;
	}

static bool mmdb_lookup_loc(const zeek::IPAddr& addr, MMDB_lookup_result_s& result)
	{
	return mmdb_lookup(addr, result, false);
	}

static bool mmdb_lookup_asn(const zeek::IPAddr& addr, MMDB_lookup_result_s& result)
	{
	return mmdb_lookup(addr, result, true);
	}

static zeek::ValPtr mmdb_getvalue(MMDB_entry_data_s* entry_data, int status,
                            int data_type )
	{
	switch (status)
		{
		case MMDB_SUCCESS:
			if ( entry_data->has_data )
				{
				switch (data_type)
					{
					case MMDB_DATA_TYPE_UTF8_STRING:
						return zeek::make_intrusive<zeek::StringVal>(
						    entry_data->data_size, entry_data->utf8_string);
						break;

					case MMDB_DATA_TYPE_DOUBLE:
						return zeek::make_intrusive<zeek::DoubleVal>(entry_data->double_value);
						break;

					case MMDB_DATA_TYPE_UINT32:
						return zeek::val_mgr->Count(entry_data->uint32);

					default:
						break;
					}
				}
			break;

		case MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR:
			// key doesn't exist, nothing to do
			break;

		default:
			report_mmdb_msg("MaxMind DB error [%s]", MMDB_strerror(status));
			break;
		}

	return nullptr;
	}

static bool mmdb_try_open_loc ()
	{
	// City database is always preferred over Country database.
	const auto& mmdb_dir_val = zeek::detail::global_scope()->Find("mmdb_dir")->GetVal();
	std::string mmdb_dir = mmdb_dir_val->AsString()->CheckString();

	if ( ! mmdb_dir.empty() )
		{
		auto d = mmdb_dir + "/GeoLite2-City.mmdb";

		if ( mmdb_open_loc(d.data()) )
			return true;

		d = mmdb_dir + "/GeoLite2-Country.mmdb";

		if ( mmdb_open_loc(d.data()) )
			return true;;
		}

	return mmdb_open_loc("/usr/share/GeoIP/GeoLite2-City.mmdb")
	    || mmdb_open_loc("/var/lib/GeoIP/GeoLite2-City.mmdb")
	    || mmdb_open_loc("/usr/local/share/GeoIP/GeoLite2-City.mmdb")
	    || mmdb_open_loc("/usr/local/var/GeoIP/GeoLite2-City.mmdb")
	    || mmdb_open_loc("/usr/share/GeoIP/GeoLite2-Country.mmdb")
	    || mmdb_open_loc("/var/lib/GeoIP/GeoLite2-Country.mmdb")
	    || mmdb_open_loc("/usr/local/share/GeoIP/GeoLite2-Country.mmdb")
	    || mmdb_open_loc("/usr/local/var/GeoIP/GeoLite2-Country.mmdb");
	}

static bool mmdb_try_open_asn ()
	{
	const auto& mmdb_dir_val = zeek::detail::global_scope()->Find("mmdb_dir")->GetVal();
	std::string mmdb_dir = mmdb_dir_val->AsString()->CheckString();

	if ( ! mmdb_dir.empty() )
		{
		auto d = mmdb_dir + "/GeoLite2-ASN.mmdb";

		if ( mmdb_open_asn(d.data()) )
			return true;
		}

	return mmdb_open_asn("/usr/share/GeoIP/GeoLite2-ASN.mmdb")
	    || mmdb_open_asn("/var/lib/GeoIP/GeoLite2-ASN.mmdb")
	    || mmdb_open_asn("/usr/local/share/GeoIP/GeoLite2-ASN.mmdb")
	    || mmdb_open_asn("/usr/local/var/GeoIP/GeoLite2-ASN.mmdb");
	}

#endif
%%}

## Initializes MMDB for later use of lookup_location.
## Requires Zeek to be built with ``libmaxminddb``.
##
## f: The filename of the MaxMind City or Country DB.
##
## Returns: A boolean indicating whether the db was successfully opened.
##
## .. zeek:see:: lookup_asn
function mmdb_open_location_db%(f: string%) : bool
	%{
#ifdef USE_GEOIP
	return zeek::val_mgr->Bool(mmdb_open_loc(f->CheckString()));
#else
	return zeek::val_mgr->False();
#endif
	%}

## Initializes MMDB for later use of lookup_asn.
## Requires Zeek to be built with ``libmaxminddb``.
##
## f: The filename of the MaxMind ASN DB.
##
## Returns: A boolean indicating whether the db was successfully opened.
##
## .. zeek:see:: lookup_asn
function mmdb_open_asn_db%(f: string%) : bool
	%{
#ifdef USE_GEOIP
	return zeek::val_mgr->Bool(mmdb_open_asn(f->CheckString()));
#else
	return zeek::val_mgr->False();
#endif
	%}

## Performs a geo-lookup of an IP address.
## Requires Zeek to be built with ``libmaxminddb``.
##
## a: The IP address to lookup.
##
## Returns: A record with country, region, city, latitude, and longitude.
##
## .. zeek:see:: lookup_asn
function lookup_location%(a: addr%) : geo_location
	%{
	static auto geo_location = zeek::id::find_type<zeek::RecordType>("geo_location");
	auto location = zeek::make_intrusive<zeek::RecordVal>(geo_location);

#ifdef USE_GEOIP
	mmdb_check_loc();
	if ( ! mmdb_loc )
		{
		if ( ! mmdb_try_open_loc() )
			{
			if ( ! did_mmdb_loc_db_error )
				{
				did_mmdb_loc_db_error = true;
				zeek::emit_builtin_error("Failed to open GeoIP location database");
				}

			return location;
			}
		}

	MMDB_lookup_result_s result;

	if ( mmdb_lookup_loc(a->AsAddr(), result) )
		{
		MMDB_entry_data_s entry_data;
		int status;

		// Get Country ISO Code
		status = MMDB_get_value(&result.entry, &entry_data,
		                        "country", "iso_code", nullptr);
		location->Assign(0, mmdb_getvalue(&entry_data, status,
		                 MMDB_DATA_TYPE_UTF8_STRING));

		// Get Major Subdivision ISO Code
		status = MMDB_get_value(&result.entry, &entry_data,
		                        "subdivisions", "0", "iso_code", nullptr);
		location->Assign(1, mmdb_getvalue(&entry_data, status,
		                 MMDB_DATA_TYPE_UTF8_STRING));

		// Get City English Name
		status = MMDB_get_value(&result.entry, &entry_data,
		                        "city", "names", "en", nullptr);
		location->Assign(2, mmdb_getvalue(&entry_data, status,
		                 MMDB_DATA_TYPE_UTF8_STRING));

		// Get Location Latitude
		status = MMDB_get_value(&result.entry, &entry_data,
		                        "location", "latitude", nullptr);
		location->Assign(3, mmdb_getvalue(&entry_data, status,
		                 MMDB_DATA_TYPE_DOUBLE));

		// Get Location Longitude
		status = MMDB_get_value(&result.entry, &entry_data,
		                        "location", "longitude", nullptr);
		location->Assign(4, mmdb_getvalue(&entry_data, status,
		                 MMDB_DATA_TYPE_DOUBLE));

		return location;
		}

#else // not USE_GEOIP
	static int missing_geoip_reported = 0;

	if ( ! missing_geoip_reported )
		{
		zeek::emit_builtin_error("Zeek was not configured for GeoIP support");
		missing_geoip_reported = 1;
		}
#endif

	// We can get here even if we have MMDB support if we weren't
	// able to initialize it or it didn't return any information for
	// the address.

	return location;
	%}

## Performs an ASN lookup of an IP address.
## Requires Zeek to be built with ``libmaxminddb``.
##
## a: The IP address to lookup.
##
## Returns: The number of the ASN that contains *a*.
##
## .. zeek:see:: lookup_location
function lookup_asn%(a: addr%) : count
	%{
#ifdef USE_GEOIP
	mmdb_check_asn();
	if ( ! mmdb_asn )
		{
		if ( ! mmdb_try_open_asn() )
			{
			if ( ! did_mmdb_asn_db_error )
				{
				did_mmdb_asn_db_error = true;
				zeek::emit_builtin_error("Failed to open GeoIP ASN database");
				}

			return zeek::val_mgr->Count(0);
			}
		}

	MMDB_lookup_result_s result;

	if ( mmdb_lookup_asn(a->AsAddr(), result) )
		{
		MMDB_entry_data_s entry_data;
		int status;

		// Get Autonomous System Number
		status = MMDB_get_value(&result.entry, &entry_data,
		                        "autonomous_system_number", nullptr);
		auto asn = mmdb_getvalue(&entry_data, status, MMDB_DATA_TYPE_UINT32);
		return asn == nullptr ? zeek::val_mgr->Count(0) : asn;
		}

#else // not USE_GEOIP
	static int missing_geoip_reported = 0;

	if ( ! missing_geoip_reported )
		{
		zeek::emit_builtin_error("Zeek was not configured for GeoIP ASN support");
		missing_geoip_reported = 1;
		}
#endif

	// We can get here even if we have GeoIP support, if we weren't
	// able to initialize it or it didn't return any information for
	// the address.
	return zeek::val_mgr->Count(0);
	%}

## Calculates distance between two geographic locations using the haversine
## formula.  Latitudes and longitudes must be given in degrees, where southern
## hemispere latitudes are negative and western hemisphere longitudes are
## negative.
##
## lat1: Latitude (in degrees) of location 1.
##
## long1: Longitude (in degrees) of location 1.
##
## lat2: Latitude (in degrees) of location 2.
##
## long2: Longitude (in degrees) of location 2.
##
## Returns: Distance in miles.
##
## .. zeek:see:: haversine_distance_ip
function haversine_distance%(lat1: double, long1: double, lat2: double, long2: double%): double
	%{
	const double PI = 3.14159;
	const double RADIUS = 3958.8;  // Earth's radius in miles.

	double s1 = sin((lat2 - lat1) * PI/360);
	double s2 = sin((long2 - long1) * PI/360);
	double a = s1 * s1 + cos(lat1 * PI/180) * cos(lat2 * PI/180) * s2 * s2;
	double distance = 2 * RADIUS * asin(sqrt(a));

	return zeek::make_intrusive<zeek::DoubleVal>(distance);
	%}

## Converts UNIX file permissions given by a mode to an ASCII string.
##
## mode: The permissions (an octal number like 0644 converted to decimal).
##
## Returns: A string representation of *mode* in the format
##          ``rw[xsS]rw[xsS]rw[xtT]``.
function file_mode%(mode: count%): string
	%{
	char str[12];
	char *p = str;

	/* usr */
	if (mode & S_IRUSR)
		*p++ = 'r';
	else
		*p++ = '-';

	if (mode & S_IWUSR)
		*p++ = 'w';
	else
		*p++ = '-';

	switch (mode & (S_IXUSR | S_ISUID)) {
	case 0:
		*p++ = '-';
		break;
	case S_IXUSR:
		*p++ = 'x';
		break;
	case S_ISUID:
		*p++ = 'S';
		break;
	case S_IXUSR | S_ISUID:
		*p++ = 's';
		break;
	}

	/* group */
	if (mode & S_IRGRP)
		*p++ = 'r';
	else
		*p++ = '-';
	if (mode & S_IWGRP)
		*p++ = 'w';
	else
		*p++ = '-';

	switch (mode & (S_IXGRP | S_ISGID)) {
	case 0:
		*p++ = '-';
		break;
	case S_IXGRP:
		*p++ = 'x';
		break;
	case S_ISGID:
		*p++ = 'S';
		break;
	case S_IXGRP | S_ISGID:
		*p++ = 's';
		break;
	}

	/* other */
	if (mode & S_IROTH)
		*p++ = 'r';
	else
		*p++ = '-';
	if (mode & S_IWOTH)
		*p++ = 'w';
	else
		*p++ = '-';

	switch (mode & (S_IXOTH | S_ISVTX)) {
	case 0:
		*p++ = '-';
		break;
	case S_IXOTH:
		*p++ = 'x';
		break;
	case S_ISVTX:
		*p++ = 'T';
		break;
	case S_IXOTH | S_ISVTX:
		*p++ = 't';
		break;
	}

	*p = '\0';

	return zeek::make_intrusive<zeek::StringVal>(str);
	%}

# ===========================================================================
#
#                        Controlling Analyzer Behavior
#
# ===========================================================================

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

## Disables the analyzer which raised the current event (if the analyzer
## belongs to the given connection).
##
## cid: The connection identifier.
##
## aid: The analyzer ID.
##
## err_if_no_conn: Emit an error message if the connection does not exit.
##
## prevent: Prevent the same analyzer type from being attached in the future.
##          This is useful for preventing the same analyzer from being
##          automatically reattached in the future, e.g. as a result of a
##          DPD signature suddenly matching.
##
## Returns: True if the connection identified by *cid* exists and has analyzer
##          *aid* and it is scheduled for removal.
##
## .. zeek:see:: Analyzer::schedule_analyzer Analyzer::name
function disable_analyzer%(cid: conn_id, aid: count, err_if_no_conn: bool &default=T, prevent: bool &default=F%) : bool
	%{
	Connection* c = session_mgr->FindConnection(cid);
	if ( ! c )
		{
		zeek::reporter->Error("cannot find connection");
		return zeek::val_mgr->False();
		}

	analyzer::Analyzer* a = c->FindAnalyzer(aid);
	if ( ! a )
		{
		if ( err_if_no_conn )
			zeek::reporter->Error("connection does not have analyzer specified to disable");
		return zeek::val_mgr->False();
		}

	if ( prevent )
		a->Parent()->PreventChildren(a->GetAnalyzerTag());

	auto rval = a->Remove();
	return zeek::val_mgr->Bool(rval);
	%}

## Informs Zeek that it should skip any further processing of the contents of
## a given connection. In particular, Zeek will refrain from reassembling the
## TCP byte stream and from generating events relating to any analyzers that
## have been processing the connection.
##
## cid: The connection ID.
##
## Returns: False if *cid* does not point to an active connection, and true
##          otherwise.
##
## .. note::
##
##     Zeek will still generate connection-oriented events such as
##     :zeek:id:`connection_finished`.
function skip_further_processing%(cid: conn_id%): bool
	%{
	Connection* c = session_mgr->FindConnection(cid);
	if ( ! c )
		return zeek::val_mgr->False();

	c->GetSessionAdapter()->SetSkip(1);
	return zeek::val_mgr->True();
	%}

## Controls whether packet contents belonging to a connection should be
## recorded (when ``-w`` option is provided on the command line).
##
## cid: The connection identifier.
##
## do_record: True to enable packet contents, and false to disable for the
##            connection identified by *cid*.
##
## Returns: False if *cid* does not point to an active connection, and true
##          otherwise.
##
## .. zeek:see:: skip_further_processing
##
## .. note::
##
##     This is independent of whether Zeek processes the packets of this
##     connection, which is controlled separately by
##     :zeek:id:`skip_further_processing`.
##
## .. zeek:see:: get_contents_file set_contents_file
function set_record_packets%(cid: conn_id, do_record: bool%): bool
	%{
	Connection* c = session_mgr->FindConnection(cid);
	if ( ! c )
		return zeek::val_mgr->False();

	c->SetRecordPackets(do_record);
	return zeek::val_mgr->True();
	%}

## Sets an individual inactivity timeout for a connection and thus
## overrides the global inactivity timeout.
##
## cid: The connection ID.
##
## t: The new inactivity timeout for the connection identified by *cid*.
##
## Returns: The previous timeout interval.
function set_inactivity_timeout%(cid: conn_id, t: interval%): interval
	%{
	Connection* c = session_mgr->FindConnection(cid);
	if ( ! c )
		return zeek::make_intrusive<zeek::IntervalVal>(0.0);

	double old_timeout = c->InactivityTimeout();
	c->SetInactivityTimeout(t);

	return zeek::make_intrusive<zeek::IntervalVal>(old_timeout);
	%}

# ===========================================================================
#
#                            Files and Directories
#
# ===========================================================================

## Opens a file for writing. If a file with the same name already exists, this
## function overwrites it (as opposed to :zeek:id:`open_for_append`).
##
## f: The path to the file.
##
## Returns: A :zeek:type:`file` handle for subsequent operations.
##
## .. zeek:see:: active_file open_for_append close write_file
##              get_file_name set_buf flush_all mkdir enable_raw_output
##              rmdir unlink rename
function open%(f: string%): file
	%{
	const char* file = f->CheckString();

	if ( zeek::util::streq(file, "-") )
		return zeek::make_intrusive<zeek::FileVal>(zeek::make_intrusive<zeek::File>(stdout, "-", "w"));
	else
		return zeek::make_intrusive<zeek::FileVal>(zeek::make_intrusive<zeek::File>(file, "w"));
	%}

## Opens a file for writing or appending. If a file with the same name already
## exists, this function appends to it (as opposed to :zeek:id:`open`).
##
## f: The path to the file.
##
## Returns: A :zeek:type:`file` handle for subsequent operations.
##
## .. zeek:see:: active_file open close write_file
##              get_file_name set_buf flush_all mkdir enable_raw_output
##              rmdir unlink rename
function open_for_append%(f: string%): file
	%{
	return zeek::make_intrusive<zeek::FileVal>(zeek::make_intrusive<zeek::File>(f->CheckString(), "a"));
	%}

## Closes an open file and flushes any buffered content.
##
## f: A :zeek:type:`file` handle to an open file.
##
## Returns: True on success.
##
## .. zeek:see:: active_file open open_for_append write_file
##              get_file_name set_buf flush_all mkdir enable_raw_output
##              rmdir unlink rename
function close%(f: file%): bool
	%{
	return zeek::val_mgr->Bool(f->Close());
	%}

## Writes data to an open file.
##
## f: A :zeek:type:`file` handle to an open file.
##
## data: The data to write to *f*.
##
## Returns: True on success.
##
## .. zeek:see:: active_file open open_for_append close
##              get_file_name set_buf flush_all mkdir enable_raw_output
##              rmdir unlink rename
function write_file%(f: file, data: string%): bool
	%{
	if ( ! f )
		return zeek::val_mgr->False();

	return zeek::val_mgr->Bool(f->Write((const char*) data->Bytes(), data->Len()));
	%}

## Alters the buffering behavior of a file.
##
## f: A :zeek:type:`file` handle to an open file.
##
## buffered: When true, *f* is fully buffered, i.e., bytes are saved in a
##           buffer until the block size has been reached. When
##           false, *f* is line buffered, i.e., bytes are saved up until a
##           newline occurs.
##
## .. zeek:see:: active_file open open_for_append close
##              get_file_name write_file flush_all mkdir enable_raw_output
##              rmdir unlink rename
function set_buf%(f: file, buffered: bool%): any
	%{
	f->SetBuf(buffered);
	return zeek::val_mgr->True();
	%}

## Flushes all open files to disk.
##
## Returns: True on success.
##
## .. zeek:see:: active_file open open_for_append close
##              get_file_name write_file set_buf mkdir enable_raw_output
##              rmdir unlink rename
function flush_all%(%): bool
	%{
	return zeek::val_mgr->Bool(fflush(0) == 0);
	%}

## Creates a new directory.
##
## f: The directory name.
##
## Returns: True if the operation succeeds or if *f* already exists,
##          and false if the file creation fails.
##
## .. zeek:see:: active_file open_for_append close write_file
##              get_file_name set_buf flush_all enable_raw_output
##              rmdir unlink rename
function mkdir%(f: string%): bool
	%{
	const char* filename = f->CheckString();

	if ( mkdir(filename, 0777) < 0 )
		{
		int error = errno;
		struct stat filestat;
		// check if already exists and is directory.
		if ( errno == EEXIST && stat(filename, &filestat) == 0
		     && S_ISDIR(filestat.st_mode) )
			return zeek::val_mgr->True();

		zeek::emit_builtin_error(zeek::util::fmt("cannot create directory '%s': %s", filename,
		                                         strerror(error)));
		return zeek::val_mgr->False();
		}
	else
		return zeek::val_mgr->True();
	%}


## Removes a directory.
##
## d: The directory name.
##
## Returns: True if the operation succeeds, and false if the
##          directory delete operation fails.
##
## .. zeek:see:: active_file open_for_append close write_file
##              get_file_name set_buf flush_all enable_raw_output
##              mkdir unlink rename
function rmdir%(d: string%): bool
	%{
	const char* dirname = d->CheckString();

	if ( rmdir(dirname) < 0 )
		{
		zeek::emit_builtin_error(zeek::util::fmt("cannot remove directory '%s': %s", dirname,
		                                         strerror(errno)));
		return zeek::val_mgr->False();
		}
	else
		return zeek::val_mgr->True();
	%}

## Removes a file from a directory.
##
## f: the file to delete.
##
## Returns: True if the operation succeeds and the file was deleted,
##          and false if the deletion fails.
##
## .. zeek:see:: active_file open_for_append close write_file
##              get_file_name set_buf flush_all enable_raw_output
##              mkdir rmdir rename
function unlink%(f: string%): bool
	%{
	const char* filename = f->CheckString();

	if ( unlink(filename) < 0 )
		{
		zeek::emit_builtin_error(zeek::util::fmt("cannot unlink file '%s': %s", filename,
		                                         strerror(errno)));
		return zeek::val_mgr->False();
		}
	else
		return zeek::val_mgr->True();
	%}

## Renames a file from src_f to dst_f.
##
## src_f: the name of the file to rename.
##
## dest_f: the name of the file after the rename operation.
##
## Returns: True if the rename succeeds and false otherwise.
##
## .. zeek:see:: active_file open_for_append close write_file
##              get_file_name set_buf flush_all enable_raw_output
##              mkdir rmdir unlink
function rename%(src_f: string, dst_f: string%): bool
	%{
	const char* src_filename = src_f->CheckString();
	const char* dst_filename = dst_f->CheckString();

	if ( rename(src_filename, dst_filename) < 0 )
		{
		zeek::emit_builtin_error(zeek::util::fmt("cannot rename file '%s' to '%s': %s", src_filename,
		                                         dst_filename, strerror(errno)));
		return zeek::val_mgr->False();
		}
	else
		return zeek::val_mgr->True();
	%}

## Checks whether a given file is open.
##
## f: The file to check.
##
## Returns: True if *f* is an open :zeek:type:`file`.
##
## .. todo:: Rename to ``is_open``.
function active_file%(f: file%): bool
	%{
	return zeek::val_mgr->Bool(f->IsOpen());
	%}

## Gets the filename associated with a file handle.
##
## f: The file handle to inquire the name for.
##
## Returns: The filename associated with *f*.
##
## .. zeek:see:: open
function get_file_name%(f: file%): string
	%{
	if ( ! f )
		return zeek::val_mgr->EmptyString();

	return zeek::make_intrusive<zeek::StringVal>(f->Name());
	%}

## Rotates a file.
##
## f: An open file handle.
##
## Returns: Rotation statistics which include the original file name, the name
##          after the rotation, and the time when *f* was opened/closed.
##
## .. zeek:see:: rotate_file_by_name calc_next_rotate
function rotate_file%(f: file%): rotate_info
	%{
	zeek::RecordValPtr info{zeek::AdoptRef{}, f->Rotate()};

	if ( info )
		return info;

	// Record indicating error.
	static auto rotate_info = zeek::id::find_type<zeek::RecordType>("rotate_info");
	info = zeek::make_intrusive<zeek::RecordVal>(rotate_info);
	info->Assign(0, zeek::val_mgr->EmptyString());
	info->Assign(1, zeek::val_mgr->EmptyString());
	info->AssignTime(2, 0.0);
	info->AssignTime(3, 0.0);

	return info;
	%}

## Rotates a file identified by its name.
##
## f: The name of the file to rotate
##
## Returns: Rotation statistics which include the original file name, the name
##          after the rotation, and the time when *f* was opened/closed.
##
## .. zeek:see:: rotate_file calc_next_rotate
function rotate_file_by_name%(f: string%): rotate_info
	%{
	static auto rotate_info = zeek::id::find_type<zeek::RecordType>("rotate_info");
	auto info = zeek::make_intrusive<zeek::RecordVal>(rotate_info);

	bool is_pkt_dumper = false;
	bool is_addl_pkt_dumper = false;

	// Special case: one of current dump files.
	if ( zeek::run_state::detail::pkt_dumper &&
	     zeek::util::streq(zeek::run_state::detail::pkt_dumper->Path().c_str(), f->CheckString()) )
		{
		is_pkt_dumper = true;
		zeek::run_state::detail::pkt_dumper->Close();
		}

	if ( addl_pkt_dumper &&
	     zeek::util::streq(addl_pkt_dumper->Path().c_str(), f->CheckString()) )
		{
		is_addl_pkt_dumper = true;
		addl_pkt_dumper->Close();
		}

	FILE* file = zeek::util::detail::rotate_file(f->CheckString(), info.get());
	if ( ! file )
		{
		// Record indicating error.
		info->Assign(0, zeek::val_mgr->EmptyString());
		info->Assign(1, zeek::val_mgr->EmptyString());
		info->AssignTime(2, 0.0);
		info->AssignTime(3, 0.0);
		return info;
		}

	fclose(file);

	if ( is_pkt_dumper )
		{
		info->AssignTime(2, zeek::run_state::detail::pkt_dumper->OpenTime());
		zeek::run_state::detail::pkt_dumper->Open();
		}

	if ( is_addl_pkt_dumper )
		info->AssignTime(2, addl_pkt_dumper->OpenTime());

	return info;
	%}

## Calculates the duration until the next time a file is to be rotated, based
## on a given rotate interval.
##
## i: The rotate interval to base the calculation on.
##
## Returns: The duration until the next file rotation time.
##
## .. zeek:see:: rotate_file rotate_file_by_name
function calc_next_rotate%(i: interval%) : interval
	%{
	static auto log_rotate_base_time = zeek::id::find_val<zeek::StringVal>("log_rotate_base_time");
	static auto base_time = log_rotate_base_time->AsString()->CheckString();

	double base = zeek::util::detail::parse_rotate_base_time(base_time);
	return zeek::make_intrusive<zeek::IntervalVal>(zeek::util::detail::calc_next_rotate(zeek::run_state::network_time, i, base));
	%}

## Returns the size of a given file.
##
## f: The name of the file whose size to lookup.
##
## Returns: The size of *f* in bytes.
function file_size%(f: string%) : double
	%{
	struct stat s;

	if ( stat(f->CheckString(), &s) < 0 )
		return zeek::make_intrusive<zeek::DoubleVal>(-1.0);

	return zeek::make_intrusive<zeek::DoubleVal>(double(s.st_size));
	%}

## Prevents escaping of non-ASCII characters when writing to a file.
## This function is equivalent to :zeek:attr:`&raw_output`.
##
## f: The file to disable raw output for.
function enable_raw_output%(f: file%): any
	%{
	f->EnableRawOutput();
	return nullptr;
	%}

# ===========================================================================
#
#                              Packet Filtering
#
# ===========================================================================

## Installs a filter to drop packets from a given IP source address with
## a certain probability if none of a given set of TCP flags are set.
## Note that for IPv6 packets with a Destination options header that has
## the Home Address option, this filters out against that home address.
##
## ip: The IP address to drop.
##
## tcp_flags: If none of these TCP flags are set, drop packets from *ip* with
##            probability *prob*.
##
## prob: The probability [0.0, 1.0] used to drop packets from *ip*.
##
## Returns: True (unconditionally).
##
## .. zeek:see:: Pcap::precompile_pcap_filter
##              Pcap::install_pcap_filter
##              install_src_net_filter
##              uninstall_src_addr_filter
##              uninstall_src_net_filter
##              install_dst_addr_filter
##              install_dst_net_filter
##              uninstall_dst_addr_filter
##              uninstall_dst_net_filter
##              Pcap::error
##
## .. todo:: The return value should be changed to any.
function install_src_addr_filter%(ip: addr, tcp_flags: count, prob: double%) : bool
	%{
	packet_mgr->GetPacketFilter()->AddSrc(ip->AsAddr(), tcp_flags, prob);
	return zeek::val_mgr->True();
	%}

## Installs a filter to drop packets originating from a given subnet with
## a certain probability if none of a given set of TCP flags are set.
##
## snet: The subnet to drop packets from.
##
## tcp_flags: If none of these TCP flags are set, drop packets from *snet* with
##            probability *prob*.
##
## prob: The probability [0.0, 1.0] used to drop packets from *snet*.
##
## Returns: True (unconditionally).
##
## .. zeek:see:: Pcap::precompile_pcap_filter
##              Pcap::install_pcap_filter
##              install_src_addr_filter
##              uninstall_src_addr_filter
##              uninstall_src_net_filter
##              install_dst_addr_filter
##              install_dst_net_filter
##              uninstall_dst_addr_filter
##              uninstall_dst_net_filter
##              Pcap::error
##
## .. todo:: The return value should be changed to any.
function install_src_net_filter%(snet: subnet, tcp_flags: count, prob: double%) : bool
	%{
	packet_mgr->GetPacketFilter()->AddSrc(snet, tcp_flags, prob);
	return zeek::val_mgr->True();
	%}

## Removes a source address filter.
##
## ip: The IP address for which a source filter was previously installed.
##
## Returns: True on success.
##
## .. zeek:see:: Pcap::precompile_pcap_filter
##              Pcap::install_pcap_filter
##              install_src_addr_filter
##              install_src_net_filter
##              uninstall_src_net_filter
##              install_dst_addr_filter
##              install_dst_net_filter
##              uninstall_dst_addr_filter
##              uninstall_dst_net_filter
##              Pcap::error
function uninstall_src_addr_filter%(ip: addr%) : bool
	%{
	return zeek::val_mgr->Bool(packet_mgr->GetPacketFilter()->RemoveSrc(ip->AsAddr()));
	%}

## Removes a source subnet filter.
##
## snet: The subnet for which a source filter was previously installed.
##
## Returns: True on success.
##
## .. zeek:see:: Pcap::precompile_pcap_filter
##              Pcap::install_pcap_filter
##              install_src_addr_filter
##              install_src_net_filter
##              uninstall_src_addr_filter
##              install_dst_addr_filter
##              install_dst_net_filter
##              uninstall_dst_addr_filter
##              uninstall_dst_net_filter
##              Pcap::error
function uninstall_src_net_filter%(snet: subnet%) : bool
	%{
	return zeek::val_mgr->Bool(packet_mgr->GetPacketFilter()->RemoveSrc(snet));
	%}

## Installs a filter to drop packets destined to a given IP address with
## a certain probability if none of a given set of TCP flags are set.
## Note that for IPv6 packets with a routing type header and non-zero
## segments left, this filters out against the final destination of the
## packet according to the routing extension header.
##
## ip: Drop packets to this IP address.
##
## tcp_flags: If none of these TCP flags are set, drop packets to *ip* with
##            probability *prob*.
##
## prob: The probability [0.0, 1.0] used to drop packets to *ip*.
##
## Returns: True (unconditionally).
##
## .. zeek:see:: Pcap::precompile_pcap_filter
##              Pcap::install_pcap_filter
##              install_src_addr_filter
##              install_src_net_filter
##              uninstall_src_addr_filter
##              uninstall_src_net_filter
##              install_dst_net_filter
##              uninstall_dst_addr_filter
##              uninstall_dst_net_filter
##              Pcap::error
##
## .. todo:: The return value should be changed to any.
function install_dst_addr_filter%(ip: addr, tcp_flags: count, prob: double%) : bool
	%{
	packet_mgr->GetPacketFilter()->AddDst(ip->AsAddr(), tcp_flags, prob);
	return zeek::val_mgr->True();
	%}

## Installs a filter to drop packets destined to a given subnet with
## a certain probability if none of a given set of TCP flags are set.
##
## snet: Drop packets to this subnet.
##
## tcp_flags: If none of these TCP flags are set, drop packets to *snet* with
##            probability *prob*.
##
## prob: The probability [0.0, 1.0] used to drop packets to *snet*.
##
## Returns: True (unconditionally).
##
## .. zeek:see:: Pcap::precompile_pcap_filter
##              Pcap::install_pcap_filter
##              install_src_addr_filter
##              install_src_net_filter
##              uninstall_src_addr_filter
##              uninstall_src_net_filter
##              install_dst_addr_filter
##              uninstall_dst_addr_filter
##              uninstall_dst_net_filter
##              Pcap::error
##
## .. todo:: The return value should be changed to any.
function install_dst_net_filter%(snet: subnet, tcp_flags: count, prob: double%) : bool
	%{
	packet_mgr->GetPacketFilter()->AddDst(snet, tcp_flags, prob);
	return zeek::val_mgr->True();
	%}

## Removes a destination address filter.
##
## ip: The IP address for which a destination filter was previously installed.
##
## Returns: True on success.
##
## .. zeek:see:: Pcap::precompile_pcap_filter
##              Pcap::install_pcap_filter
##              install_src_addr_filter
##              install_src_net_filter
##              uninstall_src_addr_filter
##              uninstall_src_net_filter
##              install_dst_addr_filter
##              install_dst_net_filter
##              uninstall_dst_net_filter
##              Pcap::error
function uninstall_dst_addr_filter%(ip: addr%) : bool
	%{
	return zeek::val_mgr->Bool(packet_mgr->GetPacketFilter()->RemoveDst(ip->AsAddr()));
	%}

## Removes a destination subnet filter.
##
## snet: The subnet for which a destination filter was previously installed.
##
## Returns: True on success.
##
## .. zeek:see:: Pcap::precompile_pcap_filter
##              Pcap::install_pcap_filter
##              install_src_addr_filter
##              install_src_net_filter
##              uninstall_src_addr_filter
##              uninstall_src_net_filter
##              install_dst_addr_filter
##              install_dst_net_filter
##              uninstall_dst_addr_filter
##              Pcap::error
function uninstall_dst_net_filter%(snet: subnet%) : bool
	%{
	return zeek::val_mgr->Bool(packet_mgr->GetPacketFilter()->RemoveDst(snet));
	%}

## Checks whether the last raised event came from a remote peer.
##
## Returns: True if the last raised event came from a remote peer.
function is_remote_event%(%) : bool
	%{
	return zeek::val_mgr->Bool(zeek::event_mgr.CurrentSource() != zeek::util::detail::SOURCE_LOCAL);
	%}

## Stops Zeek's packet processing. This function is used to synchronize
## distributed trace processing with communication enabled
## (*pseudo-realtime* mode).
##
## .. zeek:see:: continue_processing
function suspend_processing%(%) : any
	%{
	zeek::run_state::suspend_processing();
	return nullptr;
	%}

## Resumes Zeek's packet processing.
##
## .. zeek:see:: suspend_processing
function continue_processing%(%) : any
	%{
	zeek::run_state::continue_processing();
	return nullptr;
	%}

# ===========================================================================
#
#                             Internal Functions
#
# ===========================================================================

## Manually triggers the signature engine for a given connection.
## This is an internal function.
function match_signatures%(c: connection, pattern_type: int, s: string,
				bol: bool, eol: bool,
				from_orig: bool, clear: bool%) : bool
	%{
	if ( ! zeek::detail::rule_matcher )
		return zeek::val_mgr->False();

	c->Match((zeek::detail::Rule::PatternType) pattern_type, s->Bytes(), s->Len(),
	         from_orig, bol, eol, clear);

	return zeek::val_mgr->True();
	%}

## By default, zeek does not generate (raise) events that have not handled by
## any scripts. This means that these events will be invisible to a lot of other
## event handlers - and will not raise :zeek:id:`new_event`.
##
## Calling this function will cause all event handlers to be raised. This is, likely,
## only useful for debugging and causes reduced performance.
function generate_all_events%(%) : bool
	%{
	auto event_names = event_registry->AllHandlers();
	for ( const auto& name: event_names )
		{
		auto event = event_registry->Lookup(name);
		if ( event == nullptr )
			continue;

		event->SetGenerateAlways();
		}

	return zeek::val_mgr->True();
	%}

%%{
// Autogenerated from CMake bif_target()
#include "__all__.bif.cc"
#include "__all__.bif.register.cc"

static void init_secondary_bifs()
	{
	#include "__all__.bif.init.cc"
	}
%%}

## An internal function that helps initialize BIFs.
function __init_secondary_bifs%(%): bool
	%{
	init_secondary_bifs();
	return zeek::val_mgr->True();
	%}

# ===========================================================================
#
#                            Anonymization Functions
#                            (Not Fully Functional)
#
# ===========================================================================



%%{
#include "zeek/Anon.h"
%%}

## Preserves the prefix of an IP address in anonymization.
##
## a: The address to preserve.
##
## width: The number of bits from the top that should remain intact.
##
## .. zeek:see:: preserve_subnet anonymize_addr
##
## .. todo:: Currently dysfunctional.
function preserve_prefix%(a: addr, width: count%): any
	%{
	zeek::detail::AnonymizeIPAddr* ip_anon = zeek::detail::ip_anonymizer[zeek::detail::PREFIX_PRESERVING_A50];
	if ( ip_anon )
		{
		if ( a->AsAddr().GetFamily() == IPv6 )
			zeek::emit_builtin_error("preserve_prefix() not supported for IPv6 addresses");
		else
			{
			const uint32_t* bytes;
			a->AsAddr().GetBytes(&bytes);
			ip_anon->PreservePrefix(*bytes, width);
			}
		}

	return nullptr;
	%}

## Preserves the prefix of a subnet in anonymization.
##
## a: The subnet to preserve.
##
## .. zeek:see:: preserve_prefix anonymize_addr
##
## .. todo:: Currently dysfunctional.
function preserve_subnet%(a: subnet%): any
	%{
	DEBUG_MSG("%s/%d\n", a->Prefix().AsString().c_str(), a->Width());
	zeek::detail::AnonymizeIPAddr* ip_anon = zeek::detail::ip_anonymizer[zeek::detail::PREFIX_PRESERVING_A50];
	if ( ip_anon )
		{
		if ( a->AsSubNet().Prefix().GetFamily() == IPv6 )
			zeek::emit_builtin_error("preserve_subnet() not supported for IPv6 addresses");
		else
			{
			const uint32_t* bytes;
			a->AsSubNet().Prefix().GetBytes(&bytes);
			ip_anon->PreservePrefix(*bytes, a->AsSubNet().Length());
			}
		}

	return nullptr;
	%}

## Anonymizes an IP address.
##
## a: The address to anonymize.
##
## cl: The anonymization class, which can take on three different values:
##
##     - ``ORIG_ADDR``: Tag *a* as an originator address.
##
##     - ``RESP_ADDR``: Tag *a* as an responder address.
##
##     - ``OTHER_ADDR``: Tag *a* as an arbitrary address.
##
## Returns: An anonymized version of *a*.
##
## .. zeek:see:: preserve_prefix preserve_subnet
##
## .. todo:: Currently dysfunctional.
function anonymize_addr%(a: addr, cl: IPAddrAnonymizationClass%): addr
	%{
	int anon_class = cl->InternalInt();
	if ( anon_class < 0 || anon_class >= zeek::detail::NUM_ADDR_ANONYMIZATION_CLASSES )
		zeek::emit_builtin_error("anonymize_addr(): invalid ip addr anonymization class");

	if ( a->AsAddr().GetFamily() == IPv6 )
		{
		zeek::emit_builtin_error("anonymize_addr() not supported for IPv6 addresses");
		return nullptr;
		}
	else
		{
		const uint32_t* bytes;
		a->AsAddr().GetBytes(&bytes);
		return zeek::make_intrusive<zeek::AddrVal>(zeek::detail::anonymize_ip(*bytes,
		        static_cast<zeek::detail::ip_addr_anonymization_class_t>(anon_class)));
		}
	%}

## A function to convert arbitrary Zeek data into a JSON string.
##
## v: The value to convert to JSON.  Typically a record.
##
## only_loggable: If the v value is a record this will only cause
##                fields with the &log attribute to be included in the JSON.
##
## returns: a JSON formatted string.
##
## .. zeek:see:: fmt cat cat_sep string_cat print_raw
function to_json%(val: any, only_loggable: bool &default=F, field_escape_pattern: pattern &default=/^_/%): string
	%{
	return val->ToJSON(only_loggable, field_escape_pattern);
	%}

## Compresses a given path by removing '..'s and the parent directory it
## references and also removing dual '/'s and extraneous '/./'s.
##
## dir: a path string, either relative or absolute.
##
## Returns: a compressed version of the input path.
function compress_path%(dir: string%): string
	%{
	return zeek::make_intrusive<zeek::StringVal>(zeek::util::detail::normalize_path(dir->ToStdString()));
	%}
