zeek/src/zeek.bif

5616 lines
162 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 <sys/stat.h>
#include <algorithm>
#include <cinttypes>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <vector>
#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/CompHash.h"
#include "zeek/packet_analysis/Manager.h"
using namespace std;
zeek::TableType* var_sizes;
static zeek::iosource::PktDumper* addl_pkt_dumper = nullptr;
zeek_int_t parse_int(const char*& fmt)
{
zeek_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 bool check_fmt_type(zeek::TypeTag t, zeek::TypeTag ok[])
{
for ( int i = 0; ok[i] != zeek::TYPE_ERROR; ++i )
if ( ok[i] == t )
return true;
return false;
}
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 )
{
zeek_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 bool 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 false;
fmt = fp + 1;
if ( *fmt == '%' )
{
// "%%" -> '%'
d->Add("%");
++fmt;
return next_fmt(fmt, args, d, n);
}
if ( ++n >= static_cast<int>(args->size()) )
return false;
do_fmt(fmt, (*args)[n].get(), d);
return *fmt != '\0';
}
zeek::RecordValPtr zeek::detail::build_dummy_conn_record()
{
// 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;
}
%%}
# ===========================================================================
#
# Core
#
# ===========================================================================
## Returns the current wall-clock time.
##
## In general, you should use :zeek:id:`network_time` instead
## unless you are using Zeek for non-networking uses (such as general
## scripting; not particularly recommended), because otherwise your script
## may behave very differently on live traffic versus played-back traffic
## from a save file.
##
## Returns: The wall-clock time.
##
## .. zeek:see:: network_time set_network_time
function current_time%(%): time
%{
return zeek::make_intrusive<zeek::TimeVal>(zeek::util::current_time());
%}
## Returns the timestamp of the last packet processed. This function returns
## the timestamp of the most recently read packet, whether read from a
## live network interface or from a save file.
##
## Returns: The timestamp of the packet processed.
##
## .. zeek:see:: current_time set_network_time
function network_time%(%): time
%{
return zeek::make_intrusive<zeek::TimeVal>(zeek::run_state::network_time);
%}
## Sets the timestamp associated with the last packet processed. Used for
## event replaying.
##
## nt: The time to which to set "network time".
##
## Returns: The timestamp of the packet processed.
##
## .. zeek:see:: current_time network_time
function set_network_time%(nt: time%): bool
%{
zeek::run_state::network_time = nt;
return zeek::val_mgr->True();
%}
## Returns the timestamp of the last raised event. The timestamp reflects the
## network time the event was intended to be executed. For scheduled events,
## this is the time the event was scheduled for. For any other event, this is
## the time when the event was created.
##
## Returns: The timestamp of the last raised event.
##
## .. zeek:see:: current_time set_network_time
function current_event_time%(%): time
%{
return zeek::make_intrusive<zeek::TimeVal>(zeek::event_mgr.CurrentEventTime());
%}
## Register the expected Zeek type for event metadata.
##
## id: The event metadata identifier.
##
## t: A type expression or type alias. The type cannot be ``any``, ``func``,
## ``file``, ``opaque`` or a composite type containing one of these types.
##
## Returns: true if the registration was successful, false if *id* is
## registered with a different type already, or type is invalid.
##
## .. zeek:see:: EventMetadata::current EventMetadata::current_all
function EventMetadata::register%(id: EventMetadata::ID, t: any%): bool
%{
const auto& tty = t->GetType();
if ( tty->Tag() != zeek::TYPE_TYPE )
{
zeek::emit_builtin_error("register_event_metadata() expects type");
return zeek::val_mgr->False();
}
EnumValPtr idvp = {zeek::NewRef{}, id->AsEnumVal()};
bool r = zeek::event_registry->RegisterMetadata(idvp, tty->AsTypeType()->GetType());
return zeek::val_mgr->Bool(r);
%}
## Query the current event's metadata with identifier *id*.
##
## id: The metadata identifier, e.g. ``EventMetadata::NETWORK_TIMESTAMP``.
##
## Returns: A vector of values. The vector is empty if no metadata with
## the given identifier is attached to this event, otherwise a
## vector whose elements are of the type used during registration.
##
## .. zeek:see:: EventMetadata::register EventMetadata::current_all
function EventMetadata::current%(id: EventMetadata::ID%): any_vec
%{
static const auto& vt = zeek::id::find_type<zeek::VectorType>("any_vec");
const auto* event = zeek::event_mgr.CurrentEvent();
if ( ! event )
return zeek::make_intrusive<zeek::VectorVal>(vt);
return event->MetadataValues({zeek::NewRef{}, id->AsEnumVal()});
%}
## Query all of the current event's metadata.
##
## Returns: A vector :zeek:see:`EventMetadata::Entry` elements holding all
## the metadata attached to this event.
##
## .. zeek:see:: EventMetadata::register EventMetadata::current
function EventMetadata::current_all%(%): event_metadata_vec
%{
static const auto& vt = zeek::id::find_type<zeek::VectorType>("event_metadata_vec");
auto result = zeek::make_intrusive<zeek::VectorVal>(vt);
const auto* event = zeek::event_mgr.CurrentEvent();
if ( ! event )
return result;
if ( const auto* mdv = event->Metadata() ) {
for ( const auto& entry : *mdv )
result->Append(entry.BuildVal());
}
return result;
%}
## 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;
}
auto [data,len] = key->AsString()->CheckStringWithSize();
char* tmp = zeek::util::copy_string(data, len);
zeek::util::to_upper(tmp);
std::string var1 = zeek::util::fmt("ZEEK_ARG_%s", tmp);
delete [] tmp;
if ( set )
setenv(var1.data(), val->AsString()->CheckString(), 1);
else
unsetenv(var1.data());
}
return true;
}
static int do_system(const char* s)
{
const char* system_fmt = "(%s) 1>&2 &"; // output to stderr
auto cmd_len = strlen(system_fmt) + strlen(s) + 1;
char* cmd = new char[cmd_len];
snprintf(cmd, cmd_len, 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();
%}
## Sleeps for the given amount of time.
##
## i: The time interval to sleep for.
##
## Returns: The :zeek:type:`interval` Zeek actually slept for.
##
## .. note::
##
## This is a blocking sleep! Zeek will not run most of its processing
## during that time. You almost certainly DO NOT WANT THIS outside
## of specific testing/troubleshooting scenarios. To sleep asynchronously,
## :zeek:see:`schedule` an event, or consider :zeek:id:`Exec::run`.
function sleep%(i: interval%): interval
%{
const auto start = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(std::chrono::duration<double>(i));
const auto end = std::chrono::high_resolution_clock::now();
const auto slept = std::chrono::duration<double>(end - start).count();
return zeek::make_intrusive<zeek::IntervalVal>(slept);
%}
%%{
#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[ZEEK_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[ZEEK_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[ZEEK_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[ZEEK_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 std::move(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 std::move(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 std::move(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
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_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_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))
);
%}
%%{
template<typename IntType>
IntType fnva(zeek::Val* input, IntType offset, IntType prime) {
zeek::ODesc desc(zeek::DESC_BINARY);
auto length = 0;
const u_char* bytes = NULL;
if (input->GetType()->Tag() == zeek::TYPE_STRING)
{
length = ((zeek::StringVal*) input) -> Len();
bytes = ((zeek::StringVal*) input)-> Bytes();
}
else
{
input->Describe(&desc);
bytes = desc.Bytes();
length = desc.Len();
}
IntType rval = offset;
for ( auto i = 0; i < length; ++i )
{
rval ^= bytes[i];
rval *= prime;
}
return rval;
}
%%}
## 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
%{
uint32_t offset = 2166136261;
uint32_t prime = 16777619;
auto hash = fnva<uint32_t>(input, offset, prime);
return zeek::val_mgr->Count(hash);
%}
## Returns 64-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.
function fnv1a64%(input: any%): count
%{
uint64_t offset = 0xCBF29CE484222325;
uint64_t prime = 0x100000001B3;
uint64_t hash = fnva(input, offset, prime);
return zeek::val_mgr->Count(hash);
%}
## 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 = zeek_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 std::move(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 std::move(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 values from a table.
##
## t: The :zeek:type:`table`
##
## Returns: A ``vector of T`` of all the values in t.
##
## .. zeek:see:: table_keys
function table_values%(t: any%): any_vec
%{
if ( ! t->GetType()->IsTable() )
{
zeek::emit_builtin_error("table_values() requires a table argument");
return nullptr;
}
auto vt = zeek::make_intrusive<zeek::VectorType>(t->GetType()->AsTableType()->Yield());
auto vv = zeek::make_intrusive<zeek::VectorVal>(std::move(vt));
for ( const auto& iter : *t->AsTable() )
{
vv->Append(iter.value->GetVal());
}
return std::move(vv);
%}
## Gets all keys from a table.
##
## t: The :zeek:type:`table`
##
## Returns: A ``set of T`` of all the keys in t.
##
## .. zeek:see:: table_values
function table_keys%(t: any%): any
%{
if ( ! t->GetType()->IsTable() )
{
zeek::emit_builtin_error("table_keys() requires a table argument");
return nullptr;
}
auto tl = t->GetType()->AsTableType()->GetIndices();
auto st = zeek::make_intrusive<zeek::SetType>(std::move(tl), nullptr);
auto sv = zeek::make_intrusive<zeek::TableVal>(std::move(st));
auto th = t->AsTableVal()->GetTableHash();
for ( const auto& iter : *t->AsTable() )
{
sv->Assign(th->RecoverVals(*iter.GetHashKey()), nullptr);
}
return std::move(sv);
%}
## 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);
%}
## 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 concatenation 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(true, 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 the 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 = 2u; i < @ARGC@; ++i )
{
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(true, 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(true, 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:: ceil sqrt exp ln log2 log10 pow
function floor%(d: double%): double
%{
return zeek::make_intrusive<zeek::DoubleVal>(floor(d));
%}
## Computes the smallest integer greater or equal than the given :zeek:type:`double` value.
## For example, ``ceil(3.14)`` returns ``4.0``, and ``ceil(-3.14)``
## returns ``-3.0``.
##
## d: The :zeek:type:`double` to manipulate.
##
## Returns: The next lowest integer of *d* as :zeek:type:`double`.
##
## .. zeek:see:: floor sqrt exp ln log2 log10 pow
function ceil%(d: double%): double
%{
return zeek::make_intrusive<zeek::DoubleVal>(ceil(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 ceil exp ln log2 log10 pow
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 ceil sqrt ln log2 log10 pow
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:: floor ceil sqrt exp log2 log10 pow
function ln%(d: double%): double
%{
return zeek::make_intrusive<zeek::DoubleVal>(log(d));
%}
## Computes the base 2 logarithm of a number.
##
## d: The argument to the logarithm.
##
## Returns: The base 2 logarithm of *d*.
##
## .. zeek:see:: floor ceil sqrt exp ln log10 pow
function log2%(d: double%): double
%{
return zeek::make_intrusive<zeek::DoubleVal>(log2(d));
%}
## Computes the common logarithm of a number.
##
## d: The argument to the logarithm.
##
## Returns: The common logarithm of *d*.
##
## .. zeek:see:: floor ceil sqrt exp ln log2 pow
function log10%(d: double%): double
%{
return zeek::make_intrusive<zeek::DoubleVal>(log10(d));
%}
## Computes the *x* raised to the power *y*.
##
## x: The number to be raised to a power.
##
## y: The number that specifies a power.
##
## Returns: The number *x* raised to the power *y*.
##
## .. zeek:see:: floor ceil sqrt exp ln log2 log10
function pow%(x: double, y: double%): double
%{
return zeek::make_intrusive<zeek::DoubleVal>(pow(x, y));
%}
# ===========================================================================
#
# Introspection
#
# ===========================================================================
## Returns the ID of the analyzer which raised the current event.
##
## Returns: The ID of the analyzer which raised the current event, or 0 if
## none.
function current_analyzer%(%) : count
%{
return zeek::val_mgr->Count(zeek::event_mgr.CurrentAnalyzer());
%}
## Returns Zeek's process ID.
##
## Returns: Zeek's process ID.
function getpid%(%) : count
%{
return zeek::val_mgr->Count(getpid());
%}
%%{
namespace zeek {
extern const char* zeek_version();
} // namespace zeek
%%}
## Returns the Zeek version string.
##
## Returns: Zeek's version, e.g., 2.0-beta-47-debug.
function zeek_version%(%): string
%{
return zeek::make_intrusive<zeek::StringVal>(zeek::zeek_version());
%}
%%{
namespace zeek::run_state::detail {
extern bool bare_mode;
} // namespace zeek::run_state::detail
%%}
## Returns whether Zeek was started in bare mode.
##
## Returns: True if Zeek was started in bare mode, false otherwise.
function bare_mode%(%): bool
%{
return zeek::val_mgr->Bool(zeek::run_state::detail::bare_mode);
%}
## Converts a record type name to a vector of strings, where each element is
## the name of a record field. Nested records are flattened.
##
## rt: The name of the record type.
##
## Returns: A string vector with the field names of *rt*.
function record_type_to_vector%(rt: string%): string_vec
%{
auto result = zeek::make_intrusive<zeek::VectorVal>(zeek::id::string_vec);
zeek::RecordType* type = zeek::id::find_type(rt->CheckString())->AsRecordType();
for ( int i = 0; i < type->NumFields(); ++i )
result->Assign(i+1, zeek::make_intrusive<zeek::StringVal>(type->FieldName(i)));
return std::move(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(true, 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 std::move(rval);
%}
## Returns all value names associated with an enum type.
##
## et: An enum type or a string naming one.
##
## Returns: All enum value names associated with enum type *et*.
## If *et* is not an enum type or does not name one, an empty set is returned.
function enum_names%(et: any%): string_set
%{
auto rval = make_intrusive<zeek::TableVal>(zeek::id::string_set);
zeek::TypePtr t = zeek::Type::nil;
if ( et->GetType()->Tag() == TYPE_STRING )
{
const auto& id = zeek::detail::global_scope()->Find(et->AsStringVal()->ToStdStringView());
if ( id && id->IsType() )
t = id->GetType();
}
else if ( et->GetType()->Tag() == TYPE_TYPE )
t = et->GetType()->AsTypeType()->GetType();
if ( ! t || 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 std::move(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 std::move(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 std::move(r);
%}
## Generates a table of the "footprint" of all global container variables.
## This is (approximately) the number of objects the global contains either
## directly or indirectly. The number is not meant to be precise, but
## rather comparable: larger footprint correlates with more memory consumption.
## The table index is the variable name and the value is the footprint.
##
## Returns: A table that maps variable names to their footprints.
##
## .. zeek:see:: val_footprint
function global_container_footprints%(%): var_sizes
%{
auto sizes = zeek::make_intrusive<zeek::TableVal>(IntrusivePtr{zeek::NewRef{}, var_sizes});
const auto& globals = zeek::detail::global_scope()->Vars();
for ( const auto& global : globals )
{
auto& id = global.second;
auto v = id->GetVal();
if ( ! v || ! IsAggr(v->GetType()) )
continue;
auto id_name = zeek::make_intrusive<zeek::StringVal>(id->Name());
auto fp = zeek::val_mgr->Count(v->Footprint());
sizes->Assign(std::move(id_name), std::move(fp));
}
return std::move(sizes);
%}
## Computes a value's "footprint": the number of objects the value contains
## either directly or indirectly. The number is not meant to be precise, but
## rather comparable: larger footprint correlates with more memory consumption.
##
## Returns: the footprint.
##
## .. zeek:see:: global_container_footprints
function val_footprint%(v: any%): count
%{
return zeek::val_mgr->Count(v->Footprint());
%}
## Generates a table with information about all global identifiers. The table
## value is a record containing the type name of the identifier, whether it is
## exported, a constant, an enum constant, redefinable, and its value (if it
## has one).
##
## Module names are included in the returned table as well. The ``type_name``
## field is set to "module" and their names are prefixed with "module " to avoid
## clashing with global identifiers. Note that there is no module type in Zeek.
##
## Returns: A table that maps identifier names to information about them.
function global_ids%(%): id_table
%{
static auto id_table = zeek::id::find_type<zeek::TableType>("id_table");
static auto script_id = zeek::id::find_type<zeek::RecordType>("script_id");
zeek::ODesc d;
auto ids = zeek::make_intrusive<zeek::TableVal>(id_table);
for ( const auto& [_, id] : zeek::detail::global_scope()->Vars() )
{
d.Clear();
id->GetType()->Describe(&d);
auto rec = zeek::make_intrusive<zeek::RecordVal>(script_id);
rec->Assign(0, d.Description());
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));
}
auto module_str = zeek::make_intrusive<zeek::StringVal>("module");
for ( const auto& module_name : zeek::detail::module_names() )
{
auto rec = zeek::make_intrusive<zeek::RecordVal>(script_id);
rec->Assign(0, module_str); // type_name
rec->Assign(1, false); // exported
rec->Assign(2, false); // constant
rec->Assign(3, false); // enum_constant
rec->Assign(4, false); // option_value
rec->Assign(5, false); // redefinable
rec->Assign(6, false); // broker_backend
auto id_name = zeek::make_intrusive<zeek::StringVal>(zeek::util::fmt("module %s", module_name.c_str()));
ids->Assign(std::move(id_name), std::move(rec));
}
return std::move(ids);
%}
## Returns a set giving the names of all global options.
function global_options%(%): string_set
%{
auto options = make_intrusive<zeek::TableVal>(zeek::id::string_set);
const auto& globals = zeek::detail::global_scope()->Vars();
for ( const auto& global : globals )
{
const auto& id = global.second;
if ( ! id->IsOption() )
continue;
auto id_name = zeek::make_intrusive<zeek::StringVal>(id->Name());
options->Assign(std::move(id_name), nullptr);
}
return std::move(options);
%}
## Returns the value of a global identifier.
##
## id: The global identifier.
##
## Returns: The value of *id*. If *id* does not describe a valid identifier,
## the string ``"<unknown id>"`` or ``"<no ID value>"`` is returned.
function lookup_ID%(id: string%) : any
%{
const auto& i = zeek::detail::global_scope()->Find(id->ToStdStringView());
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()->ToStdStringView());
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();
#ifndef _MSC_VER
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();
}
#endif
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();
%}
%%{
#include "zeek/Func.h"
%%}
## 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
%{
return zeek::detail::get_current_script_backtrace();
%}
# ===========================================================================
#
# 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 std::move(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 std::move(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`. For values where
## ``base`` is set to 16, a prefix of ``0x`` or ``0X`` will be ignored.
##
## str: The :zeek:type:`string` to convert.
##
## base: The :zeek:type:`count` to use as the numeric base.
##
## Returns: The :zeek:type:`string` *str* as :zeek:type:`int`.
##
## .. zeek:see:: to_addr to_port to_subnet
function to_int%(str: string, base: count &default=10%): int
%{
const char* s = str->CheckString();
char* end_s;
zeek_int_t i = strtoll(s, &end_s, base);
if ( s[0] == '\0' || std::any_of(static_cast<const char*>(end_s), s + ::strlen(s),
[](char c) { return ! (c == '\0' || ::isspace(c)); }) )
{
zeek::emit_builtin_error("bad conversion to integer", @ARG@[0]);
}
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:`int`.
##
## d: The :zeek:type:`double` to convert.
##
## Returns: The :zeek:type:`double` *d* as signed integer. The value returned
## follows typical rounding rules, as implemented by rint().
##
## .. zeek:see:: double_to_time
function double_to_int%(d: double%): int
%{
return zeek::val_mgr->Int(zeek_int_t(rint(d)));
%}
## Converts a :zeek:type:`double` to a :zeek:type:`count`.
##
## d: The :zeek:type:`double` to convert.
##
## Returns: The :zeek:type:`double` *d* as unsigned integer, or 0 if *d* < 0.0.
## The value returned follows typical rounding rules, as implemented
## by rint().
function double_to_count%(d: double%): count
%{
if ( d < 0.0 )
zeek::emit_builtin_error("bad conversion to count", @ARG@[0]);
return zeek::val_mgr->Count(zeek_uint_t(rint(d)));
%}
## Converts a :zeek:type:`string` to a :zeek:type:`count`. For values where
## ``base`` is set to 16, a prefix of ``0x`` or ``0X`` will be ignored.
##
## str: The :zeek:type:`string` to convert.
##
## base: The :zeek:type:`count` to use as the numeric base.
##
## 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, base: count &default=10%): count
%{
const char* s = str->CheckString();
char* end_s;
uint64_t u = static_cast<uint64_t>(strtoull(s, &end_s, base));
if ( s[0] == '\0' || std::any_of(static_cast<const char*>(end_s), s + ::strlen(s),
[](char c) { return ! (c == '\0' || ::isspace(c)); }) )
{
zeek::emit_builtin_error("bad conversion to count", @ARG@[0]);
u = 0;
}
return zeek::val_mgr->Count(u);
%}
## Converts an :zeek:type:`interval` to a :zeek:type:`double`.
##
## i: The :zeek:type:`interval` to convert.
##
## Returns: The :zeek:type:`interval` *i* as :zeek:type:`double`.
##
## .. zeek:see:: double_to_interval
function interval_to_double%(i: interval%): double
%{
return zeek::make_intrusive<zeek::DoubleVal>(i);
%}
## Converts a :zeek:type:`count` to a :zeek:type:`double`.
##
## c: The :zeek:type:`count` to convert.
##
## Returns: The :zeek:type:`count` *c* as :zeek:type:`double`.
##
## .. zeek:see:: int_to_double double_to_count
function count_to_double%(c: count%): double
%{
return zeek::make_intrusive<zeek::DoubleVal>(c);
%}
## Converts an :zeek:type:`int` to a :zeek:type:`double`.
##
## i: The :zeek:type:`int` to convert.
##
## Returns: The :zeek:type:`int` *i* as :zeek:type:`double`.
##
## .. zeek:see:: count_to_double double_to_count
function int_to_double%(i: int%): double
%{
return zeek::make_intrusive<zeek::DoubleVal>(i);
%}
## Converts a :zeek:type:`time` value to a :zeek:type:`double`.
##
## t: The :zeek:type:`time` to convert.
##
## Returns: The :zeek:type:`time` value *t* as :zeek:type:`double`.
##
## .. zeek:see:: double_to_time
function time_to_double%(t: time%): double
%{
return zeek::make_intrusive<zeek::DoubleVal>(t);
%}
## Converts a :zeek:type:`double` value to a :zeek:type:`time`.
##
## d: The :zeek:type:`double` to convert.
##
## Returns: The :zeek:type:`double` value *d* as :zeek:type:`time`.
##
## .. zeek:see:: time_to_double double_to_count
function double_to_time%(d: double%): time
%{
return zeek::make_intrusive<zeek::TimeVal>(d);
%}
## Converts a :zeek:type:`double` to an :zeek:type:`interval`.
##
## d: The :zeek:type:`double` to convert.
##
## Returns: The :zeek:type:`double` *d* as :zeek:type:`interval`.
##
## .. zeek:see:: interval_to_double
function double_to_interval%(d: double%): interval
%{
return zeek::make_intrusive<zeek::IntervalVal>(d);
%}
## Converts a :zeek:type:`port` to a :zeek:type:`count`.
##
## p: The :zeek:type:`port` to convert.
##
## Returns: The :zeek:type:`port` *p* as :zeek:type:`count`.
##
## .. zeek:see:: count_to_port
function port_to_count%(p: port%): count
%{
return zeek::val_mgr->Count(p->Port());
%}
## Converts a :zeek:type:`count` and ``transport_proto`` to a :zeek:type:`port`.
##
## num: The :zeek:type:`port` number.
##
## proto: The transport protocol.
##
## Returns: The :zeek:type:`count` *num* as :zeek:type:`port`.
##
## .. zeek:see:: port_to_count
function count_to_port%(num: count, proto: transport_proto%): port
%{
return zeek::val_mgr->Port(num, (TransportProto)proto->AsEnum());
%}
## Converts a :zeek:type:`string` to an :zeek:type:`addr`.
##
## ip: The :zeek:type:`string` to convert.
##
## Returns: The :zeek:type:`string` *ip* as :zeek:type:`addr`, or the unspecified
## address ``::`` if the input string does not parse correctly.
##
## .. zeek:see:: to_count to_int to_port count_to_v4_addr raw_bytes_to_v4_addr raw_bytes_to_v6_addr
## to_subnet
function to_addr%(ip: string%): addr
%{
char* s = ip->AsString()->Render();
zeek::ValPtr ret;
in6_addr tmp;
if ( zeek::IPAddr::ConvertString(s, &tmp) )
ret = zeek::make_intrusive<zeek::AddrVal>(zeek::IPAddr(tmp));
else
{
ret = zeek::make_intrusive<zeek::AddrVal>(zeek::IPAddr());
zeek::emit_builtin_error("failed converting string to IP address", ip);
}
delete [] s;
return ret;
%}
## Checks if a string is a valid IPv4 or IPv6 address.
##
## ip: the string to check for valid IP formatting.
##
## Returns: T if the string is a valid IPv4 or IPv6 address format.
function is_valid_ip%(ip: string%): bool
%{
return zeek::val_mgr->Bool(zeek::IPAddr::IsValid(ip->ToStdString().c_str()));
%}
## Checks if a string is a valid IPv4 or IPv6 subnet.
##
## cidr: the string to check for valid subnet formatting.
##
## Returns: T if the string is a valid IPv4 or IPv6 subnet format.
function is_valid_subnet%(cidr: string%): bool
%{
return zeek::val_mgr->Bool(zeek::IPPrefix::IsValid(cidr->ToStdString().c_str()));
%}
## Converts a :zeek:type:`string` to a :zeek:type:`subnet`.
##
## sn: The subnet to convert.
##
## Returns: The *sn* string as a :zeek:type:`subnet`, or the unspecified subnet
## ``::/0`` if the input string does not parse correctly.
##
## .. zeek:see:: to_count to_int to_port count_to_v4_addr raw_bytes_to_v4_addr raw_bytes_to_v6_addr
## to_addr
function to_subnet%(sn: string%): subnet
%{
char* s = sn->AsString()->Render();
IPPrefix tmp;
if ( ! IPPrefix::ConvertString(s, &tmp) )
zeek::emit_builtin_error("failed converting string to IP prefix", sn);
auto ret = zeek::make_intrusive<zeek::SubNetVal>(tmp);
delete [] s;
return std::move(ret);
%}
## Converts a :zeek:type:`addr` to a :zeek:type:`subnet`.
##
## a: The address to convert.
##
## Returns: The address as a :zeek:type:`subnet`.
##
## .. zeek:see:: to_subnet
function addr_to_subnet%(a: addr%): subnet
%{
int width = (a->AsAddr().GetFamily() == IPv4 ? 32 : 128);
return zeek::make_intrusive<zeek::SubNetVal>(a->AsAddr(), width);
%}
## Converts a :zeek:type:`subnet` to an :zeek:type:`addr` by
## extracting the prefix.
##
## sn: The subnet to convert.
##
## Returns: The subnet as an :zeek:type:`addr`.
##
## .. zeek:see:: to_subnet
function subnet_to_addr%(sn: subnet%): addr
%{
return zeek::make_intrusive<zeek::AddrVal>(sn->Prefix());
%}
## Returns the width of a :zeek:type:`subnet`.
##
## sn: The subnet.
##
## Returns: The width of the subnet.
##
## .. zeek:see:: to_subnet
function subnet_width%(sn: subnet%): count
%{
return zeek::val_mgr->Count(sn->Width());
%}
## Converts a :zeek:type:`string` to a :zeek:type:`double`.
##
## str: The :zeek:type:`string` to convert.
##
## Returns: The :zeek:type:`string` *str* as double, or 0 if *str* has
## an invalid format.
##
function to_double%(str: string%): double
%{
const char* s = str->CheckString();
char* end_s;
double d = strtod(s, &end_s);
if ( s[0] == '\0' || end_s[0] != '\0' )
{
zeek::emit_builtin_error("bad conversion to double", @ARG@[0]);
d = 0;
}
return zeek::make_intrusive<zeek::DoubleVal>(d);
%}
## Converts a :zeek:type:`count` to an :zeek:type:`addr`.
##
## ip: The :zeek:type:`count` to convert.
##
## Returns: The :zeek:type:`count` *ip* as :zeek:type:`addr`.
##
## .. zeek:see:: raw_bytes_to_v4_addr to_addr to_subnet raw_bytes_to_v6_addr
function count_to_v4_addr%(ip: count%): addr
%{
if ( ip > 4294967295LU )
{
zeek::emit_builtin_error("conversion of non-IPv4 count to addr", @ARG@[0]);
return zeek::make_intrusive<zeek::AddrVal>(uint32_t(0));
}
return zeek::make_intrusive<zeek::AddrVal>(htonl(uint32_t(ip)));
%}
## Converts a :zeek:type:`string` of bytes into an IPv4 address. In particular,
## this function interprets the first 4 bytes of the string as an IPv4 address
## in network order.
##
## b: The raw bytes (:zeek:type:`string`) to convert.
##
## Returns: The byte :zeek:type:`string` *b* as :zeek:type:`addr`.
##
## .. zeek:see:: raw_bytes_to_v4_addr to_addr to_subnet
function raw_bytes_to_v4_addr%(b: string%): addr
%{
uint32_t a = 0;
if ( b->Len() < 4 )
zeek::emit_builtin_error("too short a string as input to raw_bytes_to_v4_addr()");
else
{
const u_char* bp = b->Bytes();
a = (bp[0] << 24) | (bp[1] << 16) | (bp[2] << 8) | bp[3];
}
return zeek::make_intrusive<zeek::AddrVal>(htonl(a));
%}
## Converts a :zeek:type:`string` of bytes into an IPv6 address. In particular,
## this function interprets the first 16 bytes of the string as an IPv6 address
## in network order.
##
## b: The raw bytes (:zeek:type:`string`) to convert.
##
## Returns: The byte :zeek:type:`string` *b* as :zeek:type:`addr`.
##
## .. zeek:see:: raw_bytes_to_v6_addr to_addr to_subnet
function raw_bytes_to_v6_addr%(x: string%): addr
%{
uint32_t bytes[4] = {0, 0, 0, 0};
if ( x->Len() < 16 )
zeek::emit_builtin_error("too short a string as input to raw_bytes_to_v6_addr()");
else
{
const u_char* xp = x->Bytes();
bytes[0] = htonl((xp[0] << 24) | (xp[1] << 16) | (xp[2] << 8) | xp[3]);
bytes[1] = htonl((xp[0+4] << 24) | (xp[1+4] << 16) | (xp[2+4] << 8) | xp[3+4]);
bytes[2] = htonl((xp[0+8] << 24) | (xp[1+8] << 16) | (xp[2+8] << 8) | xp[3+8]);
bytes[3] = htonl((xp[0+12] << 24) | (xp[1+12] << 16) | (xp[2+12] << 8) | xp[3+12]);
}
return zeek::make_intrusive<zeek::AddrVal>(bytes);
%}
## Converts a :zeek:type:`string` to a :zeek:type:`port`.
##
## s: The :zeek:type:`string` to convert.
##
## Returns: A :zeek:type:`port` converted from *s*.
##
## .. zeek:see:: to_addr to_count to_int to_subnet
function to_port%(s: string%): port
%{
int port = 0;
if ( s->Len() > 0 && 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 representing a double value (in network byte order)
## to a :zeek:type:`double`. This is similar to :zeek:id:`bytestring_to_float`
## but works on 8-byte strings.
##
## 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.
##
## .. zeek:see:: bytestring_to_float
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 representing a float value (in network byte order)
## to a :zeek:type:`double`. This is similar to :zeek:id:`bytestring_to_double`
## but works on 4-byte strings.
##
## s: A string of bytes containing the binary representation of a float value.
##
## Returns: The float value contained in *s*, or 0 if the conversion
## failed.
##
## .. zeek:see:: bytestring_to_double
function bytestring_to_float%(s: string%): double
%{
if ( s->Len() != sizeof(float) )
{
zeek::emit_builtin_error("bad conversion to float");
return zeek::make_intrusive<zeek::DoubleVal>(0.0);
}
float f;
memcpy(&f, s->Bytes(), sizeof(float));
return zeek::make_intrusive<zeek::DoubleVal>(ntohf(f));
%}
## 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();
size_t string_len = static_cast<size_t>(s->Len());
unsigned int i = 0;
if ( string_len == 0 )
{
return zeek::val_mgr->Count(0);
}
if ( string_len == sizeof(uint8_t) )
{
uint8_t value = 0;
memcpy(&value, p, sizeof(uint8_t));
return zeek::val_mgr->Count(value);
}
else if ( string_len <= sizeof(uint16_t) )
{
uint16_t value = 0;
if ( host_bigendian == is_le )
{
char buf[sizeof(uint16_t)];
memset(buf, 0, sizeof(uint16_t));
char *d = &buf[sizeof(uint16_t)-1];
if ( is_le )
d -= sizeof(uint16_t) - string_len;
for ( i = 0; i < string_len; i++ )
*d-- = *p++;
memcpy(&value, buf, sizeof(uint16_t));
}
else
memcpy(&value, p, sizeof(uint16_t));
return zeek::val_mgr->Count(value);
}
else if ( string_len <= sizeof(uint32_t) )
{
uint32_t value = 0;
char buf[sizeof(uint32_t)];
memset(buf, 0, sizeof(uint32_t));
if ( host_bigendian == is_le )
{
char *d = &buf[sizeof(uint32_t)-1];
if ( ! is_le )
d -= sizeof(uint32_t) - string_len;
for ( i = 0; i < string_len; i++ )
*d-- = *p++;
}
else
{
char* d = &buf[0];
if ( ! is_le )
d += sizeof(uint32_t) - string_len;
for ( i = 0; i < string_len; i++ )
*d++ = *p++;
}
memcpy(&value, buf, sizeof(uint32_t));
return zeek::val_mgr->Count(value);
}
else if ( string_len <= sizeof(uint64_t) )
{
uint64_t value = 0;
char buf[sizeof(uint64_t)];
memset(buf, 0, sizeof(uint64_t));
if ( host_bigendian == is_le )
{
char *d = &buf[sizeof(uint64_t)-1];
if ( ! is_le )
d -= sizeof(uint64_t) - string_len;
for ( i = 0; i < string_len; i++ )
*d-- = *p++;
}
else
{
char* d = &buf[0];
if ( ! is_le )
d += sizeof(uint64_t) - string_len;
for ( i = 0; i < string_len; i++ )
*d++ = *p++;
}
memcpy(&value, buf, 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(),
"%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32
"%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32
"%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32
"%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32 "%1" SCNx32
"%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
%{
zeek_uint_t len = bytestring->AsString()->Len();
const u_char* bytes = bytestring->AsString()->Bytes();
auto hexstr_buf = std::make_unique<char[]>((2 * len) + 1);
auto hexstr = hexstr_buf.get();
for ( zeek_uint_t i = 0; i < len; ++i )
zeek::util::bytetohex(bytes[i], &hexstr[2 * i]);
hexstr[2 * len] = 0;
// Adopt the memory...
String* s = new String(true, reinterpret_cast<uint8_t*>(hexstr_buf.release()), 2 * len);
return zeek::make_intrusive<zeek::StringVal>(s);
%}
## 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
%{
zeek_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();
size_t outlen = (len/2);
auto bytestring_buf = std::make_unique<char[]>(outlen);
auto bytestring = bytestring_buf.get();
memset(bytestring, 0, outlen);
for ( zeek_uint_t i = 0; i < len/2; ++i )
{
int res = sscanf(bytes + (2*i), "%2hhx", &bytestring[i]);
if ( res == EOF )
{
zeek::reporter->Error("Hex string %s contains invalid input: %s", hexstr->CheckString(), strerror(errno));
return zeek::val_mgr->EmptyString();
}
else if ( res != 1 )
{
zeek::reporter->Error("Could not read hex element from input %s", hexstr->CheckString());
return zeek::val_mgr->EmptyString();
}
}
return zeek::make_intrusive<zeek::StringVal>(outlen, bytestring);
%}
## Encodes a Base64-encoded string.
##
## s: The string to encode.
##
## a: An optional custom alphabet. The empty string indicates the default
## alphabet. If given, the string must consist of 64 unique characters.
##
## Returns: The encoded version of *s*.
##
## .. zeek:see:: decode_base64
function encode_base64%(s: string, a: string &default=""%): string
%{
String* t = zeek::detail::encode_base64(s->AsString(), a->AsString());
if ( t )
return zeek::make_intrusive<zeek::StringVal>(t);
else
{
zeek::reporter->Error("Broker query has an invalid data store");
return zeek::val_mgr->EmptyString();
}
%}
## Decodes a Base64-encoded string.
##
## s: The Base64-encoded string.
##
## a: An optional custom alphabet. The empty string indicates the default
## alphabet. If given, the string must consist of 64 unique characters.
##
## Returns: The decoded version of *s*.
##
## .. zeek:see:: decode_base64_conn encode_base64
function decode_base64%(s: string, a: string &default=""%): string
%{
String* t = zeek::detail::decode_base64(s->AsString(), a->AsString());
if ( t )
return zeek::make_intrusive<zeek::StringVal>(t);
else
{
zeek::reporter->Error("error in decoding string %s", s->CheckString());
return zeek::val_mgr->EmptyString();
}
%}
## Decodes a Base64-encoded string that was derived from processing a connection.
## If an error is encountered decoding the string, that will be logged to
## ``weird.log`` with the associated connection.
##
## cid: The identifier of the connection that the encoding originates from.
##
## s: The Base64-encoded string.
##
## a: An optional custom alphabet. The empty string indicates the default
## alphabet. If given, the string must consist of 64 unique characters.
##
## Returns: The decoded version of *s*.
##
## .. zeek:see:: decode_base64
function decode_base64_conn%(cid: conn_id, s: string, a: string &default=""%): string
%{
Connection* conn = session_mgr->FindConnection(cid);
if ( ! conn )
{
zeek::emit_builtin_error("connection ID not a known connection", cid);
return zeek::val_mgr->EmptyString();
}
String* t = zeek::detail::decode_base64(s->AsString(), a->AsString(), conn);
if ( t )
return zeek::make_intrusive<zeek::StringVal>(t);
else
{
zeek::reporter->Error("error in decoding string %s", s->CheckString());
return zeek::val_mgr->EmptyString();
}
%}
%%{
struct zeek_uuid_t {
uint32_t time_low;
uint16_t time_mid;
uint16_t time_hi_and_version;
uint8_t clock_seq_hi_and_reserved;
uint8_t clock_seq_low;
uint8_t node[6];
};
%%}
## Converts a bytes representation of a UUID into its string form. For example,
## given a string of 16 bytes, it produces an output string in this format:
## ``550e8400-e29b-41d4-a716-446655440000``.
## See `<http://en.wikipedia.org/wiki/Universally_unique_identifier>`_.
##
## uuid: The 16 bytes of the UUID.
##
## Returns: The string representation of *uuid*.
function uuid_to_string%(uuid: string%): string
%{
if ( uuid->Len() != 16 )
return zeek::make_intrusive<zeek::StringVal>("<Invalid UUID>");
zeek_uuid_t* id = (zeek_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 std::move(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 zeek::detail::build_dummy_conn_record();
%}
%%{
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
##
## .. note::
##
## See :zeek:see:`get_current_packet` for caveats.
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
##
## .. note::
##
## Calling ``get_current_packet()`` within events that are not directly
## raised as a result of processing a specific packet may result in
## unexpected behavior. For example, out-of-order TCP segments or IP
## defragmentation may result in such scenarios. Details depend on the
## involved packet and protocol analyzers. As a rule of thumb, in low-level
## events, like :zeek:see:`raw_packet`, the behavior is well defined.
##
## The returned packet is directly taken from the packet source and any
## tunnel or encapsulation layers will be present in the payload. Correctly
## inspecting the payload using Zeek script is therefore a non-trivial task.
##
## The return value of ``get_current_packet()`` further should be considered
## undefined when called within event handlers raised via :zeek:see:`event`,
## :zeek:see:`schedule` or by recipient of Broker messages.
function get_current_packet%(%) : pcap_packet
%{
const Packet* p = nullptr;
zeek::iosource::PktSrc* pkt_src = zeek::run_state::detail::current_packet_source();
if ( ! pkt_src || ! pkt_src->GetCurrentPacket(&p) )
p = nullptr;
return Packet::ToVal(p);
%}
## Function to get the raw headers of the currently processed packet.
##
## Returns: The :zeek:type:`raw_pkt_hdr` record containing the Layer 2, 3 and
## 4 headers of the currently processed packet.
##
## .. zeek:see:: raw_pkt_hdr get_current_packet
##
## .. note::
##
## See :zeek:see:`get_current_packet` for caveats.
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 std::move(hdr);
%}
## Returns the currently processed PCAP packet's timestamp or a 0 timestamp if
## there is no packet being processed at the moment.
##
## Returns: The currently processed packet's timestamp.
##
## .. zeek:see:: get_current_packet get_current_packet_header network_time
##
## .. note::
##
## When there is no packet being processed, ``get_current_packet_ts()``
## will return a 0 timestamp, while ``network_time()`` will return the
## timestamp of the last processed packet until it falls back to tracking
## wall clock after ``packet_source_inactivity_timeout``.
function get_current_packet_ts%(%) : time
%{
double ts = 0;
const Packet* p = nullptr;
zeek::iosource::PktSrc* pkt_src = zeek::run_state::detail::current_packet_source();
if ( pkt_src && pkt_src->GetCurrentPacket(&p) )
ts = p->time;
return zeek::make_intrusive<zeek::TimeVal>(ts);
%}
## Writes a given packet to a file.
##
## pkt: The PCAP packet.
##
## file_name: The name of the file to write *pkt* to.
##
## Returns: True on success
##
## .. zeek:see:: get_current_packet dump_current_packet
function dump_packet%(pkt: pcap_packet, file_name: string%) : bool
%{
if ( addl_pkt_dumper && addl_pkt_dumper->Path() != file_name->CheckString())
{
addl_pkt_dumper->Close();
addl_pkt_dumper = nullptr;
}
if ( ! addl_pkt_dumper )
addl_pkt_dumper = zeek::iosource_mgr->OpenPktDumper(file_name->CheckString(), true);
if ( ! addl_pkt_dumper->IsError() )
{
pkt_timeval ts;
uint32_t caplen, len, link_type;
u_char *data;
auto pkt_r = pkt->As<zeek::RecordVal*>();
ts.tv_sec = pkt_r->GetFieldAs<zeek::CountVal>(0);
ts.tv_usec = pkt_r->GetFieldAs<zeek::CountVal>(1);
caplen = pkt_r->GetFieldAs<zeek::CountVal>(2);
len = pkt_r->GetFieldAs<zeek::CountVal>(3);
data = pkt_r->GetFieldAs<zeek::StringVal>(4)->Bytes();
link_type = pkt_r->GetFieldAs<zeek::EnumVal>(5);
Packet p(link_type, &ts, caplen, len, data, true);
addl_pkt_dumper->Dump(&p);
}
return zeek::val_mgr->Bool(addl_pkt_dumper && ! addl_pkt_dumper->IsError());
%}
%%{
#include "zeek/DNS_Mgr.h"
#include "zeek/Trigger.h"
class LookupHostCallback : public zeek::detail::DNS_Mgr::LookupCallback {
public:
LookupHostCallback(zeek::detail::trigger::Trigger* arg_trigger,
const void* arg_assoc, bool arg_lookup_name)
{
Ref(arg_trigger);
trigger = arg_trigger;
assoc = arg_assoc;
lookup_name = arg_lookup_name;
}
~LookupHostCallback()
{
Unref(trigger);
}
// Overridden from zeek::detail::DNS_Mgr:Lookup:Callback.
void Resolved(const std::string& name) override
{
zeek::Val* result = new zeek::StringVal(name);
trigger->Cache(assoc, result);
Unref(result);
trigger->Release();
}
void Resolved(zeek::TableValPtr addrs) override
{
// No Ref() for addrs.
trigger->Cache(assoc, addrs.get());
trigger->Release();
}
void Timeout() override
{
if ( lookup_name )
{
zeek::Val* result = new zeek::StringVal("<\?\?\?>");
trigger->Cache(assoc, result);
Unref(result);
}
else
{
auto* lv = new zeek::ListVal(zeek::TYPE_ADDR);
lv->Append(zeek::make_intrusive<zeek::AddrVal>("0.0.0.0"));
auto result = lv->ToSetVal();
trigger->Cache(assoc, result.get());
Unref(lv);
}
trigger->Release();
}
private:
zeek::detail::trigger::Trigger* trigger;
const void* assoc;
bool lookup_name;
};
%%}
## Issues an asynchronous reverse DNS lookup and delays the function result.
## This function can therefore only be called inside a ``when`` condition,
## e.g., ``when ( local host = lookup_addr(10.0.0.1) ) { f(host); }``.
##
## host: The IP address to lookup.
##
## Returns: The DNS name of *host*.
##
## .. zeek:see:: lookup_hostname
function lookup_addr%(host: addr%) : string
%{
// FIXME: It should be easy to adapt the function to synchronous
// lookups if we're reading a trace.
zeek::detail::trigger::Trigger* trigger = frame->GetTrigger();
if ( ! trigger)
{
zeek::emit_builtin_error("lookup_addr() can only be called inside a when-condition");
return zeek::make_intrusive<zeek::StringVal>("<error>");
}
frame->SetDelayed();
trigger->Hold();
zeek::detail::dns_mgr->LookupAddr(host->AsAddr(),
new LookupHostCallback(trigger, frame->GetTriggerAssoc(), true));
return nullptr;
%}
## Issues an asynchronous TEXT DNS lookup and delays the function result.
## This function can therefore only be called inside a ``when`` condition,
## e.g., ``when ( local h = lookup_hostname_txt("www.zeek.org") ) { f(h); }``.
##
## host: The hostname to lookup.
##
## Returns: The DNS TXT record associated with *host*.
##
## .. zeek:see:: lookup_hostname
function lookup_hostname_txt%(host: string%) : string
%{
// FIXME: Is should be easy to adapt the function to synchronous
// lookups if we're reading a trace.
zeek::detail::trigger::Trigger* trigger = frame->GetTrigger();
if ( ! trigger)
{
zeek::emit_builtin_error("lookup_hostname_txt() can only be called inside a when-condition");
return zeek::make_intrusive<zeek::StringVal>("<error>");
}
frame->SetDelayed();
trigger->Hold();
zeek::detail::dns_mgr->Lookup(host->CheckString(), T_TXT,
new LookupHostCallback(trigger, frame->GetTriggerAssoc(), true));
return nullptr;
%}
## Issues an asynchronous DNS lookup and delays the function result.
## This function can therefore only be called inside a ``when`` condition,
## e.g., ``when ( local h = lookup_hostname("www.zeek.org") ) { f(h); }``.
##
## host: The hostname to lookup.
##
## Returns: A set of DNS A and AAAA records associated with *host*.
##
## .. zeek:see:: lookup_addr blocking_lookup_hostname
function lookup_hostname%(host: string%) : addr_set
%{
// FIXME: Is should be easy to adapt the function to synchronous
// lookups if we're reading a trace.
zeek::detail::trigger::Trigger* trigger = frame->GetTrigger();
if ( ! trigger)
{
zeek::emit_builtin_error("lookup_hostname() can only be called inside a when-condition");
return zeek::make_intrusive<zeek::StringVal>("<error>");
}
frame->SetDelayed();
trigger->Hold();
zeek::detail::dns_mgr->LookupHost(host->CheckString(),
new LookupHostCallback(trigger, frame->GetTriggerAssoc(), false));
return nullptr;
%}
## Issues a synchronous DNS lookup.
##
## host: The hostname to lookup.
##
## Returns: A set addresses, either IPv4 or IPv6, associated with *host*.
##
## .. zeek:see:: lookup_addr
##
## .. note::
##
## This is a blocking call. You should use :zeek:see:`lookup_hostname`
## unless for initialization or testing purposes.
##
## .. zeek:see:: lookup_addr lookup_hostname
function blocking_lookup_hostname%(host: string%) : addr_set
%{
return zeek::detail::dns_mgr->LookupHost(host->CheckString());
%}
## Calculates distance between two geographic locations using the haversine
## formula. Latitudes and longitudes must be given in degrees, where southern
## hemisphere 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"
%%}
## Returns the numeric ID of the requested protocol analyzer for the given
## connection.
##
## cid: The connection identifier.
##
## atype: The analyzer tag, such as ``Analyzer::ANALYZER_HTTP``.
##
## Returns: a numeric identifier for the analyzer, valid for the given
## connection. When no such analyzer exists the function returns
## 0, which is never a valid analyzer ID value.
##
## .. zeek:see:: disable_analyzer Analyzer::disabling_analyzer
function lookup_connection_analyzer_id%(cid: conn_id, atype: AllAnalyzers::Tag%): count
%{
Connection* c = session_mgr->FindConnection(cid);
if ( ! c )
{
zeek::emit_builtin_error("connection ID not a known connection", cid);
return zeek::val_mgr->Count(0);
}
analyzer::Analyzer* a = c->FindAnalyzer(analyzer_mgr->GetComponentTag(atype));
if ( ! a )
return zeek::val_mgr->Count(0);
return zeek::val_mgr->Count(a->GetID());
%}
## 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::emit_builtin_error("connection ID not a known connection", cid);
return zeek::val_mgr->False();
}
analyzer::Analyzer* a = c->FindAnalyzer(aid);
if ( ! a )
{
if ( err_if_no_conn )
zeek::emit_builtin_error("connection does not have analyzer specified to disable");
return zeek::val_mgr->False();
}
// If a user passed the analyzer ID of a root analyzer like TCP or UDP
// report an error but don't do anything. This likely is a scripting
// error as it should not be possible to get access to the analyzer id
// of a root analyzer without probing for it.
if ( ! a->Parent() )
{
zeek::emit_builtin_error(zeek::util::fmt("root analyzer %s cannot be removed", a->GetAnalyzerName()));
return zeek::val_mgr->False();
}
static auto disabling_analyzer_hook = id::find_func("Analyzer::disabling_analyzer");
if ( disabling_analyzer_hook )
{
auto hook_rval = disabling_analyzer_hook->Invoke(c->GetVal(), a->GetAnalyzerTag().AsVal(),
zeek::val_mgr->Count(aid));
if ( hook_rval && ! hook_rval->AsBool() )
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::reporter->Warning("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::reporter->Warning("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::reporter->Warning("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::reporter->Warning("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 std::move(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 std::move(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 current event came from a remote peer.
##
## Returns: True if the current 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
## is_processing_suspended
function suspend_processing%(%) : any
%{
zeek::run_state::suspend_processing();
return nullptr;
%}
## Resumes Zeek's packet processing.
##
## .. zeek:see:: suspend_processing
## is_processing_suspended
function continue_processing%(%) : any
%{
zeek::run_state::continue_processing();
return nullptr;
%}
## Returns whether or not processing is currently suspended.
##
## .. zeek:see:: suspend_processing
## continue_processing
function is_processing_suspended%(%): bool
%{
return zeek::val_mgr->Bool(zeek::run_state::is_processing_suspended());
%}
# ===========================================================================
#
# 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
%{
event_registry->ActivateAllHandlers();
return zeek::val_mgr->True();
%}
## Check if an event is handled. Typically this means that a script defines an event.
## This currently is mainly used to warn when events are defined that will not be used
## in certain conditions.
##
## Raises an error if the named event does not exist.
##
## event_name: event name to check
##
## returns: true if the named event is handled.
function is_event_handled%(event_name: string%) : bool
%{
auto *h = event_registry->Lookup(event_name->ToStdStringView());
if ( ! h )
{
zeek::emit_builtin_error(zeek::util::fmt("is_event_handled: '%s' is not an event", event_name->CheckString()));
return zeek::val_mgr->False();
}
if ( *h )
return zeek::val_mgr->True();
return zeek::val_mgr->False();
%}
%%{
// 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.
##
## field_escape_pattern: If the v value is a record, the given pattern is
## matched against the field names of its type, and
## the first match, if any, is stripped from the
## rendered name. The default pattern strips a leading
## underscore.
##
## interval_as_double: If T, interval values will be logged as doubles
## instead of the broken-out version with units as strings.
##
## returns: a JSON formatted string.
##
## .. zeek:see:: fmt cat cat_sep string_cat print_raw from_json
function to_json%(val: any, only_loggable: bool &default=F, field_escape_pattern: pattern &default=/^_/, interval_as_double: bool &default=F%): string
%{
return val->ToJSON(only_loggable, field_escape_pattern, interval_as_double);
%}
## A function to convert a JSON string into Zeek values of a given type.
##
## Implicit conversion from JSON to Zeek types is implemented for:
##
## - bool
## - int, count, real
## - interval from numbers as seconds
## - time from numbers as unix timestamp
## - port from strings in "80/tcp" notation
## - addr, subnet
## - enum
## - sets
## - vectors
## - records (from JSON objects)
##
## Optional or default record fields are allowed to be missing or null in the input.
##
## s: The JSON string to parse.
##
## t: Type of Zeek data.
##
## key_func: Optional function to normalize key names in JSON objects. Useful
## when keys are not valid field identifiers, or represent reserved
## keywords like **port** or **type**.
##
## returns: A record with the result of the conversion, containing either a value or an error message.
##
## .. zeek:see:: to_json
function from_json%(s: string, t: any, key_func: string_mapper &default=from_json_default_key_mapper%): from_json_result
%{
static auto result_type = zeek::id::find_type<RecordType>("from_json_result");
static auto v_idx = result_type->FieldOffset("v");
static auto valid_idx = result_type->FieldOffset("valid");
static auto default_key_func_ptr = zeek::id::find_func("from_json_default_key_mapper");
auto rval = zeek::make_intrusive<RecordVal>(result_type);
if ( t->GetType()->Tag() != zeek::TYPE_TYPE )
{
rval->Assign(valid_idx, false);
zeek::emit_builtin_error("from_json() requires a type argument");
return rval;
}
// If key_func is the same as "from_json_default_key_mapper",
// null it out so that no key normalization happens.
auto key_func_ptr = key_func->AsFuncVal()->AsFuncPtr();
if ( key_func_ptr == default_key_func_ptr )
key_func_ptr = Func::nil;
auto res = zeek::detail::ValFromJSON(s->ToStdStringView(), t->AsType()->AsTypeType()->GetType(),
key_func_ptr);
if ( res )
{
rval->Assign(v_idx, res.value());
rval->Assign(valid_idx, true);
}
else
{
rval->Assign(valid_idx, false);
rval->Assign(result_type->FieldOffset("err"), res.error().c_str());
}
return std::move(rval);
%}
## 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->ToStdStringView()));
%}
## Returns true if the given tag belongs to a protocol analyzer.
##
## atype: The analyzer tag to check.
##
## Returns: true if *atype* is a tag of a protocol analyzer, else false.
function is_protocol_analyzer%(atype: AllAnalyzers::Tag%) : bool
%{
auto val = atype->AsEnumVal();
return val_mgr->Bool(zeek::analyzer_mgr->Lookup(val) != nullptr);
%}
## Returns true if the given tag belongs to a file analyzer.
##
## atype: The analyzer tag to check.
##
## Returns: true if *atype* is a tag of a file analyzer, else false.
function is_file_analyzer%(atype: AllAnalyzers::Tag%) : bool
%{
auto val = atype->AsEnumVal();
return val_mgr->Bool(zeek::file_mgr->Lookup(val) != nullptr);
%}
## Returns true if the given tag belongs to a packet analyzer.
##
## atype: The analyzer type to check.
##
## Returns: true if *atype* is a tag of a packet analyzer, else false.
function is_packet_analyzer%(atype: AllAnalyzers::Tag%) : bool
%{
auto val = atype->AsEnumVal();
return val_mgr->Bool(zeek::packet_mgr->Lookup(val) != nullptr);
%}
%%{ // C segment
static bool enable_event_group(zeek::EventGroupKind kind, const char *group)
{
auto g = zeek::event_registry->LookupGroup(kind, group);
if ( ! g )
{
zeek::emit_builtin_error(zeek::util::fmt("no such event group: %s", group));
return false;
}
g->Enable();
return true;
}
static bool disable_event_group(zeek::EventGroupKind kind, const char *group)
{
auto g = zeek::event_registry->LookupGroup(kind, group);
if ( ! g )
{
zeek::emit_builtin_error(zeek::util::fmt("no such event group: %s", group));
return false;
}
g->Disable();
return true;
}
static bool has_event_group(zeek::EventGroupKind kind, const char *group)
{
return zeek::event_registry->LookupGroup(kind, group) != nullptr;
}
%%}
## Enabled the given event group.
##
## All event and hook handlers with a matching :zeek:attr:`&group` attribute
## will be enabled if this group was the last disabled group of these handlers.
##
## group: The group to enable.
##
## .. zeek:see:: enable_event_group disable_event_group has_event_group
## enable_module_events disable_module_events has_module_events
function enable_event_group%(group: string%) : bool
%{
return zeek::val_mgr->Bool(enable_event_group(zeek::EventGroupKind::Attribute,
group->CheckString()));
%}
## Disabled the given event group.
##
## All event and hook handlers with a matching :zeek:attr:`&group` attribute
## will be disabled if not already disabled through another group.
##
## group: The group to disable.
##
## .. zeek:see:: enable_event_group disable_event_group has_event_group
## enable_module_events disable_module_events has_module_events
function disable_event_group%(group: string%) : bool
%{
return zeek::val_mgr->Bool(disable_event_group(zeek::EventGroupKind::Attribute,
group->CheckString()));
%}
## Does an attribute event group with this name exist?
##
## group: The group name.
##
## .. zeek:see:: enable_event_group disable_event_group has_event_group
## enable_module_events disable_module_events has_module_events
function has_event_group%(group: string%) : bool
%{
return zeek::val_mgr->Bool(has_event_group(zeek::EventGroupKind::Attribute,
group->CheckString()));
%}
## Enable all event handlers and hooks in the given module.
##
## All event handlers and hooks defined in the given module will be enabled
## if not disabled otherwise through an event group.
##
## module_name: The module to enable.
##
## .. zeek:see:: enable_event_group disable_event_group has_event_group
## enable_module_events disable_module_events has_module_events
function enable_module_events%(module_name: string%) : bool
%{
return zeek::val_mgr->Bool(enable_event_group(zeek::EventGroupKind::Module,
module_name->CheckString()));
%}
## Disable all event handlers and hooks in the given module.
##
## All event handlers and hooks defined in the given module will be disabled.
##
## module_name: The module to disable.
##
## .. zeek:see:: enable_event_group disable_event_group has_event_group
## enable_module_events disable_module_events has_module_events
function disable_module_events%(module_name: string%) : bool
%{
return zeek::val_mgr->Bool(disable_event_group(zeek::EventGroupKind::Module,
module_name->CheckString()));
%}
## Does a module event group with this name exist?
##
## group: The group name.
##
## .. zeek:see:: enable_event_group disable_event_group has_event_group
## enable_module_events disable_module_events has_module_events
function has_module_events%(group: string%) : bool
%{
return zeek::val_mgr->Bool(has_event_group(zeek::EventGroupKind::Module,
group->CheckString()));
%}
## Returns true if Zeek was built with support for using Spicy analyzers (which
## is the default).
function have_spicy%(%) : bool
%{
#ifdef HAVE_SPICY
return zeek::val_mgr->Bool(true);
#else
return zeek::val_mgr->Bool(false);
#endif
%}
## Returns true if Zeek was built with support for its in-tree Spicy analyzers
## (which is the default if Spicy support is available).
function have_spicy_analyzers%(%) : bool
%{
return zeek::val_mgr->Bool(USE_SPICY_ANALYZERS);
%}
%%{
#include "zeek/DFA.h"
%%}
## Return MatcherStats for a table[pattern] or set[pattern] value.
##
## This returns a MatcherStats objects that can be used for introspection
## of the DFA used for such a table. Statistics reset whenever elements are
## added or removed to the table as these operations result in the underlying
## DFA being rebuilt.
##
## This function iterates over all states of the DFA. Calling it at a high
## frequency is likely detrimental to performance.
##
## tbl: The table to get stats for.
##
## Returns: A record with matcher statistics.
function table_pattern_matcher_stats%(tbl: any%) : MatcherStats
%{
static auto matcher_stats_type = zeek::id::find_type<zeek::RecordType>("MatcherStats");
const auto& type = tbl->GetType();
if ( type->Tag() != zeek::TYPE_TABLE )
{
zeek::emit_builtin_error("pattern-table_stats() requires a table argument");
return nullptr;
}
if ( ! type->AsTableType()->IsPatternIndex() )
{
zeek::emit_builtin_error("pattern_table_stats() requires a single index of type pattern");
return nullptr;
}
zeek::detail::DFA_State_Cache::Stats stats;
tbl->AsTableVal()->GetPatternMatcherStats(&stats);
auto result = zeek::make_intrusive<zeek::RecordVal>(matcher_stats_type);
int n = 0;
result->Assign(n++, 1); // matchers
result->Assign(n++, stats.nfa_states);
result->Assign(n++, stats.dfa_states);
result->Assign(n++, stats.computed);
result->Assign(n++, stats.mem);
result->Assign(n++, stats.hits);
result->Assign(n++, stats.misses);
return std::move(result);
%}
## Determine the path used by a non-relative @load directive.
##
## This function is package aware: Passing *package* will yield the
## path to *package.zeek*, *package/__load__.zeek* or an empty string
## if neither can be found. Note that passing a relative path or absolute
## path is an error.
##
## path: The filename, package or path to search for in ZEEKPATH.
##
## Returns: Path of script file that would be loaded by an @load directive.
function find_in_zeekpath%(p: string%): string
%{
auto path = p->ToStdString();
if ( ! path.empty() && (path[0] == '.' || path[0] == '/') )
{
zeek::reporter->Error("find_in_zeek_path: path must be relative or absolute");
return zeek::val_mgr->EmptyString();
}
auto resolved = zeek::util::find_script_file(path, zeek::util::zeek_path());
if ( ! resolved.empty() && zeek::util::is_dir(resolved.c_str()) )
{
// If it's a directory, try opening the package using
// the absolute path. This is zeek::util::open_package()
// without the noisy error log.
resolved.append("/__load__.zeek");
resolved = zeek::util::find_file(resolved, "");
}
return zeek::make_intrusive<zeek::StringVal>(resolved.c_str());
%}