zeek/src/zeek.bif

5295 lines
147 KiB
C++

##! 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
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
function network_time%(%): time
%{
return zeek::make_intrusive<zeek::TimeVal>(zeek::run_state::network_time);
%}
## 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());
%}
## 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 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 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.
##
## .. 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
## 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
## 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
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` 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();
}
%}
%%{
typedef struct {
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];
} bro_uuid_t;
%%}
## 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
%{
static auto pcap_packet = zeek::id::find_type<zeek::RecordType>("pcap_packet");
const Packet* p;
auto pkt = zeek::make_intrusive<zeek::RecordVal>(pcap_packet);
zeek::iosource::PktSrc* pkt_src = zeek::run_state::detail::current_packet_source();
if ( ! pkt_src || ! pkt_src->GetCurrentPacket(&p) )
{
pkt->Assign(0, 0);
pkt->Assign(1, 0);
pkt->Assign(2, 0);
pkt->Assign(3, 0);
pkt->Assign(4, zeek::val_mgr->EmptyString());
pkt->Assign(5, zeek::BifType::Enum::link_encap->GetEnumVal(BifEnum::LINK_UNKNOWN));
return pkt;
}
pkt->Assign(0, static_cast<uint32_t>(p->ts.tv_sec));
pkt->Assign(1, static_cast<uint32_t>(p->ts.tv_usec));
pkt->Assign(2, p->cap_len);
pkt->Assign(3, p->len);
pkt->Assign(4, zeek::make_intrusive<zeek::StringVal>(p->cap_len, (const char*)p->data));
pkt->Assign(5, zeek::BifType::Enum::link_encap->GetEnumVal(p->link_type));
return pkt;
%}
## 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 zeek::detail::CallExpr* arg_call,
bool arg_lookup_name)
{
Ref(arg_trigger);
trigger = arg_trigger;
call = arg_call;
lookup_name = arg_lookup_name;
}
~LookupHostCallback()
{
Unref(trigger);
}
// Overridden from zeek::detail::DNS_Mgr:Lookup:Callback.
virtual void Resolved(const char* name)
{
zeek::Val* result = new zeek::StringVal(name);
trigger->Cache(call, result);
Unref(result);
trigger->Release();
}
virtual void Resolved(zeek::TableVal* addrs)
{
// No Ref() for addrs.
trigger->Cache(call, addrs);
trigger->Release();
}
virtual void Timeout()
{
if ( lookup_name )
{
zeek::Val* result = new zeek::StringVal("<\?\?\?>");
trigger->Cache(call, 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(call, result.get());
Unref(lv);
}
trigger->Release();
}
private:
zeek::detail::trigger::Trigger* trigger;
const zeek::detail::CallExpr* call;
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->AsyncLookupAddr(host->AsAddr(),
new LookupHostCallback(trigger, frame->GetCall(), 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->AsyncLookupNameText(host->CheckString(),
new LookupHostCallback(trigger, frame->GetCall(), 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->AsyncLookupName(host->CheckString(),
new LookupHostCallback(trigger, frame->GetCall(), 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()));
%}