mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
5295 lines
147 KiB
C++
5295 lines
147 KiB
C++
##! A collection of built-in functions that implement a variety of things
|
|
##! such as general programming algorithms, string processing, math functions,
|
|
##! introspection, type conversion, file/directory manipulation, packet
|
|
##! filtering, interprocess communication and controlling protocol analyzer
|
|
##! behavior.
|
|
##!
|
|
##! You'll find most of Zeek's built-in functions that aren't protocol-specific
|
|
##! in this file.
|
|
|
|
%%{ // C segment
|
|
#include <math.h>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <sys/stat.h>
|
|
#include <cstdio>
|
|
#include <time.h>
|
|
|
|
#include "zeek/digest.h"
|
|
#include "zeek/Reporter.h"
|
|
#include "zeek/IPAddr.h"
|
|
#include "zeek/util.h"
|
|
#include "zeek/file_analysis/Manager.h"
|
|
#include "zeek/iosource/Manager.h"
|
|
#include "zeek/iosource/Packet.h"
|
|
#include "zeek/iosource/PktSrc.h"
|
|
#include "zeek/iosource/PktDumper.h"
|
|
#include "zeek/IntrusivePtr.h"
|
|
#include "zeek/input.h"
|
|
#include "zeek/Hash.h"
|
|
#include "zeek/packet_analysis/Manager.h"
|
|
|
|
using namespace std;
|
|
|
|
zeek::TableType* var_sizes;
|
|
|
|
static zeek::iosource::PktDumper* addl_pkt_dumper = nullptr;
|
|
|
|
bro_int_t parse_int(const char*& fmt)
|
|
{
|
|
bro_int_t k = 0;
|
|
while ( isdigit(*fmt) )
|
|
{
|
|
k = k * 10 + (*fmt - '0');
|
|
++fmt;
|
|
}
|
|
|
|
return k;
|
|
}
|
|
|
|
static zeek::TypeTag ok_d_fmt[] = {
|
|
zeek::TYPE_BOOL, zeek::TYPE_ENUM, zeek::TYPE_INT, zeek::TYPE_COUNT, zeek::TYPE_PORT,
|
|
zeek::TYPE_SUBNET,
|
|
zeek::TYPE_ERROR
|
|
};
|
|
static zeek::TypeTag ok_f_fmt[] = {
|
|
zeek::TYPE_DOUBLE, zeek::TYPE_TIME, zeek::TYPE_INTERVAL,
|
|
zeek::TYPE_ERROR
|
|
};
|
|
|
|
static int check_fmt_type(zeek::TypeTag t, zeek::TypeTag ok[])
|
|
{
|
|
for ( int i = 0; ok[i] != zeek::TYPE_ERROR; ++i )
|
|
if ( ok[i] == t )
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void do_fmt(const char*& fmt, zeek::Val* v, zeek::ODesc* d)
|
|
{
|
|
if ( ! v )
|
|
// Help with error propagation in compiled code.
|
|
return;
|
|
|
|
zeek::TypeTag t = v->GetType()->Tag();
|
|
zeek::InternalTypeTag it = v->GetType()->InternalType();
|
|
|
|
bool zero_pad = false;
|
|
bool left_just = false;
|
|
int field_width = -1;
|
|
|
|
// Left-align, if requested.
|
|
if ( *fmt == '-' )
|
|
{
|
|
left_just = true;
|
|
++fmt;
|
|
}
|
|
|
|
// Parse field width, if given.
|
|
if ( isdigit(*fmt) )
|
|
{
|
|
// If field width starts with zero, do zero-padding.
|
|
if ( *fmt == '0' )
|
|
{
|
|
zero_pad = true;
|
|
++fmt;
|
|
}
|
|
|
|
field_width = parse_int(fmt);
|
|
}
|
|
|
|
int precision = -1;
|
|
if ( *fmt == '.' )
|
|
{
|
|
++fmt;
|
|
precision = parse_int(fmt);
|
|
}
|
|
|
|
if ( field_width > 128 || precision > 128 )
|
|
{
|
|
zeek::emit_builtin_error("excessive field width or precision");
|
|
return;
|
|
}
|
|
|
|
// Create the numerical format string.
|
|
char num_fmt[64];
|
|
num_fmt[0] = '\0';
|
|
|
|
if ( field_width >= 0 )
|
|
{
|
|
// Like sprintf(), ignore '0' if '-' is given.
|
|
const char* align = left_just ? "-" : (zero_pad ? "0" : "");
|
|
snprintf(num_fmt, sizeof(num_fmt), "%s%d", align, field_width);
|
|
}
|
|
|
|
if ( precision >= 0 )
|
|
snprintf(num_fmt + strlen(num_fmt),
|
|
sizeof(num_fmt) - strlen(num_fmt), ".%d", precision);
|
|
|
|
char fmt_buf[512];
|
|
char out_buf[512];
|
|
|
|
zeek::ODesc s;
|
|
s.SetStyle(zeek::RAW_STYLE);
|
|
|
|
if ( precision >= 0 && *fmt != 'e' && *fmt != 'f' && *fmt != 'g' )
|
|
zeek::emit_builtin_error("precision specified for non-floating point");
|
|
|
|
switch ( *fmt ) {
|
|
case 'D':
|
|
case 'T': // ISO Timestamp with microsecond precision.
|
|
{
|
|
if ( t != zeek::TYPE_TIME )
|
|
{
|
|
zeek::emit_builtin_error("bad type for Date/Time format", v);
|
|
break;
|
|
}
|
|
|
|
time_t time = time_t(v->InternalDouble());
|
|
struct tm t;
|
|
|
|
bool is_time_fmt = *fmt == 'T';
|
|
|
|
if ( ! localtime_r(&time, &t) )
|
|
s.AddSP("<problem getting time>");
|
|
|
|
if ( ! strftime(out_buf, sizeof(out_buf),
|
|
is_time_fmt ?
|
|
"%Y-%m-%d-%H:%M" : "%Y-%m-%d-%H:%M:%S",
|
|
&t) )
|
|
s.AddSP("<bad time>");
|
|
|
|
else
|
|
{
|
|
s.Add(out_buf);
|
|
|
|
if ( is_time_fmt )
|
|
{
|
|
double secs = v->CoerceToUnsigned() % 60;
|
|
|
|
secs += v->InternalDouble();
|
|
secs -= v->CoerceToUnsigned();
|
|
|
|
snprintf(out_buf, sizeof(out_buf),
|
|
":%012.9f", secs);
|
|
s.Add(out_buf);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'd':
|
|
case 'x':
|
|
{
|
|
if ( *fmt == 'x' && it == zeek::TYPE_INTERNAL_ADDR )
|
|
{
|
|
// Deficiency: we don't support num_fmt in this case.
|
|
// This makes only a very slight difference, so not
|
|
// clear it would e worth the hassle.
|
|
|
|
snprintf(out_buf, sizeof(out_buf), "%s",
|
|
v->AsAddr().AsHexString().c_str());
|
|
}
|
|
|
|
else if ( ! check_fmt_type(t, ok_d_fmt) )
|
|
{
|
|
zeek::emit_builtin_error("bad type for %d/%x format", v);
|
|
break;
|
|
}
|
|
|
|
else if ( it == zeek::TYPE_INTERNAL_UNSIGNED )
|
|
{
|
|
bro_uint_t u = v->CoerceToUnsigned();
|
|
|
|
if ( v->GetType()->IsNetworkOrder() )
|
|
{
|
|
if ( v->GetType()->Tag() == zeek::TYPE_PORT )
|
|
u = v->AsPortVal()->Port();
|
|
else
|
|
u = ntohl(uint32_t(u));
|
|
}
|
|
|
|
snprintf(fmt_buf, sizeof(fmt_buf), "%%%s%s", num_fmt,
|
|
*fmt == 'd' ? "llu" : "llx");
|
|
snprintf(out_buf, sizeof(out_buf), fmt_buf, u);
|
|
}
|
|
|
|
else
|
|
{
|
|
snprintf(fmt_buf, sizeof(fmt_buf), "%%%s%s", num_fmt,
|
|
*fmt == 'd' ? "lld" : "llx");
|
|
snprintf(out_buf, sizeof(out_buf), fmt_buf,
|
|
v->CoerceToInt());
|
|
}
|
|
|
|
s.Add(out_buf);
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
v->Describe(&s);
|
|
break;
|
|
|
|
case 'e':
|
|
case 'f':
|
|
case 'g':
|
|
{
|
|
if ( ! check_fmt_type(t, ok_f_fmt) )
|
|
{
|
|
zeek::emit_builtin_error("bad type for floating-point format", v);
|
|
break;
|
|
}
|
|
|
|
snprintf(fmt_buf, sizeof(fmt_buf), "%%%s%c", num_fmt, *fmt);
|
|
snprintf(out_buf, sizeof(out_buf), fmt_buf, v->CoerceToDouble());
|
|
s.Add(out_buf);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
zeek::emit_builtin_error("bad format");
|
|
}
|
|
|
|
// Left-padding with whitespace, if any.
|
|
if ( field_width > 0 && ! left_just )
|
|
{
|
|
int sl = strlen(s.Description());
|
|
while ( ++sl <= field_width )
|
|
d->Add(" ");
|
|
}
|
|
|
|
d->AddN((const char*)(s.Bytes()), s.Len());
|
|
|
|
// Right-padding with whitespace, if any.
|
|
if ( field_width > 0 && left_just )
|
|
{
|
|
int sl = s.Len();
|
|
while ( ++sl <= field_width )
|
|
d->Add(" ");
|
|
}
|
|
|
|
++fmt;
|
|
}
|
|
|
|
static int next_fmt(const char*& fmt, const zeek::Args* args, zeek::ODesc* d, int& n)
|
|
{
|
|
const char* fp = fmt;
|
|
|
|
// Skip up to next format indicator.
|
|
while ( *fp && *fp != '%' )
|
|
++fp;
|
|
|
|
d->AddN(fmt, fp - fmt);
|
|
|
|
if ( *fp == '\0' )
|
|
// No more to do.
|
|
return 0;
|
|
|
|
fmt = fp + 1;
|
|
if ( *fmt == '%' )
|
|
{
|
|
// "%%" -> '%'
|
|
d->Add("%");
|
|
++fmt;
|
|
return next_fmt(fmt, args, d, n);
|
|
}
|
|
|
|
if ( ++n >= static_cast<int>(args->size()) )
|
|
return 0;
|
|
|
|
do_fmt(fmt, (*args)[n].get(), d);
|
|
|
|
return *fmt != '\0';
|
|
}
|
|
%%}
|
|
|
|
# ===========================================================================
|
|
#
|
|
# Core
|
|
#
|
|
# ===========================================================================
|
|
|
|
## Returns the current wall-clock time.
|
|
##
|
|
## In general, you should use :zeek:id:`network_time` instead
|
|
## unless you are using Zeek for non-networking uses (such as general
|
|
## scripting; not particularly recommended), because otherwise your script
|
|
## may behave very differently on live traffic versus played-back traffic
|
|
## from a save file.
|
|
##
|
|
## Returns: The wall-clock time.
|
|
##
|
|
## .. zeek:see:: network_time
|
|
function current_time%(%): time
|
|
%{
|
|
return zeek::make_intrusive<zeek::TimeVal>(zeek::util::current_time());
|
|
%}
|
|
|
|
## Returns the timestamp of the last packet processed. This function returns
|
|
## the timestamp of the most recently read packet, whether read from a
|
|
## live network interface or from a save file.
|
|
##
|
|
## Returns: The timestamp of the packet processed.
|
|
##
|
|
## .. zeek:see:: current_time
|
|
function network_time%(%): time
|
|
%{
|
|
return zeek::make_intrusive<zeek::TimeVal>(zeek::run_state::network_time);
|
|
%}
|
|
|
|
## Returns a system environment variable.
|
|
##
|
|
## var: The name of the variable whose value to request.
|
|
##
|
|
## Returns: The system environment variable identified by *var*, or an empty
|
|
## string if it is not defined.
|
|
##
|
|
## .. zeek:see:: setenv
|
|
function getenv%(var: string%): string
|
|
%{
|
|
const char* env_val = getenv(var->CheckString());
|
|
if ( ! env_val )
|
|
env_val = ""; // ###
|
|
return zeek::make_intrusive<zeek::StringVal>(env_val);
|
|
%}
|
|
|
|
## Sets a system environment variable.
|
|
##
|
|
## var: The name of the variable.
|
|
##
|
|
## val: The (new) value of the variable *var*.
|
|
##
|
|
## Returns: True on success.
|
|
##
|
|
## .. zeek:see:: getenv
|
|
function setenv%(var: string, val: string%): bool
|
|
%{
|
|
int result = setenv(var->AsString()->CheckString(),
|
|
val->AsString()->CheckString(), 1);
|
|
|
|
if ( result < 0 )
|
|
return zeek::val_mgr->False();
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## Shuts down the Zeek process immediately.
|
|
##
|
|
## code: The exit code to return with.
|
|
##
|
|
## .. zeek:see:: terminate
|
|
function exit%(code: int%): any
|
|
%{
|
|
exit(code);
|
|
return nullptr;
|
|
%}
|
|
|
|
## Gracefully shut down Zeek by terminating outstanding processing.
|
|
##
|
|
## Returns: True after successful termination and false when Zeek is still in
|
|
## the process of shutting down.
|
|
##
|
|
## .. zeek:see:: exit zeek_is_terminating
|
|
function terminate%(%): bool
|
|
%{
|
|
if ( zeek::run_state::terminating )
|
|
return zeek::val_mgr->False();
|
|
|
|
zeek::util::detail::terminate_processing();
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
%%{
|
|
// Turns the table into environment variables (if 'set' is true) or removes
|
|
// all environment variables previously generated from this table (if 'set'
|
|
// is false).
|
|
static bool prepare_environment(zeek::TableVal* tbl, bool set)
|
|
{
|
|
auto idxs = tbl->ToPureListVal();
|
|
|
|
for ( int i = 0; i < idxs->Length(); ++i )
|
|
{
|
|
const auto& key = idxs->Idx(i);
|
|
const auto& val = tbl->Find(key);
|
|
|
|
if ( key->GetType()->Tag() != zeek::TYPE_STRING ||
|
|
val->GetType()->Tag() != zeek::TYPE_STRING )
|
|
{
|
|
zeek::emit_builtin_error("system_env() needs a table[string] of string");
|
|
return false;
|
|
}
|
|
|
|
char* tmp = zeek::util::copy_string(key->AsString()->CheckString());
|
|
zeek::util::to_upper(tmp);
|
|
std::string var1 = zeek::util::fmt("ZEEK_ARG_%s", tmp);
|
|
std::string var2 = zeek::util::fmt("BRO_ARG_%s", tmp); // legacy support
|
|
delete [] tmp;
|
|
|
|
if ( set )
|
|
{
|
|
setenv(var1.data(), val->AsString()->CheckString(), 1);
|
|
setenv(var2.data(), val->AsString()->CheckString(), 1);
|
|
}
|
|
else
|
|
{
|
|
unsetenv(var1.data());
|
|
unsetenv(var2.data());
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static int do_system(const char* s)
|
|
{
|
|
const char* system_fmt = "(%s) 1>&2 &"; // output to stderr
|
|
char* cmd = new char[strlen(system_fmt) + strlen(s) + 1];
|
|
|
|
sprintf(cmd, system_fmt, s);
|
|
int status = system(cmd);
|
|
delete [] cmd;
|
|
|
|
return status;
|
|
}
|
|
%%}
|
|
|
|
## Invokes a command via the ``system`` function of the OS.
|
|
## The command runs in the background with ``stdout`` redirecting to
|
|
## ``stderr``. Here is a usage example:
|
|
## ``system(fmt("rm %s", safe_shell_quote(sniffed_data)));``
|
|
##
|
|
## str: The command to execute.
|
|
##
|
|
## Returns: The return value from the OS ``system`` function.
|
|
##
|
|
## .. zeek:see:: system_env safe_shell_quote piped_exec
|
|
##
|
|
## .. note::
|
|
##
|
|
## Note that this corresponds to the status of backgrounding the
|
|
## given command, not to the exit status of the command itself. A
|
|
## value of 127 corresponds to a failure to execute ``sh``, and -1
|
|
## to an internal system failure.
|
|
function system%(str: string%): int
|
|
%{
|
|
int result = do_system(str->CheckString());
|
|
return zeek::val_mgr->Int(result);
|
|
%}
|
|
|
|
## Invokes a command via the ``system`` function of the OS with a prepared
|
|
## environment. The function is essentially the same as :zeek:id:`system`,
|
|
## but changes the environment before invoking the command.
|
|
##
|
|
## str: The command to execute.
|
|
##
|
|
## env: A :zeek:type:`table` with the environment variables in the form
|
|
## of key-value pairs. Each specified environment variable name
|
|
## will be automatically prepended with ``ZEEK_ARG_``.
|
|
##
|
|
## Returns: The return value from the OS ``system`` function.
|
|
##
|
|
## .. zeek:see:: system safe_shell_quote piped_exec
|
|
function system_env%(str: string, env: table_string_of_string%): int
|
|
%{
|
|
if ( env->GetType()->Tag() != zeek::TYPE_TABLE )
|
|
{
|
|
zeek::emit_builtin_error("system_env() requires a table argument");
|
|
return zeek::val_mgr->Int(-1);
|
|
}
|
|
|
|
if ( ! prepare_environment(env->AsTableVal(), true) )
|
|
return zeek::val_mgr->Int(-1);
|
|
|
|
int result = do_system(str->CheckString());
|
|
|
|
prepare_environment(env->AsTableVal(), false);
|
|
|
|
return zeek::val_mgr->Int(result);
|
|
%}
|
|
|
|
## Opens a program with ``popen`` and writes a given string to the returned
|
|
## stream to send it to the opened process's stdin.
|
|
##
|
|
## program: The program to execute.
|
|
##
|
|
## to_write: Data to pipe to the opened program's process via ``stdin``.
|
|
##
|
|
## Returns: True on success.
|
|
##
|
|
## .. zeek:see:: system system_env
|
|
function piped_exec%(program: string, to_write: string%): bool
|
|
%{
|
|
const char* prog = program->CheckString();
|
|
|
|
FILE* f = popen(prog, "w");
|
|
if ( ! f )
|
|
{
|
|
zeek::reporter->Error("Failed to popen %s", prog);
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
const u_char* input_data = to_write->Bytes();
|
|
int input_data_len = to_write->Len();
|
|
|
|
int bytes_written = fwrite(input_data, 1, input_data_len, f);
|
|
|
|
pclose(f);
|
|
|
|
if ( bytes_written != input_data_len )
|
|
{
|
|
zeek::reporter->Error("Failed to write all given data to %s", prog);
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
%%{
|
|
#include "zeek/OpaqueVal.h"
|
|
%%}
|
|
|
|
## Computes the MD5 hash value of the provided list of arguments.
|
|
##
|
|
## Returns: The MD5 hash value of the concatenated arguments.
|
|
##
|
|
## .. zeek:see:: md5_hmac md5_hash_init md5_hash_update md5_hash_finish
|
|
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
|
|
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
|
|
##
|
|
## .. note::
|
|
##
|
|
## This function performs a one-shot computation of its arguments.
|
|
## For incremental hash computation, see :zeek:id:`md5_hash_init` and
|
|
## friends.
|
|
function md5_hash%(...%): string
|
|
%{
|
|
unsigned char digest[MD5_DIGEST_LENGTH];
|
|
MD5Val::digest(@ARG@, digest);
|
|
return zeek::make_intrusive<zeek::StringVal>(zeek::detail::md5_digest_print(digest));
|
|
%}
|
|
|
|
## Computes the SHA1 hash value of the provided list of arguments.
|
|
##
|
|
## Returns: The SHA1 hash value of the concatenated arguments.
|
|
##
|
|
## .. zeek:see:: md5_hash md5_hmac md5_hash_init md5_hash_update md5_hash_finish
|
|
## sha1_hash_init sha1_hash_update sha1_hash_finish
|
|
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
|
|
##
|
|
## .. note::
|
|
##
|
|
## This function performs a one-shot computation of its arguments.
|
|
## For incremental hash computation, see :zeek:id:`sha1_hash_init` and
|
|
## friends.
|
|
function sha1_hash%(...%): string
|
|
%{
|
|
unsigned char digest[SHA_DIGEST_LENGTH];
|
|
SHA1Val::digest(@ARG@, digest);
|
|
return zeek::make_intrusive<zeek::StringVal>(zeek::detail::sha1_digest_print(digest));
|
|
%}
|
|
|
|
## Computes the SHA256 hash value of the provided list of arguments.
|
|
##
|
|
## Returns: The SHA256 hash value of the concatenated arguments.
|
|
##
|
|
## .. zeek:see:: md5_hash md5_hmac md5_hash_init md5_hash_update md5_hash_finish
|
|
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
|
|
## sha256_hash_init sha256_hash_update sha256_hash_finish
|
|
##
|
|
## .. note::
|
|
##
|
|
## This function performs a one-shot computation of its arguments.
|
|
## For incremental hash computation, see :zeek:id:`sha256_hash_init` and
|
|
## friends.
|
|
function sha256_hash%(...%): string
|
|
%{
|
|
unsigned char digest[SHA256_DIGEST_LENGTH];
|
|
SHA256Val::digest(@ARG@, digest);
|
|
return zeek::make_intrusive<zeek::StringVal>(zeek::detail::sha256_digest_print(digest));
|
|
%}
|
|
|
|
## Computes an HMAC-MD5 hash value of the provided list of arguments. The HMAC
|
|
## secret key is generated from available entropy when Zeek starts up, or it can
|
|
## be specified for repeatability using the ``-K`` command line flag.
|
|
##
|
|
## Returns: The HMAC-MD5 hash value of the concatenated arguments.
|
|
##
|
|
## .. zeek:see:: md5_hash md5_hash_init md5_hash_update md5_hash_finish
|
|
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
|
|
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
|
|
function md5_hmac%(...%): string
|
|
%{
|
|
unsigned char hmac[MD5_DIGEST_LENGTH];
|
|
MD5Val::hmac(@ARG@, zeek::detail::KeyedHash::shared_hmac_md5_key, hmac);
|
|
return zeek::make_intrusive<zeek::StringVal>(zeek::detail::md5_digest_print(hmac));
|
|
%}
|
|
|
|
## Constructs an MD5 handle to enable incremental hash computation. You can
|
|
## feed data to the returned opaque value with :zeek:id:`md5_hash_update` and
|
|
## eventually need to call :zeek:id:`md5_hash_finish` to finish the computation
|
|
## and get the hash digest.
|
|
##
|
|
## For example, when computing incremental MD5 values of transferred files in
|
|
## multiple concurrent HTTP connections, one keeps an optional handle in the
|
|
## HTTP session record. Then, one would call
|
|
## ``c$http$md5_handle = md5_hash_init()`` once before invoking
|
|
## ``md5_hash_update(c$http$md5_handle, some_more_data)`` in the
|
|
## :zeek:id:`http_entity_data` event handler. When all data has arrived, a call
|
|
## to :zeek:id:`md5_hash_finish` returns the final hash value.
|
|
##
|
|
## Returns: The opaque handle associated with this hash computation.
|
|
##
|
|
## .. zeek:see:: md5_hmac md5_hash md5_hash_update md5_hash_finish
|
|
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
|
|
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
|
|
function md5_hash_init%(%): opaque of md5
|
|
%{
|
|
auto digest = zeek::make_intrusive<zeek::MD5Val>();
|
|
digest->Init();
|
|
return digest;
|
|
%}
|
|
|
|
## Constructs an SHA1 handle to enable incremental hash computation. You can
|
|
## feed data to the returned opaque value with :zeek:id:`sha1_hash_update` and
|
|
## finally need to call :zeek:id:`sha1_hash_finish` to finish the computation
|
|
## and get the hash digest.
|
|
##
|
|
## For example, when computing incremental SHA1 values of transferred files in
|
|
## multiple concurrent HTTP connections, one keeps an optional handle in the
|
|
## HTTP session record. Then, one would call
|
|
## ``c$http$sha1_handle = sha1_hash_init()`` once before invoking
|
|
## ``sha1_hash_update(c$http$sha1_handle, some_more_data)`` in the
|
|
## :zeek:id:`http_entity_data` event handler. When all data has arrived, a call
|
|
## to :zeek:id:`sha1_hash_finish` returns the final hash value.
|
|
##
|
|
## Returns: The opaque handle associated with this hash computation.
|
|
##
|
|
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
|
|
## sha1_hash sha1_hash_update sha1_hash_finish
|
|
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
|
|
function sha1_hash_init%(%): opaque of sha1
|
|
%{
|
|
auto digest = zeek::make_intrusive<zeek::SHA1Val>();
|
|
digest->Init();
|
|
return digest;
|
|
%}
|
|
|
|
## Constructs an SHA256 handle to enable incremental hash computation. You can
|
|
## feed data to the returned opaque value with :zeek:id:`sha256_hash_update` and
|
|
## finally need to call :zeek:id:`sha256_hash_finish` to finish the computation
|
|
## and get the hash digest.
|
|
##
|
|
## For example, when computing incremental SHA256 values of transferred files in
|
|
## multiple concurrent HTTP connections, one keeps an optional handle in the
|
|
## HTTP session record. Then, one would call
|
|
## ``c$http$sha256_handle = sha256_hash_init()`` once before invoking
|
|
## ``sha256_hash_update(c$http$sha256_handle, some_more_data)`` in the
|
|
## :zeek:id:`http_entity_data` event handler. When all data has arrived, a call
|
|
## to :zeek:id:`sha256_hash_finish` returns the final hash value.
|
|
##
|
|
## Returns: The opaque handle associated with this hash computation.
|
|
##
|
|
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
|
|
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
|
|
## sha256_hash sha256_hash_update sha256_hash_finish
|
|
function sha256_hash_init%(%): opaque of sha256
|
|
%{
|
|
auto digest = zeek::make_intrusive<zeek::SHA256Val>();
|
|
digest->Init();
|
|
return digest;
|
|
%}
|
|
|
|
## Updates the MD5 value associated with a given index. It is required to
|
|
## call :zeek:id:`md5_hash_init` once before calling this
|
|
## function.
|
|
##
|
|
## handle: The opaque handle associated with this hash computation.
|
|
##
|
|
## data: The data to add to the hash computation.
|
|
##
|
|
## Returns: True on success.
|
|
##
|
|
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_finish
|
|
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
|
|
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
|
|
function md5_hash_update%(handle: opaque of md5, data: string%): bool
|
|
%{
|
|
bool rc = static_cast<HashVal*>(handle)->Feed(data->Bytes(), data->Len());
|
|
return zeek::val_mgr->Bool(rc);
|
|
%}
|
|
|
|
## Updates the SHA1 value associated with a given index. It is required to
|
|
## call :zeek:id:`sha1_hash_init` once before calling this
|
|
## function.
|
|
##
|
|
## handle: The opaque handle associated with this hash computation.
|
|
##
|
|
## data: The data to add to the hash computation.
|
|
##
|
|
## Returns: True on success.
|
|
##
|
|
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
|
|
## sha1_hash sha1_hash_init sha1_hash_finish
|
|
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
|
|
function sha1_hash_update%(handle: opaque of sha1, data: string%): bool
|
|
%{
|
|
bool rc = static_cast<HashVal*>(handle)->Feed(data->Bytes(), data->Len());
|
|
return zeek::val_mgr->Bool(rc);
|
|
%}
|
|
|
|
## Updates the SHA256 value associated with a given index. It is required to
|
|
## call :zeek:id:`sha256_hash_init` once before calling this
|
|
## function.
|
|
##
|
|
## handle: The opaque handle associated with this hash computation.
|
|
##
|
|
## data: The data to add to the hash computation.
|
|
##
|
|
## Returns: True on success.
|
|
##
|
|
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
|
|
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
|
|
## sha256_hash sha256_hash_init sha256_hash_finish
|
|
function sha256_hash_update%(handle: opaque of sha256, data: string%): bool
|
|
%{
|
|
bool rc = static_cast<HashVal*>(handle)->Feed(data->Bytes(), data->Len());
|
|
return zeek::val_mgr->Bool(rc);
|
|
%}
|
|
|
|
## Returns the final MD5 digest of an incremental hash computation.
|
|
##
|
|
## handle: The opaque handle associated with this hash computation.
|
|
##
|
|
## Returns: The hash value associated with the computation of *handle*.
|
|
##
|
|
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_update
|
|
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
|
|
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
|
|
function md5_hash_finish%(handle: opaque of md5%): string
|
|
%{
|
|
return static_cast<HashVal*>(handle)->Get();
|
|
%}
|
|
|
|
## Returns the final SHA1 digest of an incremental hash computation.
|
|
##
|
|
## handle: The opaque handle associated with this hash computation.
|
|
##
|
|
## Returns: The hash value associated with the computation of *handle*.
|
|
##
|
|
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
|
|
## sha1_hash sha1_hash_init sha1_hash_update
|
|
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
|
|
function sha1_hash_finish%(handle: opaque of sha1%): string
|
|
%{
|
|
return static_cast<HashVal*>(handle)->Get();
|
|
%}
|
|
|
|
## Returns the final SHA256 digest of an incremental hash computation.
|
|
##
|
|
## handle: The opaque handle associated with this hash computation.
|
|
##
|
|
## Returns: The hash value associated with the computation of *handle*.
|
|
##
|
|
## .. zeek:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
|
|
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
|
|
## sha256_hash sha256_hash_init sha256_hash_update
|
|
function sha256_hash_finish%(handle: opaque of sha256%): string
|
|
%{
|
|
return static_cast<HashVal*>(handle)->Get();
|
|
%}
|
|
|
|
## Initializes and returns a new paraglob.
|
|
##
|
|
## v: Vector of patterns to initialize the paraglob with.
|
|
##
|
|
## Returns: A new, compiled, paraglob with the patterns in *v*
|
|
##
|
|
## .. zeek:see::paraglob_match paraglob_equals paraglob_add
|
|
function paraglob_init%(v: any%) : opaque of paraglob
|
|
%{
|
|
if ( v->GetType()->Tag() != zeek::TYPE_VECTOR ||
|
|
v->GetType()->Yield()->Tag() != zeek::TYPE_STRING )
|
|
{
|
|
// reporter->Error will throw an exception.
|
|
zeek::reporter->Error("paraglob requires a vector of strings for initialization.");
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<std::string> patterns;
|
|
VectorVal* vv = v->AsVectorVal();
|
|
for ( unsigned int i = 0; i < vv->Size(); ++i )
|
|
{
|
|
auto s = vv->StringAt(i);
|
|
patterns.push_back(std::string(reinterpret_cast<const char*>(s->Bytes()), s->Len()));
|
|
}
|
|
|
|
try
|
|
{
|
|
std::unique_ptr<paraglob::Paraglob> p (new paraglob::Paraglob(patterns));
|
|
return zeek::make_intrusive<zeek::ParaglobVal>(std::move(p));
|
|
}
|
|
// Thrown if paraglob fails to add a pattern.
|
|
catch (const paraglob::add_error& e)
|
|
{
|
|
zeek::reporter->Error("Paraglob failed to add pattern: %s", e.what());
|
|
return nullptr;
|
|
}
|
|
%}
|
|
|
|
## Gets all the patterns inside the handle associated with an input string.
|
|
##
|
|
## handle: A compiled paraglob.
|
|
##
|
|
## match: string to match against the paraglob.
|
|
##
|
|
## Returns: A vector of strings matching the input string.
|
|
##
|
|
## ## .. zeek:see::paraglob_add paraglob_equals paraglob_init
|
|
function paraglob_match%(handle: opaque of paraglob, match: string%): string_vec
|
|
%{
|
|
return static_cast<ParaglobVal*>(handle)->Get(match);
|
|
%}
|
|
|
|
## Compares two paraglobs for equality.
|
|
##
|
|
## p_one: A compiled paraglob.
|
|
##
|
|
## p_two: A compiled paraglob.
|
|
##
|
|
## Returns: True if both paraglobs contain the same patterns, false otherwise.
|
|
##
|
|
## ## .. zeek:see::paraglob_add paraglob_match paraglob_init
|
|
function paraglob_equals%(p_one: opaque of paraglob, p_two: opaque of paraglob%) : bool
|
|
%{
|
|
return zeek::val_mgr->Bool(
|
|
*(static_cast<ParaglobVal*>(p_one)) == *(static_cast<ParaglobVal*>(p_two))
|
|
);
|
|
%}
|
|
|
|
## Returns 32-bit digest of arbitrary input values using FNV-1a hash algorithm.
|
|
## See `<https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function>`_.
|
|
##
|
|
## input: The desired input value to hash.
|
|
##
|
|
## Returns: The hashed value.
|
|
##
|
|
## .. zeek:see:: hrw_weight
|
|
function fnv1a32%(input: any%): count
|
|
%{
|
|
zeek::ODesc desc(DESC_BINARY);
|
|
input->Describe(&desc);
|
|
auto bytes = desc.Bytes();
|
|
|
|
uint32_t offset32 = 2166136261;
|
|
uint32_t prime32 = 16777619;
|
|
uint32_t rval = offset32;
|
|
|
|
for ( auto i = 0; i < desc.Len(); ++i )
|
|
{
|
|
rval ^= (uint32_t) bytes[i];
|
|
rval *= prime32;
|
|
}
|
|
|
|
return zeek::val_mgr->Count(rval);
|
|
%}
|
|
|
|
## Calculates a weight value for use in a Rendezvous Hashing algorithm.
|
|
## See `<https://en.wikipedia.org/wiki/Rendezvous_hashing>`_.
|
|
## The weight function used is the one recommended in the original
|
|
## paper: `<http://www.eecs.umich.edu/techreports/cse/96/CSE-TR-316-96.pdf>`_.
|
|
##
|
|
## key_digest: A 32-bit digest of a key. E.g. use :zeek:see:`fnv1a32` to
|
|
## produce this.
|
|
##
|
|
## site_id: A 32-bit site/node identifier.
|
|
##
|
|
## Returns: The weight value for the key/site pair.
|
|
##
|
|
## .. zeek:see:: fnv1a32
|
|
function hrw_weight%(key_digest: count, site_id: count%): count
|
|
%{
|
|
uint32_t d = key_digest;
|
|
d &= 0x7fffffff; // 31-bit digest
|
|
uint32_t si = site_id;
|
|
auto a = 1103515245;
|
|
auto b = 12345;
|
|
auto m = 2147483648; // 2**31
|
|
|
|
uint32_t rval = (a * ((a * si + b) ^ d) + b) % m;
|
|
|
|
return zeek::val_mgr->Count(rval);
|
|
%}
|
|
|
|
## Generates a random number.
|
|
##
|
|
## max: The maximum value of the random number.
|
|
##
|
|
## Returns: a random positive integer in the interval *[0, max)*.
|
|
##
|
|
## .. zeek:see:: srand
|
|
##
|
|
## .. note::
|
|
##
|
|
## This function is a wrapper about the function ``random``
|
|
## provided by the OS.
|
|
function rand%(max: count%): count
|
|
%{
|
|
auto result = bro_uint_t(double(max) * double(zeek::util::detail::random_number()) / (zeek::util::detail::max_random() + 1.0));
|
|
return zeek::val_mgr->Count(result);
|
|
%}
|
|
|
|
## Sets the seed for subsequent :zeek:id:`rand` calls.
|
|
##
|
|
## seed: The seed for the PRNG.
|
|
##
|
|
## .. zeek:see:: rand
|
|
##
|
|
## .. note::
|
|
##
|
|
## This function is a wrapper about the function ``srandom``
|
|
## provided by the OS.
|
|
function srand%(seed: count%): any
|
|
%{
|
|
zeek::util::detail::seed_random(seed);
|
|
return nullptr;
|
|
%}
|
|
|
|
%%{
|
|
#include <syslog.h>
|
|
%%}
|
|
|
|
## Send a string to syslog.
|
|
##
|
|
## s: The string to log via syslog
|
|
function syslog%(s: string%): any
|
|
%{
|
|
zeek::reporter->Syslog("%s", s->CheckString());
|
|
return nullptr;
|
|
%}
|
|
|
|
## Determines the MIME type of a piece of data using Zeek's file magic
|
|
## signatures.
|
|
##
|
|
## data: The data to find the MIME type for.
|
|
##
|
|
## return_mime: Deprecated argument; does nothing, except emit a warning
|
|
## when false.
|
|
##
|
|
## Returns: The MIME type of *data*, or "<unknown>" if there was an error
|
|
## or no match. This is the strongest signature match.
|
|
##
|
|
## .. zeek:see:: file_magic
|
|
function identify_data%(data: string, return_mime: bool &default=T%): string
|
|
%{
|
|
if ( ! return_mime )
|
|
zeek::reporter->Warning("identify_data() builtin-function only returns MIME types, but verbose file info requested");
|
|
|
|
string strongest_match = zeek::file_mgr->DetectMIME(data->Bytes(), data->Len());
|
|
|
|
if ( strongest_match.empty() )
|
|
return zeek::make_intrusive<zeek::StringVal>("<unknown>");
|
|
|
|
return zeek::make_intrusive<zeek::StringVal>(strongest_match);
|
|
%}
|
|
|
|
## Determines the MIME type of a piece of data using Zeek's file magic
|
|
## signatures.
|
|
##
|
|
## data: The data for which to find matching MIME types.
|
|
##
|
|
## Returns: All matching signatures, in order of strength.
|
|
##
|
|
## .. zeek:see:: identify_data
|
|
function file_magic%(data: string%): mime_matches
|
|
%{
|
|
zeek::detail::RuleMatcher::MIME_Matches matches;
|
|
zeek::file_mgr->DetectMIME(data->Bytes(), data->Len(), &matches);
|
|
return file_analysis::GenMIMEMatchesVal(matches);
|
|
%}
|
|
|
|
## Performs an entropy test on the given data.
|
|
## See http://www.fourmilab.ch/random.
|
|
##
|
|
## data: The data to compute the entropy for.
|
|
##
|
|
## Returns: The result of the entropy test, which contains the following
|
|
## fields.
|
|
##
|
|
## - ``entropy``: The information density expressed as a number of
|
|
## bits per character.
|
|
##
|
|
## - ``chi_square``: The chi-square test value expressed as an
|
|
## absolute number and a percentage which indicates how
|
|
## frequently a truly random sequence would exceed the value
|
|
## calculated, i.e., the degree to which the sequence tested is
|
|
## suspected of being non-random.
|
|
##
|
|
## If the percentage is greater than 99% or less than 1%, the
|
|
## sequence is almost certainly not random. If the percentage is
|
|
## between 99% and 95% or between 1% and 5%, the sequence is
|
|
## suspect. Percentages between 90\% and 95\% and 5\% and 10\%
|
|
## indicate the sequence is "almost suspect."
|
|
##
|
|
## - ``mean``: The arithmetic mean of all the bytes. If the data
|
|
## are close to random, it should be around 127.5.
|
|
##
|
|
## - ``monte_carlo_pi``: Each successive sequence of six bytes is
|
|
## used as 24-bit *x* and *y* coordinates within a square. If
|
|
## the distance of the randomly-generated point is less than the
|
|
## radius of a circle inscribed within the square, the six-byte
|
|
## sequence is considered a "hit." The percentage of hits can
|
|
## be used to calculate the value of pi. For very large streams
|
|
## the value will approach the correct value of pi if the
|
|
## sequence is close to random.
|
|
##
|
|
## - ``serial_correlation``: This quantity measures the extent to
|
|
## which each byte in the file depends upon the previous byte.
|
|
## For random sequences this value will be close to zero.
|
|
##
|
|
## .. zeek:see:: entropy_test_init entropy_test_add entropy_test_finish
|
|
function find_entropy%(data: string%): entropy_test_result
|
|
%{
|
|
double montepi, scc, ent, mean, chisq;
|
|
montepi = scc = ent = mean = chisq = 0.0;
|
|
EntropyVal e;
|
|
e.Feed(data->Bytes(), data->Len());
|
|
e.Get(&ent, &chisq, &mean, &montepi, &scc);
|
|
|
|
static auto entropy_test_result = zeek::id::find_type<zeek::RecordType>("entropy_test_result");
|
|
auto ent_result = zeek::make_intrusive<zeek::RecordVal>(entropy_test_result);
|
|
ent_result->Assign(0, ent);
|
|
ent_result->Assign(1, chisq);
|
|
ent_result->Assign(2, mean);
|
|
ent_result->Assign(3, montepi);
|
|
ent_result->Assign(4, scc);
|
|
return ent_result;
|
|
%}
|
|
|
|
## Initializes data structures for incremental entropy calculation.
|
|
##
|
|
## Returns: An opaque handle to be used in subsequent operations.
|
|
##
|
|
## .. zeek:see:: find_entropy entropy_test_add entropy_test_finish
|
|
function entropy_test_init%(%): opaque of entropy
|
|
%{
|
|
return zeek::make_intrusive<zeek::EntropyVal>();
|
|
%}
|
|
|
|
## Adds data to an incremental entropy calculation.
|
|
##
|
|
## handle: The opaque handle representing the entropy calculation state.
|
|
##
|
|
## data: The data to add to the entropy calculation.
|
|
##
|
|
## Returns: True on success.
|
|
##
|
|
## .. zeek:see:: find_entropy entropy_test_add entropy_test_finish
|
|
function entropy_test_add%(handle: opaque of entropy, data: string%): bool
|
|
%{
|
|
bool status = static_cast<EntropyVal*>(handle)->Feed(data->Bytes(),
|
|
data->Len());
|
|
return zeek::val_mgr->Bool(status);
|
|
%}
|
|
|
|
## Finishes an incremental entropy calculation. Before using this function,
|
|
## one needs to obtain an opaque handle with :zeek:id:`entropy_test_init` and
|
|
## add data to it via :zeek:id:`entropy_test_add`.
|
|
##
|
|
## handle: The opaque handle representing the entropy calculation state.
|
|
##
|
|
## Returns: The result of the entropy test. See :zeek:id:`find_entropy` for a
|
|
## description of the individual components.
|
|
##
|
|
## .. zeek:see:: find_entropy entropy_test_init entropy_test_add
|
|
function entropy_test_finish%(handle: opaque of entropy%): entropy_test_result
|
|
%{
|
|
double montepi, scc, ent, mean, chisq;
|
|
montepi = scc = ent = mean = chisq = 0.0;
|
|
static_cast<EntropyVal*>(handle)->Get(&ent, &chisq, &mean, &montepi, &scc);
|
|
|
|
static auto entropy_test_result = zeek::id::find_type<zeek::RecordType>("entropy_test_result");
|
|
auto ent_result = zeek::make_intrusive<zeek::RecordVal>(entropy_test_result);
|
|
ent_result->Assign(0, ent);
|
|
ent_result->Assign(1, chisq);
|
|
ent_result->Assign(2, mean);
|
|
ent_result->Assign(3, montepi);
|
|
ent_result->Assign(4, scc);
|
|
return ent_result;
|
|
%}
|
|
|
|
## Creates an identifier that is unique with high probability.
|
|
##
|
|
## prefix: A custom string prepended to the result.
|
|
##
|
|
## Returns: A string identifier that is unique.
|
|
##
|
|
## .. zeek:see:: unique_id_from
|
|
function unique_id%(prefix: string%) : string
|
|
%{
|
|
char tmp[20];
|
|
uint64_t uid = zeek::util::calculate_unique_id(UID_POOL_DEFAULT_SCRIPT);
|
|
return zeek::make_intrusive<zeek::StringVal>(
|
|
zeek::util::uitoa_n(uid, tmp, sizeof(tmp), 62, prefix->CheckString()));
|
|
%}
|
|
|
|
## Creates an identifier that is unique with high probability.
|
|
##
|
|
## pool: A seed for determinism.
|
|
##
|
|
## prefix: A custom string prepended to the result.
|
|
##
|
|
## Returns: A string identifier that is unique.
|
|
##
|
|
## .. zeek:see:: unique_id
|
|
function unique_id_from%(pool: int, prefix: string%) : string
|
|
%{
|
|
pool += UID_POOL_CUSTOM_SCRIPT; // Make sure we don't conflict with internal pool.
|
|
|
|
char tmp[20];
|
|
uint64_t uid = zeek::util::calculate_unique_id(pool);
|
|
return zeek::make_intrusive<zeek::StringVal>(
|
|
zeek::util::uitoa_n(uid, tmp, sizeof(tmp), 62, prefix->CheckString()));
|
|
%}
|
|
|
|
# ===========================================================================
|
|
#
|
|
# Generic Programming
|
|
#
|
|
# ===========================================================================
|
|
|
|
## Removes all elements from a set or table.
|
|
##
|
|
## v: The set or table
|
|
function clear_table%(v: any%): any
|
|
%{
|
|
if ( v->GetType()->Tag() == zeek::TYPE_TABLE )
|
|
v->AsTableVal()->RemoveAll();
|
|
else
|
|
zeek::emit_builtin_error("clear_table() requires a table/set argument");
|
|
|
|
return nullptr;
|
|
%}
|
|
|
|
## Gets all subnets that contain a given subnet from a set/table[subnet].
|
|
##
|
|
## search: the subnet to search for.
|
|
##
|
|
## t: the set[subnet] or table[subnet].
|
|
##
|
|
## Returns: All the keys of the set or table that cover the subnet searched for.
|
|
function matching_subnets%(search: subnet, t: any%): subnet_vec
|
|
%{
|
|
if ( t->GetType()->Tag() != zeek::TYPE_TABLE || ! t->GetType()->AsTableType()->IsSubNetIndex() )
|
|
{
|
|
zeek::reporter->Error("matching_subnets needs to be called on a set[subnet]/table[subnet].");
|
|
return nullptr;
|
|
}
|
|
|
|
return t->AsTableVal()->LookupSubnets(search);
|
|
%}
|
|
|
|
## For a set[subnet]/table[subnet], create a new table that contains all entries
|
|
## that contain a given subnet.
|
|
##
|
|
## search: the subnet to search for.
|
|
##
|
|
## t: the set[subnet] or table[subnet].
|
|
##
|
|
## Returns: A new table that contains all the entries that cover the subnet searched for.
|
|
function filter_subnet_table%(search: subnet, t: any%): any
|
|
%{
|
|
if ( t->GetType()->Tag() != zeek::TYPE_TABLE || ! t->GetType()->AsTableType()->IsSubNetIndex() )
|
|
{
|
|
zeek::reporter->Error("filter_subnet_table needs to be called on a set[subnet]/table[subnet].");
|
|
return nullptr;
|
|
}
|
|
|
|
return t->AsTableVal()->LookupSubnetValues(search);
|
|
%}
|
|
|
|
## Checks if a specific subnet is a member of a set/table[subnet].
|
|
## In contrast to the ``in`` operator, this performs an exact match, not
|
|
## a longest prefix match.
|
|
##
|
|
## search: the subnet to search for.
|
|
##
|
|
## t: the set[subnet] or table[subnet].
|
|
##
|
|
## Returns: True if the exact subnet is a member, false otherwise.
|
|
function check_subnet%(search: subnet, t: any%): bool
|
|
%{
|
|
if ( t->GetType()->Tag() != zeek::TYPE_TABLE || ! t->GetType()->AsTableType()->IsSubNetIndex() )
|
|
{
|
|
zeek::reporter->Error("check_subnet needs to be called on a set[subnet]/table[subnet].");
|
|
return nullptr;
|
|
}
|
|
|
|
const zeek::detail::PrefixTable* pt = t->AsTableVal()->Subnets();
|
|
if ( ! pt )
|
|
{
|
|
zeek::reporter->Error("check_subnet encountered nonexisting prefix table.");
|
|
return nullptr;
|
|
}
|
|
|
|
void* res = pt->Lookup(search, true);
|
|
|
|
return zeek::val_mgr->Bool(res != nullptr);
|
|
%}
|
|
|
|
## Checks whether two objects reference the same internal object. This function
|
|
## uses equality comparison of C++ raw pointer values to determine if the two
|
|
## objects are the same.
|
|
##
|
|
## o1: The first object.
|
|
##
|
|
## o2: The second object.
|
|
##
|
|
## Returns: True if *o1* and *o2* are equal.
|
|
function same_object%(o1: any, o2: any%): bool
|
|
%{
|
|
return zeek::val_mgr->Bool(o1 == o2);
|
|
%}
|
|
|
|
## Returns the number of bytes that a value occupies in memory.
|
|
##
|
|
## v: The value
|
|
##
|
|
## Returns: The number of bytes that *v* occupies.
|
|
function val_size%(v: any%): count &deprecated="Remove in v5.1. MemoryAllocation() is deprecated and will be removed."
|
|
%{
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
return zeek::val_mgr->Count(v->MemoryAllocation());
|
|
#pragma GCC diagnostic pop
|
|
%}
|
|
|
|
## Resizes a vector.
|
|
##
|
|
## aggr: The vector instance.
|
|
##
|
|
## newsize: The new size of *aggr*.
|
|
##
|
|
## Returns: The old size of *aggr*, or 0 if *aggr* is not a :zeek:type:`vector`.
|
|
function resize%(aggr: any, newsize: count%) : count
|
|
%{
|
|
if ( aggr->GetType()->Tag() != zeek::TYPE_VECTOR )
|
|
{
|
|
zeek::emit_builtin_error("resize() operates on vectors");
|
|
return nullptr;
|
|
}
|
|
|
|
return zeek::val_mgr->Count(aggr->AsVectorVal()->Resize(newsize));
|
|
%}
|
|
|
|
## Tests whether a boolean vector (``vector of bool``) has *any* true
|
|
## element.
|
|
##
|
|
## v: The boolean vector instance.
|
|
##
|
|
## Returns: True if any element in *v* is true.
|
|
##
|
|
## .. zeek:see:: all_set
|
|
function any_set%(v: any%) : bool
|
|
%{
|
|
if ( v->GetType()->Tag() != zeek::TYPE_VECTOR ||
|
|
v->GetType()->Yield()->Tag() != zeek::TYPE_BOOL )
|
|
{
|
|
zeek::emit_builtin_error("any_set() requires vector of bool");
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
VectorVal* vv = v->AsVectorVal();
|
|
for ( unsigned int i = 0; i < vv->Size(); ++i )
|
|
if ( vv->BoolAt(i) )
|
|
return zeek::val_mgr->True();
|
|
|
|
return zeek::val_mgr->False();
|
|
%}
|
|
|
|
## Tests whether *all* elements of a boolean vector (``vector of bool``) are
|
|
## true.
|
|
##
|
|
## v: The boolean vector instance.
|
|
##
|
|
## Returns: True iff all elements in *v* are true or there are no elements.
|
|
##
|
|
## .. zeek:see:: any_set
|
|
##
|
|
## .. note::
|
|
##
|
|
## Missing elements count as false.
|
|
function all_set%(v: any%) : bool
|
|
%{
|
|
if ( v->GetType()->Tag() != zeek::TYPE_VECTOR ||
|
|
v->GetType()->Yield()->Tag() != zeek::TYPE_BOOL )
|
|
{
|
|
zeek::emit_builtin_error("all_set() requires vector of bool");
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
VectorVal* vv = v->AsVectorVal();
|
|
for ( unsigned int i = 0; i < vv->Size(); ++i )
|
|
if ( ! vv->BoolAt(i) )
|
|
return zeek::val_mgr->False();
|
|
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## Sorts a vector in place. The second argument is a comparison function that
|
|
## takes two arguments: if the vector type is ``vector of T``, then the
|
|
## comparison function must be ``function(a: T, b: T): int``, which returns
|
|
## a value less than zero if ``a < b`` for some type-specific notion of the
|
|
## less-than operator. The comparison function is optional if the type
|
|
## is a numeric type (int, count, double, time, etc.).
|
|
##
|
|
## v: The vector instance to sort.
|
|
##
|
|
## Returns: The vector, sorted from minimum to maximum value. If the vector
|
|
## could not be sorted, then the original vector is returned instead.
|
|
##
|
|
## .. zeek:see:: order
|
|
function sort%(v: any, ...%) : any
|
|
%{
|
|
zeek::ValPtr rval{zeek::NewRef{}, v};
|
|
|
|
if ( v->GetType()->Tag() != zeek::TYPE_VECTOR )
|
|
{
|
|
zeek::emit_builtin_error("sort() requires vector");
|
|
return rval;
|
|
}
|
|
|
|
const auto& elt_type = v->GetType()->Yield();
|
|
zeek::Func* comp = nullptr;
|
|
|
|
if ( @ARG@.size() > 2 )
|
|
{
|
|
zeek::emit_builtin_error("sort() called with extraneous argument");
|
|
return rval;
|
|
}
|
|
|
|
if ( @ARG@.size() == 2 )
|
|
{
|
|
Val* comp_val = @ARG@[1].get();
|
|
if ( ! IsFunc(comp_val->GetType()->Tag()) )
|
|
{
|
|
zeek::emit_builtin_error("second argument to sort() needs to be comparison function");
|
|
return rval;
|
|
}
|
|
|
|
comp = comp_val->AsFunc();
|
|
const auto& comp_type = comp->GetType();
|
|
|
|
if ( comp_type->Yield()->Tag() != zeek::TYPE_INT ||
|
|
! comp_type->ParamList()->AllMatch(elt_type, 0) ||
|
|
comp_type->ParamList()->GetTypes().size() != 2 )
|
|
{
|
|
zeek::emit_builtin_error("invalid comparison function in call to sort()");
|
|
return rval;
|
|
}
|
|
}
|
|
|
|
if ( ! comp && ! IsIntegral(elt_type->Tag()) &&
|
|
elt_type->InternalType() != TYPE_INTERNAL_DOUBLE )
|
|
{
|
|
zeek::emit_builtin_error("comparison function required for sort() with non-numeric types");
|
|
return rval;
|
|
}
|
|
|
|
auto vv = v->As<zeek::VectorVal*>();
|
|
vv->Sort(comp);
|
|
|
|
return rval;
|
|
%}
|
|
|
|
## Returns the order of the elements in a vector according to some
|
|
## comparison function. See :zeek:id:`sort` for details about the comparison
|
|
## function.
|
|
##
|
|
## v: The vector whose order to compute.
|
|
##
|
|
## Returns: A ``vector of count`` with the indices of the ordered elements.
|
|
## For example, the elements of *v* in order are (assuming ``o``
|
|
## is the vector returned by ``order``): v[o[0]], v[o[1]], etc.
|
|
##
|
|
## .. zeek:see:: sort
|
|
function order%(v: any, ...%) : index_vec
|
|
%{
|
|
auto err_v = zeek::make_intrusive<zeek::VectorVal>(zeek::id::index_vec);
|
|
|
|
if ( v->GetType()->Tag() != zeek::TYPE_VECTOR )
|
|
{
|
|
zeek::emit_builtin_error("order() requires vector");
|
|
return err_v;
|
|
}
|
|
|
|
const auto& elt_type = v->GetType()->Yield();
|
|
zeek::Func* comp = nullptr;
|
|
|
|
if ( @ARG@.size() > 2 )
|
|
{
|
|
zeek::emit_builtin_error("order() called with extraneous argument");
|
|
return err_v;
|
|
}
|
|
|
|
if ( @ARG@.size() == 2 )
|
|
{
|
|
Val* comp_val = @ARG@[1].get();
|
|
if ( ! IsFunc(comp_val->GetType()->Tag()) )
|
|
{
|
|
zeek::emit_builtin_error("second argument to order() needs to be comparison function");
|
|
return zeek::ValPtr{zeek::NewRef{}, v};
|
|
}
|
|
|
|
comp = comp_val->AsFunc();
|
|
|
|
const auto& comp_type = comp->GetType();
|
|
if ( comp_type->Yield()->Tag() != zeek::TYPE_INT ||
|
|
! comp_type->ParamList()->AllMatch(elt_type, 0) ||
|
|
comp_type->ParamList()->GetTypes().size() != 2 )
|
|
{
|
|
zeek::emit_builtin_error("invalid comparison function in call to order()");
|
|
return zeek::ValPtr{zeek::NewRef{}, v};
|
|
}
|
|
}
|
|
|
|
if ( ! comp && ! IsIntegral(elt_type->Tag()) &&
|
|
elt_type->InternalType() != TYPE_INTERNAL_DOUBLE )
|
|
{
|
|
zeek::emit_builtin_error("comparison function required for order() with non-numeric types");
|
|
return err_v;
|
|
}
|
|
|
|
auto vv = v->As<zeek::VectorVal*>();
|
|
|
|
return vv->Order(comp);
|
|
%}
|
|
|
|
# ===========================================================================
|
|
#
|
|
# String Processing
|
|
#
|
|
# ===========================================================================
|
|
|
|
## Returns the concatenation of the string representation of its arguments. The
|
|
## arguments can be of any type. For example, ``cat("foo", 3, T)`` returns
|
|
## ``"foo3T"``.
|
|
##
|
|
## Returns: A string concatentation of all arguments.
|
|
function cat%(...%): string
|
|
%{
|
|
zeek::ODesc d;
|
|
d.SetStyle(RAW_STYLE);
|
|
|
|
for ( const auto& a : @ARG@ )
|
|
a->Describe(&d);
|
|
|
|
String* s = new String(1, d.TakeBytes(), d.Len());
|
|
s->SetUseFreeToDelete(true);
|
|
|
|
return zeek::make_intrusive<zeek::StringVal>(s);
|
|
%}
|
|
|
|
## Concatenates all arguments, with a separator placed between each one. This
|
|
## function is similar to :zeek:id:`cat`, but places a separator between each
|
|
## given argument. If any of the variable arguments is an empty string it is
|
|
## replaced by a given default string instead.
|
|
##
|
|
## sep: The separator to place between each argument.
|
|
##
|
|
## def: The default string to use when an argument is the empty string.
|
|
##
|
|
## Returns: A concatenation of all arguments with *sep* between each one and
|
|
## empty strings replaced with *def*.
|
|
##
|
|
## .. zeek:see:: cat string_cat
|
|
function cat_sep%(sep: string, def: string, ...%): string
|
|
%{
|
|
zeek::ODesc d;
|
|
d.SetStyle(RAW_STYLE);
|
|
|
|
int pre_size = 0;
|
|
|
|
for ( auto i = 0u; i < @ARG@.size(); ++i )
|
|
{
|
|
// Skip named parameters.
|
|
if ( i < 2 )
|
|
continue;
|
|
|
|
if ( i > 2 )
|
|
d.Add(sep->CheckString(), 0);
|
|
|
|
Val* v = @ARG@[i].get();
|
|
if ( v->GetType()->Tag() == zeek::TYPE_STRING && ! v->AsString()->Len() )
|
|
v = def;
|
|
|
|
v->Describe(&d);
|
|
}
|
|
|
|
String* s = new String(1, d.TakeBytes(), d.Len());
|
|
s->SetUseFreeToDelete(true);
|
|
|
|
return zeek::make_intrusive<zeek::StringVal>(s);
|
|
%}
|
|
|
|
## Produces a formatted string à la ``printf``. The first argument is the
|
|
## *format string* and specifies how subsequent arguments are converted for
|
|
## output. It is composed of zero or more directives: ordinary characters (not
|
|
## ``%``), which are copied unchanged to the output, and conversion
|
|
## specifications, each of which fetches zero or more subsequent arguments.
|
|
## Conversion specifications begin with ``%`` and the arguments must properly
|
|
## correspond to the specifier. After the ``%``, the following characters
|
|
## may appear in sequence:
|
|
##
|
|
## - ``%``: Literal ``%``
|
|
##
|
|
## - ``-``: Left-align field
|
|
##
|
|
## - ``[0-9]+``: The field width (< 128)
|
|
##
|
|
## - ``.``: Precision of floating point specifiers ``[efg]`` (< 128)
|
|
##
|
|
## - ``[DTdxsefg]``: Format specifier
|
|
##
|
|
## - ``[DT]``: ISO timestamp with microsecond precision
|
|
##
|
|
## - ``d``: Signed/Unsigned integer (using C-style ``%lld``/``%llu``
|
|
## for ``int``/``count``)
|
|
##
|
|
## - ``x``: Unsigned hexadecimal (using C-style ``%llx``);
|
|
## addresses/ports are converted to host-byte order
|
|
##
|
|
## - ``s``: String (byte values less than 32 or greater than 126
|
|
## will be escaped)
|
|
##
|
|
## - ``[efg]``: Double
|
|
##
|
|
## Returns: Returns the formatted string. Given no arguments, :zeek:id:`fmt`
|
|
## returns an empty string. Given no format string or the wrong
|
|
## number of additional arguments for the given format specifier,
|
|
## :zeek:id:`fmt` generates a run-time error.
|
|
##
|
|
## .. zeek:see:: cat cat_sep string_cat
|
|
function fmt%(...%): string
|
|
%{
|
|
if ( @ARGC@ == 0 )
|
|
return zeek::val_mgr->EmptyString();
|
|
|
|
Val* fmt_v = @ARG@[0].get();
|
|
|
|
// Type of fmt_v will be string here, check_built_in_call() in Func.cc
|
|
// checks that.
|
|
|
|
const char* fmt = fmt_v->AsString()->CheckString();
|
|
zeek::ODesc d;
|
|
d.SetStyle(RAW_STYLE);
|
|
|
|
int n = 0;
|
|
|
|
while ( next_fmt(fmt, @ARGS@, &d, n) )
|
|
;
|
|
|
|
if ( n < static_cast<int>(@ARGC@) - 1 )
|
|
{
|
|
zeek::emit_builtin_error("too many arguments for format", fmt_v);
|
|
return zeek::val_mgr->EmptyString();
|
|
}
|
|
|
|
else if ( n >= static_cast<int>(@ARGC@) )
|
|
{
|
|
zeek::emit_builtin_error("too few arguments for format", fmt_v);
|
|
return zeek::val_mgr->EmptyString();
|
|
}
|
|
|
|
String* s = new String(1, d.TakeBytes(), d.Len());
|
|
s->SetUseFreeToDelete(true);
|
|
|
|
return zeek::make_intrusive<zeek::StringVal>(s);
|
|
%}
|
|
|
|
## Renders a sequence of values to a string of bytes and outputs them directly
|
|
## to ``stdout`` with no additional escape sequences added. No additional
|
|
## newline is added to the end either.
|
|
##
|
|
## Returns: Always true.
|
|
##
|
|
## .. zeek:see:: fmt cat cat_sep string_cat to_json
|
|
function print_raw%(...%): bool
|
|
%{
|
|
zeek::ODesc d(DESC_READABLE);
|
|
d.SetStyle(RAW_STYLE);
|
|
describe_vals(@ARG@, &d, 0);
|
|
printf("%.*s", d.Len(), d.Description());
|
|
return zeek::val_mgr->Bool(true);
|
|
%}
|
|
|
|
# ===========================================================================
|
|
#
|
|
# Math
|
|
#
|
|
# ===========================================================================
|
|
|
|
## Computes the greatest integer less than the given :zeek:type:`double` value.
|
|
## For example, ``floor(3.14)`` returns ``3.0``, and ``floor(-3.14)``
|
|
## returns ``-4.0``.
|
|
##
|
|
## d: The :zeek:type:`double` to manipulate.
|
|
##
|
|
## Returns: The next lowest integer of *d* as :zeek:type:`double`.
|
|
##
|
|
## .. zeek:see:: sqrt exp ln log10
|
|
function floor%(d: double%): double
|
|
%{
|
|
return zeek::make_intrusive<zeek::DoubleVal>(floor(d));
|
|
%}
|
|
|
|
## Computes the square root of a :zeek:type:`double`.
|
|
##
|
|
## x: The number to compute the square root of.
|
|
##
|
|
## Returns: The square root of *x*.
|
|
##
|
|
## .. zeek:see:: floor exp ln log10
|
|
function sqrt%(x: double%): double
|
|
%{
|
|
if ( x < 0 )
|
|
{
|
|
zeek::reporter->Error("negative sqrt argument");
|
|
return zeek::make_intrusive<zeek::DoubleVal>(-1.0);
|
|
}
|
|
|
|
return zeek::make_intrusive<zeek::DoubleVal>(sqrt(x));
|
|
%}
|
|
|
|
## Computes the exponential function.
|
|
##
|
|
## d: The argument to the exponential function.
|
|
##
|
|
## Returns: *e* to the power of *d*.
|
|
##
|
|
## .. zeek:see:: floor sqrt ln log10
|
|
function exp%(d: double%): double
|
|
%{
|
|
return zeek::make_intrusive<zeek::DoubleVal>(exp(d));
|
|
%}
|
|
|
|
## Computes the natural logarithm of a number.
|
|
##
|
|
## d: The argument to the logarithm.
|
|
##
|
|
## Returns: The natural logarithm of *d*.
|
|
##
|
|
## .. zeek:see:: exp floor sqrt log10
|
|
function ln%(d: double%): double
|
|
%{
|
|
return zeek::make_intrusive<zeek::DoubleVal>(log(d));
|
|
%}
|
|
|
|
## Computes the common logarithm of a number.
|
|
##
|
|
## d: The argument to the logarithm.
|
|
##
|
|
## Returns: The common logarithm of *d*.
|
|
##
|
|
## .. zeek:see:: exp floor sqrt ln
|
|
function log10%(d: double%): double
|
|
%{
|
|
return zeek::make_intrusive<zeek::DoubleVal>(log10(d));
|
|
%}
|
|
|
|
# ===========================================================================
|
|
#
|
|
# Introspection
|
|
#
|
|
# ===========================================================================
|
|
|
|
## Returns the ID of the analyzer which raised the current event.
|
|
##
|
|
## Returns: The ID of the analyzer which raised the current event, or 0 if
|
|
## none.
|
|
function current_analyzer%(%) : count
|
|
%{
|
|
return zeek::val_mgr->Count(zeek::event_mgr.CurrentAnalyzer());
|
|
%}
|
|
|
|
## Returns Zeek's process ID.
|
|
##
|
|
## Returns: Zeek's process ID.
|
|
function getpid%(%) : count
|
|
%{
|
|
return zeek::val_mgr->Count(getpid());
|
|
%}
|
|
|
|
%%{
|
|
namespace zeek {
|
|
extern const char* zeek_version();
|
|
} // namespace zeek
|
|
%%}
|
|
|
|
## Returns the Zeek version string.
|
|
##
|
|
## Returns: Zeek's version, e.g., 2.0-beta-47-debug.
|
|
function zeek_version%(%): string
|
|
%{
|
|
return zeek::make_intrusive<zeek::StringVal>(zeek::zeek_version());
|
|
%}
|
|
|
|
## Converts a record type name to a vector of strings, where each element is
|
|
## the name of a record field. Nested records are flattened.
|
|
##
|
|
## rt: The name of the record type.
|
|
##
|
|
## Returns: A string vector with the field names of *rt*.
|
|
function record_type_to_vector%(rt: string%): string_vec
|
|
%{
|
|
auto result = zeek::make_intrusive<zeek::VectorVal>(zeek::id::string_vec);
|
|
|
|
zeek::RecordType* type = zeek::id::find_type(rt->CheckString())->AsRecordType();
|
|
|
|
for ( int i = 0; i < type->NumFields(); ++i )
|
|
result->Assign(i+1, zeek::make_intrusive<zeek::StringVal>(type->FieldName(i)));
|
|
|
|
return result;
|
|
%}
|
|
|
|
## Returns the type name of an arbitrary Zeek variable.
|
|
##
|
|
## t: An arbitrary object.
|
|
##
|
|
## Returns: The type name of *t*.
|
|
function type_name%(t: any%): string
|
|
%{
|
|
zeek::ODesc d;
|
|
t->GetType()->Describe(&d);
|
|
|
|
String* s = new String(1, d.TakeBytes(), d.Len());
|
|
s->SetUseFreeToDelete(true);
|
|
|
|
return zeek::make_intrusive<zeek::StringVal>(s);
|
|
%}
|
|
|
|
%%{
|
|
static std::map<std::string, std::set<std::string>> build_complete_alias_map()
|
|
{
|
|
std::map<std::string, std::set<std::string>> rval;
|
|
|
|
for ( const auto& [name, alias_types] : zeek::Type::GetAliasMap() )
|
|
{
|
|
rval[name].emplace(name);
|
|
|
|
for ( const auto& alias_type : alias_types )
|
|
{
|
|
const auto& alias_name = alias_type->GetName();
|
|
rval[name].emplace(alias_name);
|
|
rval[alias_name].emplace(name);
|
|
}
|
|
}
|
|
|
|
for ( const auto& [name, aliases] : rval )
|
|
{
|
|
std::map<std::string, bool> more;
|
|
|
|
for ( const auto& alias_name : aliases )
|
|
if ( alias_name != name )
|
|
more.emplace(alias_name, false);
|
|
|
|
for ( ; ; )
|
|
{
|
|
auto prev_size = more.size();
|
|
|
|
for ( auto& [alias_name, checked] : more )
|
|
{
|
|
if ( checked ) continue;
|
|
|
|
for ( const auto& alias_alias : rval[alias_name] )
|
|
more.emplace(alias_alias, false);
|
|
|
|
checked = true;
|
|
}
|
|
|
|
if ( prev_size == more.size() )
|
|
break;
|
|
}
|
|
|
|
for ( const auto& [alias_name, checked] : more )
|
|
rval[name].emplace(alias_name);
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
%%}
|
|
|
|
## Returns all type name aliases of a value or type.
|
|
##
|
|
## x: An arbitrary value or type.
|
|
##
|
|
## Returns: The set of all type name aliases of *x* (or the type of *x*
|
|
## if it's a value instead of a type). For primitive values
|
|
## and types like :zeek:type:`string` or :zeek:type:`count`,
|
|
## this returns an empty set. For types with user-defined
|
|
## names like :zeek:type:`record` or :zeek:type:`enum`, the
|
|
## returned set contains the original user-defined name for the
|
|
## type along with all aliases. For other compound types, like
|
|
## :zeek:type:`table`, the returned set is empty unless
|
|
## explicitly requesting aliases for a user-defined type alias
|
|
## or a value that was explicitly created using a type alias
|
|
## (as opposed to originating from an "anonymous" constructor
|
|
## or initializer for that compound type).
|
|
function type_aliases%(x: any%): string_set
|
|
%{
|
|
auto rval = make_intrusive<zeek::TableVal>(zeek::id::string_set);
|
|
|
|
auto t = x->GetType();
|
|
|
|
if ( t->Tag() == TYPE_TYPE )
|
|
t = t->AsTypeType()->GetType();
|
|
|
|
const auto& tname = t->GetName();
|
|
|
|
if ( tname.empty() )
|
|
return rval;
|
|
|
|
static auto all_aliases = build_complete_alias_map();
|
|
auto it = all_aliases.find(tname);
|
|
|
|
if ( it == all_aliases.end() )
|
|
return rval;
|
|
|
|
for ( const auto& name : it->second )
|
|
rval->Assign(make_intrusive<zeek::StringVal>(name), nullptr);
|
|
|
|
return rval;
|
|
%}
|
|
|
|
## Returns all value names associated with an enum type.
|
|
##
|
|
## et: An enum type.
|
|
##
|
|
## Returns: All enum value names associated with enum type *et*.
|
|
## If *et* is not an enum type, an empty set is returned.
|
|
function enum_names%(et: any%): string_set
|
|
%{
|
|
auto rval = make_intrusive<zeek::TableVal>(zeek::id::string_set);
|
|
|
|
if ( et->GetType()->Tag() != TYPE_TYPE )
|
|
return rval;
|
|
|
|
const auto& t = et->GetType()->AsTypeType()->GetType();
|
|
|
|
if ( t->Tag() != TYPE_ENUM )
|
|
return rval;
|
|
|
|
auto enum_type = t->AsEnumType();
|
|
|
|
for ( const auto& [name, i] : enum_type->Names() )
|
|
rval->Assign(make_intrusive<zeek::StringVal>(name), nullptr);
|
|
|
|
return rval;
|
|
%}
|
|
|
|
## Returns: list of command-line arguments (``argv``) used to run Zeek.
|
|
function zeek_args%(%): string_vec
|
|
%{
|
|
auto sv = zeek::id::string_vec;
|
|
auto rval = zeek::make_intrusive<zeek::VectorVal>(std::move(sv));
|
|
|
|
for ( auto i = 0; i < zeek::detail::zeek_argc; ++i )
|
|
rval->Assign(rval->Size(), zeek::make_intrusive<zeek::StringVal>(zeek::detail::zeek_argv[i]));
|
|
|
|
return rval;
|
|
%}
|
|
|
|
## Checks whether Zeek reads traffic from one or more network interfaces (as
|
|
## opposed to from a network trace in a file). Note that this function returns
|
|
## true even after Zeek has stopped reading network traffic, for example due to
|
|
## receiving a termination signal.
|
|
##
|
|
## Returns: True if reading traffic from a network interface.
|
|
##
|
|
## .. zeek:see:: reading_traces packet_source
|
|
function reading_live_traffic%(%): bool
|
|
%{
|
|
return zeek::val_mgr->Bool(zeek::run_state::reading_live);
|
|
%}
|
|
|
|
## Checks whether Zeek reads traffic from a trace file (as opposed to from a
|
|
## network interface).
|
|
##
|
|
## Returns: True if reading traffic from a network trace.
|
|
##
|
|
## .. zeek:see:: reading_live_traffic packet_source
|
|
function reading_traces%(%): bool
|
|
%{
|
|
return zeek::val_mgr->Bool(zeek::run_state::reading_traces);
|
|
%}
|
|
|
|
## Returns: the packet source being read by Zeek.
|
|
##
|
|
## .. zeek:see:: reading_live_traffic reading_traces
|
|
function packet_source%(%): PacketSource
|
|
%{
|
|
static auto ps_type = zeek::id::find_type<zeek::RecordType>("PacketSource");
|
|
auto ps = zeek::iosource_mgr->GetPktSrc();
|
|
auto r = zeek::make_intrusive<zeek::RecordVal>(ps_type);
|
|
|
|
if ( ps )
|
|
{
|
|
r->Assign(0, ps->IsLive());
|
|
r->Assign(1, ps->Path());
|
|
r->Assign(2, ps->LinkType());
|
|
r->Assign(3, ps->Netmask());
|
|
}
|
|
|
|
return r;
|
|
%}
|
|
|
|
## Generates a table of the size of all global variables. The table index is
|
|
## the variable name and the value is the variable size in bytes.
|
|
##
|
|
## Returns: A table that maps variable names to their sizes.
|
|
##
|
|
## .. zeek:see:: global_ids
|
|
function global_sizes%(%): var_sizes &deprecated="Remove in v5.1. MemoryAllocation() is deprecated and will be removed."
|
|
%{
|
|
auto sizes = zeek::make_intrusive<zeek::TableVal>(IntrusivePtr{zeek::NewRef{}, var_sizes});
|
|
const auto& globals = zeek::detail::global_scope()->Vars();
|
|
|
|
for ( const auto& global : globals )
|
|
{
|
|
auto& id = global.second;
|
|
if ( id->HasVal() )
|
|
{
|
|
auto id_name = zeek::make_intrusive<zeek::StringVal>(id->Name());
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
auto id_size = zeek::val_mgr->Count(id->GetVal()->MemoryAllocation());
|
|
#pragma GCC diagnostic pop
|
|
sizes->Assign(std::move(id_name), std::move(id_size));
|
|
}
|
|
}
|
|
|
|
return sizes;
|
|
%}
|
|
|
|
## Generates a table with information about all global identifiers. The table
|
|
## value is a record containing the type name of the identifier, whether it is
|
|
## exported, a constant, an enum constant, redefinable, and its value (if it
|
|
## has one).
|
|
##
|
|
## Returns: A table that maps identifier names to information about them.
|
|
##
|
|
## .. zeek:see:: global_sizes
|
|
function global_ids%(%): id_table
|
|
%{
|
|
static auto id_table = zeek::id::find_type<zeek::TableType>("id_table");
|
|
auto ids = zeek::make_intrusive<zeek::TableVal>(id_table);
|
|
const auto& globals = zeek::detail::global_scope()->Vars();
|
|
|
|
for ( const auto& global : globals )
|
|
{
|
|
const auto& id = global.second;
|
|
static auto script_id = zeek::id::find_type<zeek::RecordType>("script_id");
|
|
auto rec = zeek::make_intrusive<zeek::RecordVal>(script_id);
|
|
rec->Assign(0, type_name(id->GetType()->Tag()));
|
|
rec->Assign(1, id->IsExport());
|
|
rec->Assign(2, id->IsConst());
|
|
rec->Assign(3, id->IsEnumConst());
|
|
rec->Assign(4, id->IsOption());
|
|
rec->Assign(5, id->IsRedefinable());
|
|
rec->Assign(6, id->GetAttr(zeek::detail::ATTR_BACKEND) != zeek::detail::Attr::nil);
|
|
|
|
if ( id->HasVal() )
|
|
rec->Assign(7, id->GetVal());
|
|
|
|
auto id_name = zeek::make_intrusive<zeek::StringVal>(id->Name());
|
|
ids->Assign(std::move(id_name), std::move(rec));
|
|
}
|
|
|
|
return ids;
|
|
%}
|
|
|
|
## Returns the value of a global identifier.
|
|
##
|
|
## id: The global identifier.
|
|
##
|
|
## Returns: The value of *id*. If *id* does not describe a valid identifier,
|
|
## the string ``"<unknown id>"`` or ``"<no ID value>"`` is returned.
|
|
function lookup_ID%(id: string%) : any
|
|
%{
|
|
const auto& i = zeek::detail::global_scope()->Find(id->CheckString());
|
|
if ( ! i )
|
|
return zeek::make_intrusive<zeek::StringVal>("<unknown id>");
|
|
|
|
if ( i->IsEnumConst() )
|
|
{
|
|
auto et = i->GetType()->AsEnumType();
|
|
auto idx = et->Lookup(detail::GLOBAL_MODULE_NAME, i->Name());
|
|
return et->GetEnumVal(idx);
|
|
}
|
|
|
|
if ( ! i->GetVal() )
|
|
return zeek::make_intrusive<zeek::StringVal>("<no ID value>");
|
|
|
|
return i->GetVal();
|
|
%}
|
|
|
|
## Generates metadata about a record's fields. The returned information
|
|
## includes the field name, whether it is logged, its value (if it has one),
|
|
## and its default value (if specified).
|
|
##
|
|
## rec: The record value or type to inspect.
|
|
##
|
|
## Returns: A table that describes the fields of a record.
|
|
function record_fields%(rec: any%): record_field_table
|
|
%{
|
|
static auto record_field_table = zeek::id::find_type<zeek::TableType>("record_field_table");
|
|
|
|
if ( rec->GetType()->Tag() == zeek::TYPE_STRING )
|
|
{
|
|
const auto& id = zeek::detail::global_scope()->Find(rec->AsStringVal()->ToStdString());
|
|
|
|
if ( ! id || ! id->IsType() || id->GetType()->Tag() != zeek::TYPE_RECORD )
|
|
{
|
|
zeek::reporter->Error("record_fields string argument does not name a record type");
|
|
return zeek::make_intrusive<zeek::TableVal>(record_field_table);
|
|
}
|
|
|
|
return id->GetType()->AsRecordType()->GetRecordFieldsVal();
|
|
}
|
|
|
|
return rec->GetRecordFields();
|
|
%}
|
|
|
|
## Enables detailed collection of profiling statistics. Statistics include
|
|
## CPU/memory usage, connections, TCP states/reassembler, DNS lookups,
|
|
## timers, and script-level state. The script variable :zeek:id:`profiling_file`
|
|
## holds the name of the file.
|
|
##
|
|
## .. zeek:see:: get_conn_stats
|
|
## get_dns_stats
|
|
## get_event_stats
|
|
## get_file_analysis_stats
|
|
## get_gap_stats
|
|
## get_matcher_stats
|
|
## get_net_stats
|
|
## get_proc_stats
|
|
## get_reassembler_stats
|
|
## get_thread_stats
|
|
## get_timer_stats
|
|
function do_profiling%(%) : any
|
|
%{
|
|
if ( zeek::detail::profiling_logger )
|
|
zeek::detail::profiling_logger->Log();
|
|
|
|
return nullptr;
|
|
%}
|
|
|
|
## Checks whether a given IP address belongs to a local interface.
|
|
##
|
|
## ip: The IP address to check.
|
|
##
|
|
## Returns: True if *ip* belongs to a local interface.
|
|
function is_local_interface%(ip: addr%) : bool
|
|
%{
|
|
if ( ip->AsAddr().IsLoopback() )
|
|
return zeek::val_mgr->True();
|
|
|
|
list<zeek::IPAddr> addrs;
|
|
|
|
char host[MAXHOSTNAMELEN];
|
|
|
|
strcpy(host, "localhost");
|
|
gethostname(host, MAXHOSTNAMELEN);
|
|
host[MAXHOSTNAMELEN-1] = '\0';
|
|
|
|
struct hostent* ent = gethostbyname2(host, AF_INET);
|
|
|
|
if ( ent )
|
|
{
|
|
for ( unsigned int len = 0; ent->h_addr_list[len]; ++len )
|
|
addrs.push_back(zeek::IPAddr(IPv4, (uint32_t*)ent->h_addr_list[len],
|
|
zeek::IPAddr::Network));
|
|
}
|
|
|
|
ent = gethostbyname2(host, AF_INET6);
|
|
|
|
if ( ent )
|
|
{
|
|
for ( unsigned int len = 0; ent->h_addr_list[len]; ++len )
|
|
addrs.push_back(zeek::IPAddr(IPv6, (uint32_t*)ent->h_addr_list[len],
|
|
zeek::IPAddr::Network));
|
|
}
|
|
|
|
list<zeek::IPAddr>::const_iterator it;
|
|
for ( it = addrs.begin(); it != addrs.end(); ++it )
|
|
{
|
|
if ( *it == ip->AsAddr() )
|
|
return zeek::val_mgr->True();
|
|
}
|
|
|
|
return zeek::val_mgr->False();
|
|
%}
|
|
|
|
## Write rule matcher statistics (DFA states, transitions, memory usage, cache
|
|
## hits/misses) to a file.
|
|
##
|
|
## f: The file to write to.
|
|
##
|
|
## Returns: True (unconditionally).
|
|
##
|
|
## .. zeek:see:: get_matcher_stats
|
|
function dump_rule_stats%(f: file%): bool
|
|
%{
|
|
if ( zeek::detail::rule_matcher )
|
|
zeek::detail::rule_matcher->DumpStats(f);
|
|
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## Checks if Zeek is terminating.
|
|
##
|
|
## Returns: True if Zeek is in the process of shutting down.
|
|
##
|
|
## .. zeek:see:: terminate
|
|
function zeek_is_terminating%(%): bool
|
|
%{
|
|
return zeek::val_mgr->Bool(zeek::run_state::terminating);
|
|
%}
|
|
|
|
## Returns the hostname of the machine Zeek runs on.
|
|
##
|
|
## Returns: The hostname of the machine Zeek runs on.
|
|
function gethostname%(%) : string
|
|
%{
|
|
char buffer[MAXHOSTNAMELEN];
|
|
if ( gethostname(buffer, MAXHOSTNAMELEN) < 0 )
|
|
strcpy(buffer, "<unknown>");
|
|
|
|
buffer[MAXHOSTNAMELEN-1] = '\0';
|
|
return zeek::make_intrusive<zeek::StringVal>(buffer);
|
|
%}
|
|
|
|
## Returns whether an address is IPv4 or not.
|
|
##
|
|
## a: the address to check.
|
|
##
|
|
## Returns: true if *a* is an IPv4 address, else false.
|
|
function is_v4_addr%(a: addr%): bool
|
|
%{
|
|
if ( a->AsAddr().GetFamily() == IPv4 )
|
|
return zeek::val_mgr->True();
|
|
else
|
|
return zeek::val_mgr->False();
|
|
%}
|
|
|
|
## Returns whether an address is IPv6 or not.
|
|
##
|
|
## a: the address to check.
|
|
##
|
|
## Returns: true if *a* is an IPv6 address, else false.
|
|
function is_v6_addr%(a: addr%): bool
|
|
%{
|
|
if ( a->AsAddr().GetFamily() == IPv6 )
|
|
return zeek::val_mgr->True();
|
|
else
|
|
return zeek::val_mgr->False();
|
|
%}
|
|
|
|
## Returns whether a subnet specification is IPv4 or not.
|
|
##
|
|
## s: the subnet to check.
|
|
##
|
|
## Returns: true if *s* is an IPv4 subnet, else false.
|
|
function is_v4_subnet%(s: subnet%): bool
|
|
%{
|
|
if ( s->AsSubNet().Prefix().GetFamily() == IPv4 )
|
|
return zeek::val_mgr->True();
|
|
else
|
|
return zeek::val_mgr->False();
|
|
%}
|
|
|
|
## Returns whether a subnet specification is IPv6 or not.
|
|
##
|
|
## s: the subnet to check.
|
|
##
|
|
## Returns: true if *s* is an IPv6 subnet, else false.
|
|
function is_v6_subnet%(s: subnet%): bool
|
|
%{
|
|
if ( s->AsSubNet().Prefix().GetFamily() == IPv6 )
|
|
return zeek::val_mgr->True();
|
|
else
|
|
return zeek::val_mgr->False();
|
|
%}
|
|
|
|
## Returns a representation of the call stack as a vector of call stack
|
|
## elements, each containing call location information.
|
|
##
|
|
## Returns: the call stack information, including function, file, and line
|
|
## location information.
|
|
function backtrace%(%): Backtrace
|
|
%{
|
|
using zeek::detail::call_stack;
|
|
static auto backtrace_type = id::find_type<VectorType>("Backtrace");
|
|
static auto elem_type = id::find_type<RecordType>("BacktraceElement");
|
|
static auto function_name_idx = elem_type->FieldOffset("function_name");
|
|
static auto function_args_idx = elem_type->FieldOffset("function_args");
|
|
static auto file_location_idx = elem_type->FieldOffset("file_location");
|
|
static auto line_location_idx = elem_type->FieldOffset("line_location");
|
|
|
|
auto rval = make_intrusive<VectorVal>(backtrace_type);
|
|
|
|
// The body of the following loop can wind up adding items to
|
|
// the call stack (because MakeCallArgumentVector() evaluates
|
|
// default arguments, which can in turn involve calls to script
|
|
// functions), so we work from a copy of the current call stack
|
|
// to prevent problems with iterator invalidation.
|
|
auto cs_copy = call_stack;
|
|
|
|
for ( auto it = cs_copy.rbegin(); it != cs_copy.rend(); ++it )
|
|
{
|
|
const auto& ci = *it;
|
|
if ( ! ci.func )
|
|
// This happens for compiled code.
|
|
continue;
|
|
|
|
auto elem = make_intrusive<RecordVal>(elem_type);
|
|
|
|
const auto& params = ci.func->GetType()->Params();
|
|
auto args = MakeCallArgumentVector(ci.args, params);
|
|
|
|
elem->Assign(function_name_idx, ci.func->Name());
|
|
elem->Assign(function_args_idx, std::move(args));
|
|
|
|
if ( ci.call )
|
|
{
|
|
auto loc = ci.call->GetLocationInfo();
|
|
elem->Assign(file_location_idx, loc->filename);
|
|
elem->Assign(line_location_idx, loc->first_line);
|
|
}
|
|
|
|
rval->Assign(rval->Size(), std::move(elem));
|
|
}
|
|
|
|
return rval;
|
|
%}
|
|
|
|
# ===========================================================================
|
|
#
|
|
# Conversion
|
|
#
|
|
# ===========================================================================
|
|
|
|
## Converts the *data* field of :zeek:type:`ip6_routing` records that have
|
|
## *rtype* of 0 into a vector of addresses.
|
|
##
|
|
## s: The *data* field of an :zeek:type:`ip6_routing` record that has
|
|
## an *rtype* of 0.
|
|
##
|
|
## Returns: The vector of addresses contained in the routing header data.
|
|
function routing0_data_to_addrs%(s: string%): addr_vec
|
|
%{
|
|
auto rval = zeek::make_intrusive<zeek::VectorVal>(zeek::id::find_type<zeek::VectorType>("addr_vec"));
|
|
|
|
int len = s->Len();
|
|
const u_char* bytes = s->Bytes();
|
|
bytes += 4; // go past 32-bit reserved field
|
|
len -= 4;
|
|
|
|
if ( ( len % 16 ) != 0 )
|
|
zeek::reporter->Warning("Bad ip6_routing data length: %d", s->Len());
|
|
|
|
while ( len > 0 )
|
|
{
|
|
zeek::IPAddr a(IPv6, (const uint32_t*) bytes, zeek::IPAddr::Network);
|
|
rval->Assign(rval->Size(), zeek::make_intrusive<zeek::AddrVal>(a));
|
|
bytes += 16;
|
|
len -= 16;
|
|
}
|
|
|
|
return rval;
|
|
%}
|
|
|
|
## Converts an :zeek:type:`addr` to an :zeek:type:`index_vec`.
|
|
##
|
|
## a: The address to convert into a vector of counts.
|
|
##
|
|
## Returns: A vector containing the host-order address representation,
|
|
## four elements in size for IPv6 addresses, or one element for IPv4.
|
|
##
|
|
## .. zeek:see:: counts_to_addr
|
|
function addr_to_counts%(a: addr%): index_vec
|
|
%{
|
|
auto rval = zeek::make_intrusive<zeek::VectorVal>(zeek::id::index_vec);
|
|
const uint32_t* bytes;
|
|
int len = a->AsAddr().GetBytes(&bytes);
|
|
|
|
for ( int i = 0; i < len; ++i )
|
|
rval->Assign(i, zeek::val_mgr->Count(ntohl(bytes[i])));
|
|
|
|
return rval;
|
|
%}
|
|
|
|
## Converts an :zeek:type:`index_vec` to an :zeek:type:`addr`.
|
|
##
|
|
## v: The vector containing host-order IP address representation,
|
|
## one element for IPv4 addresses, four elements for IPv6 addresses.
|
|
##
|
|
## Returns: An IP address.
|
|
##
|
|
## .. zeek:see:: addr_to_counts
|
|
function counts_to_addr%(v: index_vec%): addr
|
|
%{
|
|
auto vv = v->As<zeek::VectorVal*>();
|
|
|
|
if ( vv->Size() == 1 )
|
|
{
|
|
return zeek::make_intrusive<zeek::AddrVal>(htonl(vv->CountAt(0)));
|
|
}
|
|
else if ( vv->Size() == 4 )
|
|
{
|
|
uint32_t bytes[4];
|
|
for ( int i = 0; i < 4; ++i )
|
|
bytes[i] = htonl(vv->CountAt(i));
|
|
return zeek::make_intrusive<zeek::AddrVal>(bytes);
|
|
}
|
|
else
|
|
{
|
|
zeek::emit_builtin_error("invalid vector size", @ARG@[0]);
|
|
uint32_t bytes[4];
|
|
memset(bytes, 0, sizeof(bytes));
|
|
return zeek::make_intrusive<zeek::AddrVal>(bytes);
|
|
}
|
|
%}
|
|
|
|
## Converts an :zeek:type:`enum` to an :zeek:type:`int`.
|
|
##
|
|
## e: The :zeek:type:`enum` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`int` value that corresponds to the :zeek:type:`enum`.
|
|
function enum_to_int%(e: any%): int
|
|
%{
|
|
if ( e->GetType()->Tag() != zeek::TYPE_ENUM )
|
|
{
|
|
zeek::emit_builtin_error("enum_to_int() requires enum value");
|
|
return zeek::val_mgr->Int(-1);
|
|
}
|
|
|
|
return zeek::val_mgr->Int(e->AsEnum());
|
|
%}
|
|
|
|
## Converts a :zeek:type:`string` to an :zeek:type:`int`.
|
|
##
|
|
## str: The :zeek:type:`string` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`string` *str* as :zeek:type:`int`.
|
|
##
|
|
## .. zeek:see:: to_addr to_port to_subnet
|
|
function to_int%(str: string%): int
|
|
%{
|
|
const char* s = str->CheckString();
|
|
char* end_s;
|
|
|
|
bro_int_t i = strtoll(s, &end_s, 10);
|
|
|
|
#if 0
|
|
// Not clear we should complain. For example, is " 205 "
|
|
// a legal conversion?
|
|
if ( s[0] == '\0' || end_s[0] != '\0' )
|
|
zeek::emit_builtin_error("bad conversion to integer", @ARG@[0]);
|
|
#endif
|
|
|
|
return zeek::val_mgr->Int(i);
|
|
%}
|
|
|
|
|
|
## Converts a (positive) :zeek:type:`int` to a :zeek:type:`count`.
|
|
##
|
|
## n: The :zeek:type:`int` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`int` *n* as unsigned integer, or 0 if *n* < 0.
|
|
function int_to_count%(n: int%): count
|
|
%{
|
|
if ( n < 0 )
|
|
{
|
|
zeek::emit_builtin_error("bad conversion to count", @ARG@[0]);
|
|
n = 0;
|
|
}
|
|
return zeek::val_mgr->Count(n);
|
|
%}
|
|
|
|
## Converts a :zeek:type:`double` to a :zeek:type:`count`.
|
|
##
|
|
## d: The :zeek:type:`double` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`double` *d* as unsigned integer, or 0 if *d* < 0.0.
|
|
##
|
|
## .. zeek:see:: double_to_time
|
|
function double_to_count%(d: double%): count
|
|
%{
|
|
if ( d < 0.0 )
|
|
zeek::emit_builtin_error("bad conversion to count", @ARG@[0]);
|
|
|
|
return zeek::val_mgr->Count(bro_uint_t(rint(d)));
|
|
%}
|
|
|
|
## Converts a :zeek:type:`string` to a :zeek:type:`count`.
|
|
##
|
|
## str: The :zeek:type:`string` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`string` *str* as unsigned integer, or 0 if *str* has
|
|
## an invalid format.
|
|
##
|
|
## .. zeek:see:: to_addr to_int to_port to_subnet
|
|
function to_count%(str: string%): count
|
|
%{
|
|
const char* s = str->CheckString();
|
|
char* end_s;
|
|
|
|
uint64_t u = (uint64_t) strtoull(s, &end_s, 10);
|
|
|
|
if ( s[0] == '\0' || end_s[0] != '\0' )
|
|
{
|
|
zeek::emit_builtin_error("bad conversion to count", @ARG@[0]);
|
|
u = 0;
|
|
}
|
|
|
|
return zeek::val_mgr->Count(u);
|
|
%}
|
|
|
|
## Converts an :zeek:type:`interval` to a :zeek:type:`double`.
|
|
##
|
|
## i: The :zeek:type:`interval` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`interval` *i* as :zeek:type:`double`.
|
|
##
|
|
## .. zeek:see:: double_to_interval
|
|
function interval_to_double%(i: interval%): double
|
|
%{
|
|
return zeek::make_intrusive<zeek::DoubleVal>(i);
|
|
%}
|
|
|
|
## Converts a :zeek:type:`count` to a :zeek:type:`double`.
|
|
##
|
|
## c: The :zeek:type:`count` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`count` *c* as :zeek:type:`double`.
|
|
##
|
|
## .. zeek:see:: int_to_double double_to_count
|
|
function count_to_double%(c: count%): double
|
|
%{
|
|
return zeek::make_intrusive<zeek::DoubleVal>(c);
|
|
%}
|
|
|
|
## Converts an :zeek:type:`int` to a :zeek:type:`double`.
|
|
##
|
|
## i: The :zeek:type:`int` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`int` *i* as :zeek:type:`double`.
|
|
##
|
|
## .. zeek:see:: count_to_double double_to_count
|
|
function int_to_double%(i: int%): double
|
|
%{
|
|
return zeek::make_intrusive<zeek::DoubleVal>(i);
|
|
%}
|
|
|
|
## Converts a :zeek:type:`time` value to a :zeek:type:`double`.
|
|
##
|
|
## t: The :zeek:type:`time` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`time` value *t* as :zeek:type:`double`.
|
|
##
|
|
## .. zeek:see:: double_to_time
|
|
function time_to_double%(t: time%): double
|
|
%{
|
|
return zeek::make_intrusive<zeek::DoubleVal>(t);
|
|
%}
|
|
|
|
## Converts a :zeek:type:`double` value to a :zeek:type:`time`.
|
|
##
|
|
## d: The :zeek:type:`double` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`double` value *d* as :zeek:type:`time`.
|
|
##
|
|
## .. zeek:see:: time_to_double double_to_count
|
|
function double_to_time%(d: double%): time
|
|
%{
|
|
return zeek::make_intrusive<zeek::TimeVal>(d);
|
|
%}
|
|
|
|
## Converts a :zeek:type:`double` to an :zeek:type:`interval`.
|
|
##
|
|
## d: The :zeek:type:`double` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`double` *d* as :zeek:type:`interval`.
|
|
##
|
|
## .. zeek:see:: interval_to_double
|
|
function double_to_interval%(d: double%): interval
|
|
%{
|
|
return zeek::make_intrusive<zeek::IntervalVal>(d);
|
|
%}
|
|
|
|
## Converts a :zeek:type:`port` to a :zeek:type:`count`.
|
|
##
|
|
## p: The :zeek:type:`port` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`port` *p* as :zeek:type:`count`.
|
|
##
|
|
## .. zeek:see:: count_to_port
|
|
function port_to_count%(p: port%): count
|
|
%{
|
|
return zeek::val_mgr->Count(p->Port());
|
|
%}
|
|
|
|
## Converts a :zeek:type:`count` and ``transport_proto`` to a :zeek:type:`port`.
|
|
##
|
|
## num: The :zeek:type:`port` number.
|
|
##
|
|
## proto: The transport protocol.
|
|
##
|
|
## Returns: The :zeek:type:`count` *num* as :zeek:type:`port`.
|
|
##
|
|
## .. zeek:see:: port_to_count
|
|
function count_to_port%(num: count, proto: transport_proto%): port
|
|
%{
|
|
return zeek::val_mgr->Port(num, (TransportProto)proto->AsEnum());
|
|
%}
|
|
|
|
## Converts a :zeek:type:`string` to an :zeek:type:`addr`.
|
|
##
|
|
## ip: The :zeek:type:`string` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`string` *ip* as :zeek:type:`addr`, or the unspecified
|
|
## address ``::`` if the input string does not parse correctly.
|
|
##
|
|
## .. zeek:see:: to_count to_int to_port count_to_v4_addr raw_bytes_to_v4_addr
|
|
## to_subnet
|
|
function to_addr%(ip: string%): addr
|
|
%{
|
|
char* s = ip->AsString()->Render();
|
|
zeek::ValPtr ret;
|
|
in6_addr tmp;
|
|
|
|
if ( zeek::IPAddr::ConvertString(s, &tmp) )
|
|
ret = zeek::make_intrusive<zeek::AddrVal>(zeek::IPAddr(tmp));
|
|
else
|
|
{
|
|
ret = zeek::make_intrusive<zeek::AddrVal>(zeek::IPAddr());
|
|
zeek::emit_builtin_error("failed converting string to IP address", ip);
|
|
}
|
|
|
|
delete [] s;
|
|
return ret;
|
|
%}
|
|
|
|
## Checks if a string is a valid IPv4 or IPv6 address.
|
|
##
|
|
## ip: the string to check for valid IP formatting.
|
|
##
|
|
## Returns: T if the string is a valid IPv4 or IPv6 address format.
|
|
function is_valid_ip%(ip: string%): bool
|
|
%{
|
|
char* s = ip->AsString()->Render();
|
|
auto rval = zeek::IPAddr::IsValid(s);
|
|
delete [] s;
|
|
return zeek::val_mgr->Bool(rval);
|
|
%}
|
|
|
|
## Converts a :zeek:type:`string` to a :zeek:type:`subnet`.
|
|
##
|
|
## sn: The subnet to convert.
|
|
##
|
|
## Returns: The *sn* string as a :zeek:type:`subnet`, or the unspecified subnet
|
|
## ``::/0`` if the input string does not parse correctly.
|
|
##
|
|
## .. zeek:see:: to_count to_int to_port count_to_v4_addr raw_bytes_to_v4_addr
|
|
## to_addr
|
|
function to_subnet%(sn: string%): subnet
|
|
%{
|
|
char* s = sn->AsString()->Render();
|
|
IPPrefix tmp;
|
|
|
|
if ( ! IPPrefix::ConvertString(s, &tmp) )
|
|
zeek::emit_builtin_error("failed converting string to IP prefix", sn);
|
|
|
|
auto ret = zeek::make_intrusive<zeek::SubNetVal>(tmp);
|
|
delete [] s;
|
|
return ret;
|
|
%}
|
|
|
|
## Converts a :zeek:type:`addr` to a :zeek:type:`subnet`.
|
|
##
|
|
## a: The address to convert.
|
|
##
|
|
## Returns: The address as a :zeek:type:`subnet`.
|
|
##
|
|
## .. zeek:see:: to_subnet
|
|
function addr_to_subnet%(a: addr%): subnet
|
|
%{
|
|
int width = (a->AsAddr().GetFamily() == IPv4 ? 32 : 128);
|
|
return zeek::make_intrusive<zeek::SubNetVal>(a->AsAddr(), width);
|
|
%}
|
|
|
|
## Converts a :zeek:type:`subnet` to an :zeek:type:`addr` by
|
|
## extracting the prefix.
|
|
##
|
|
## sn: The subnet to convert.
|
|
##
|
|
## Returns: The subnet as an :zeek:type:`addr`.
|
|
##
|
|
## .. zeek:see:: to_subnet
|
|
function subnet_to_addr%(sn: subnet%): addr
|
|
%{
|
|
return zeek::make_intrusive<zeek::AddrVal>(sn->Prefix());
|
|
%}
|
|
|
|
## Returns the width of a :zeek:type:`subnet`.
|
|
##
|
|
## sn: The subnet.
|
|
##
|
|
## Returns: The width of the subnet.
|
|
##
|
|
## .. zeek:see:: to_subnet
|
|
function subnet_width%(sn: subnet%): count
|
|
%{
|
|
return zeek::val_mgr->Count(sn->Width());
|
|
%}
|
|
|
|
## Converts a :zeek:type:`string` to a :zeek:type:`double`.
|
|
##
|
|
## str: The :zeek:type:`string` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`string` *str* as double, or 0 if *str* has
|
|
## an invalid format.
|
|
##
|
|
function to_double%(str: string%): double
|
|
%{
|
|
const char* s = str->CheckString();
|
|
char* end_s;
|
|
|
|
double d = strtod(s, &end_s);
|
|
|
|
if ( s[0] == '\0' || end_s[0] != '\0' )
|
|
{
|
|
zeek::emit_builtin_error("bad conversion to double", @ARG@[0]);
|
|
d = 0;
|
|
}
|
|
|
|
return zeek::make_intrusive<zeek::DoubleVal>(d);
|
|
%}
|
|
|
|
## Converts a :zeek:type:`count` to an :zeek:type:`addr`.
|
|
##
|
|
## ip: The :zeek:type:`count` to convert.
|
|
##
|
|
## Returns: The :zeek:type:`count` *ip* as :zeek:type:`addr`.
|
|
##
|
|
## .. zeek:see:: raw_bytes_to_v4_addr to_addr to_subnet
|
|
function count_to_v4_addr%(ip: count%): addr
|
|
%{
|
|
if ( ip > 4294967295LU )
|
|
{
|
|
zeek::emit_builtin_error("conversion of non-IPv4 count to addr", @ARG@[0]);
|
|
return zeek::make_intrusive<zeek::AddrVal>(uint32_t(0));
|
|
}
|
|
|
|
return zeek::make_intrusive<zeek::AddrVal>(htonl(uint32_t(ip)));
|
|
%}
|
|
|
|
## Converts a :zeek:type:`string` of bytes into an IPv4 address. In particular,
|
|
## this function interprets the first 4 bytes of the string as an IPv4 address
|
|
## in network order.
|
|
##
|
|
## b: The raw bytes (:zeek:type:`string`) to convert.
|
|
##
|
|
## Returns: The byte :zeek:type:`string` *b* as :zeek:type:`addr`.
|
|
##
|
|
## .. zeek:see:: raw_bytes_to_v4_addr to_addr to_subnet
|
|
function raw_bytes_to_v4_addr%(b: string%): addr
|
|
%{
|
|
uint32_t a = 0;
|
|
|
|
if ( b->Len() < 4 )
|
|
zeek::emit_builtin_error("too short a string as input to raw_bytes_to_v4_addr()");
|
|
|
|
else
|
|
{
|
|
const u_char* bp = b->Bytes();
|
|
a = (bp[0] << 24) | (bp[1] << 16) | (bp[2] << 8) | bp[3];
|
|
}
|
|
|
|
return zeek::make_intrusive<zeek::AddrVal>(htonl(a));
|
|
%}
|
|
|
|
## Converts a :zeek:type:`string` to a :zeek:type:`port`.
|
|
##
|
|
## s: The :zeek:type:`string` to convert.
|
|
##
|
|
## Returns: A :zeek:type:`port` converted from *s*.
|
|
##
|
|
## .. zeek:see:: to_addr to_count to_int to_subnet
|
|
function to_port%(s: string%): port
|
|
%{
|
|
int port = 0;
|
|
if ( s->Len() < 10 )
|
|
{
|
|
char* slash;
|
|
errno = 0;
|
|
port = strtol(s->CheckString(), &slash, 10);
|
|
if ( ! errno )
|
|
{
|
|
++slash;
|
|
if ( zeek::util::streq(slash, "tcp") )
|
|
return zeek::val_mgr->Port(port, TRANSPORT_TCP);
|
|
else if ( zeek::util::streq(slash, "udp") )
|
|
return zeek::val_mgr->Port(port, TRANSPORT_UDP);
|
|
else if ( zeek::util::streq(slash, "icmp") )
|
|
return zeek::val_mgr->Port(port, TRANSPORT_ICMP);
|
|
}
|
|
}
|
|
|
|
zeek::emit_builtin_error("wrong port format, must be /[0-9]{1,5}\\/(tcp|udp|icmp)/");
|
|
return zeek::val_mgr->Port(port, TRANSPORT_UNKNOWN);
|
|
%}
|
|
|
|
## Converts a string of bytes (in network byte order) to a :zeek:type:`double`.
|
|
##
|
|
## s: A string of bytes containing the binary representation of a double value.
|
|
##
|
|
## Returns: The double value contained in *s*, or 0 if the conversion
|
|
## failed.
|
|
##
|
|
function bytestring_to_double%(s: string%): double
|
|
%{
|
|
if ( s->Len() != sizeof(double) )
|
|
{
|
|
zeek::emit_builtin_error("bad conversion to double");
|
|
return zeek::make_intrusive<zeek::DoubleVal>(0.0);
|
|
}
|
|
|
|
// See #908 for a discussion of portability.
|
|
double d;
|
|
memcpy(&d, s->Bytes(), sizeof(double));
|
|
return zeek::make_intrusive<zeek::DoubleVal>(ntohd(d));
|
|
%}
|
|
|
|
## Converts a string of bytes to a :zeek:type:`count`.
|
|
##
|
|
## s: A string of bytes containing the binary representation of the value.
|
|
##
|
|
## is_le: If true, *s* is assumed to be in little endian format, else it's big endian.
|
|
##
|
|
## Returns: The value contained in *s*, or 0 if the conversion failed.
|
|
##
|
|
function bytestring_to_count%(s: string, is_le: bool &default=F%): count
|
|
%{
|
|
#ifdef HOST_BIGENDIAN
|
|
static const bool host_bigendian = true;
|
|
#else
|
|
static const bool host_bigendian = false;
|
|
#endif
|
|
const u_char *p = s->Bytes();
|
|
unsigned int i;
|
|
|
|
switch ( s->Len() ) {
|
|
case sizeof(uint8_t):
|
|
{
|
|
uint8_t value = 0;
|
|
memcpy(&value, p, sizeof(uint8_t));
|
|
return zeek::val_mgr->Count(value);
|
|
}
|
|
|
|
case sizeof(uint16_t):
|
|
{
|
|
uint16_t value = 0;
|
|
|
|
if ( (host_bigendian && is_le) || (! host_bigendian && ! is_le) )
|
|
{
|
|
char buf[sizeof(uint16_t)];
|
|
char *d = &buf[sizeof(uint16_t)-1];
|
|
|
|
for ( i = 0; i < sizeof(uint16_t); i++ )
|
|
*d-- = *p++;
|
|
|
|
memcpy(&value, buf, sizeof(uint16_t));
|
|
}
|
|
else
|
|
memcpy(&value, p, sizeof(uint16_t));
|
|
|
|
return zeek::val_mgr->Count(value);
|
|
}
|
|
|
|
case sizeof(uint32_t):
|
|
{
|
|
uint32_t value = 0;
|
|
|
|
if ( (host_bigendian && is_le) || (! host_bigendian && ! is_le) )
|
|
{
|
|
char buf[sizeof(uint32_t)];
|
|
char *d = &buf[sizeof(uint32_t)-1];
|
|
|
|
for ( i = 0; i < sizeof(uint32_t); i++ )
|
|
*d-- = *p++;
|
|
|
|
memcpy(&value, buf, sizeof(uint32_t));
|
|
}
|
|
else
|
|
memcpy(&value, p, sizeof(uint32_t));
|
|
|
|
return zeek::val_mgr->Count(value);
|
|
}
|
|
|
|
case sizeof(uint64_t):
|
|
{
|
|
uint64_t value = 0;
|
|
|
|
if ( (host_bigendian && is_le) || (! host_bigendian && ! is_le) )
|
|
{
|
|
char buf[sizeof(uint64_t)];
|
|
char *d = &buf[sizeof(uint64_t)-1];
|
|
|
|
for ( i = 0; i < sizeof(uint64_t); i++ )
|
|
*d-- = *p++;
|
|
|
|
memcpy(&value, buf, sizeof(uint64_t));
|
|
}
|
|
else
|
|
memcpy(&value, p, sizeof(uint64_t));
|
|
|
|
return zeek::val_mgr->Count(value);
|
|
}
|
|
}
|
|
|
|
zeek::emit_builtin_error("unsupported byte length for bytestring_to_count");
|
|
return zeek::val_mgr->Count(0);
|
|
%}
|
|
|
|
## Converts a reverse pointer name to an address. For example,
|
|
## ``1.0.168.192.in-addr.arpa`` to ``192.168.0.1``.
|
|
##
|
|
## s: The string with the reverse pointer name.
|
|
##
|
|
## Returns: The IP address corresponding to *s*.
|
|
##
|
|
## .. zeek:see:: addr_to_ptr_name to_addr
|
|
function ptr_name_to_addr%(s: string%): addr
|
|
%{
|
|
if ( s->Len() != 72 )
|
|
{
|
|
int a[4];
|
|
uint32_t addr;
|
|
char ss[13]; // this will contain "in-addr.arpa"
|
|
|
|
if ( sscanf(s->CheckString(),
|
|
"%d.%d.%d.%d.%12s",
|
|
a, a+1, a+2, a+3, ss) != 5
|
|
|| strcmp(ss, "in-addr.arpa") != 0 )
|
|
{
|
|
zeek::emit_builtin_error("bad PTR name", @ARG@[0]);
|
|
addr = 0;
|
|
}
|
|
else
|
|
addr = (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
|
|
|
|
return zeek::make_intrusive<zeek::AddrVal>(htonl(addr));
|
|
}
|
|
else
|
|
{
|
|
uint32_t addr6[4];
|
|
uint32_t b[32];
|
|
char ss[9]; // this will contain "ip6.arpa"
|
|
if ( sscanf(s->CheckString(),
|
|
"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
|
|
"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
|
|
"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
|
|
"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x.%8s",
|
|
b+31, b+30, b+29, b+28, b+27, b+26, b+25, b+24,
|
|
b+23, b+22, b+21, b+20, b+19, b+18, b+17, b+16,
|
|
b+15, b+14, b+13, b+12, b+11, b+10, b+9, b+8,
|
|
b+7, b+6, b+5, b+4, b+3, b+2, b+1, b, ss) != 33
|
|
|| strcmp(ss, "ip6.arpa") != 0 )
|
|
{
|
|
zeek::emit_builtin_error("bad PTR name", @ARG@[0]);
|
|
memset(addr6, 0, sizeof addr6);
|
|
}
|
|
else
|
|
{
|
|
for ( unsigned int i = 0; i < 4; ++i )
|
|
{
|
|
uint32_t a = 0;
|
|
for ( unsigned int j = 1; j <= 8; ++j )
|
|
a |= b[8*i+j-1] << (32-j*4);
|
|
|
|
addr6[i] = htonl(a);
|
|
}
|
|
}
|
|
|
|
return zeek::make_intrusive<zeek::AddrVal>(addr6);
|
|
}
|
|
%}
|
|
|
|
## Converts an IP address to a reverse pointer name. For example,
|
|
## ``192.168.0.1`` to ``1.0.168.192.in-addr.arpa``.
|
|
##
|
|
## a: The IP address to convert to a reverse pointer name.
|
|
##
|
|
## Returns: The reverse pointer representation of *a*.
|
|
##
|
|
## .. zeek:see:: ptr_name_to_addr to_addr
|
|
function addr_to_ptr_name%(a: addr%): string
|
|
%{
|
|
return zeek::make_intrusive<zeek::StringVal>(a->AsAddr().PtrName().c_str());
|
|
%}
|
|
|
|
## Converts a string of bytes into its hexadecimal representation.
|
|
## For example, ``"04"`` would be converted to ``"3034"``.
|
|
##
|
|
## bytestring: The string of bytes.
|
|
##
|
|
## Returns: The hexadecimal representation of *bytestring*.
|
|
##
|
|
## .. zeek:see:: hexdump hexstr_to_bytestring
|
|
function bytestring_to_hexstr%(bytestring: string%): string
|
|
%{
|
|
bro_uint_t len = bytestring->AsString()->Len();
|
|
const u_char* bytes = bytestring->AsString()->Bytes();
|
|
auto hextr_buf = std::make_unique<char[]>((2 * len) + 1);
|
|
auto hexstr = hextr_buf.get();
|
|
|
|
hexstr[0] = 0;
|
|
for ( bro_uint_t i = 0; i < len; ++i )
|
|
snprintf(hexstr + (2 * i), 3, "%.2hhx", bytes[i]);
|
|
|
|
return zeek::make_intrusive<zeek::StringVal>(hexstr);
|
|
%}
|
|
|
|
## Converts a hex-string into its binary representation.
|
|
## For example, ``"3034"`` would be converted to ``"04"``.
|
|
##
|
|
## The input string is assumed to contain an even number of hexadecimal digits
|
|
## (0-9, a-f, or A-F), otherwise behavior is undefined.
|
|
##
|
|
## hexstr: The hexadecimal string representation.
|
|
##
|
|
## Returns: The binary representation of *hexstr*.
|
|
##
|
|
## .. zeek:see:: hexdump bytestring_to_hexstr
|
|
function hexstr_to_bytestring%(hexstr: string%): string
|
|
%{
|
|
bro_uint_t len = hexstr->AsString()->Len();
|
|
if ( len % 2 != 0 )
|
|
{
|
|
zeek::reporter->Error("Hex string '%s' has invalid length (not divisible by 2)", hexstr->CheckString());
|
|
return zeek::val_mgr->EmptyString();
|
|
}
|
|
|
|
const char* bytes = hexstr->AsString()->CheckString();
|
|
int outlen = (len/2);
|
|
auto bytestring_buf = std::make_unique<char[]>(outlen);
|
|
auto bytestring = bytestring_buf.get();
|
|
memset(bytestring, 0, outlen);
|
|
|
|
for ( bro_uint_t i = 0; i < len/2; ++i )
|
|
{
|
|
int res = sscanf(bytes + (2*i), "%2hhx", &bytestring[i]);
|
|
|
|
if ( res == EOF )
|
|
{
|
|
zeek::reporter->Error("Hex string %s contains invalid input: %s", hexstr->CheckString(), strerror(errno));
|
|
return zeek::val_mgr->EmptyString();
|
|
}
|
|
|
|
else if ( res != 1 )
|
|
{
|
|
zeek::reporter->Error("Could not read hex element from input %s", hexstr->CheckString());
|
|
return zeek::val_mgr->EmptyString();
|
|
}
|
|
|
|
}
|
|
|
|
return zeek::make_intrusive<zeek::StringVal>(outlen, bytestring);
|
|
%}
|
|
|
|
## Encodes a Base64-encoded string.
|
|
##
|
|
## s: The string to encode.
|
|
##
|
|
## a: An optional custom alphabet. The empty string indicates the default
|
|
## alphabet. If given, the string must consist of 64 unique characters.
|
|
##
|
|
## Returns: The encoded version of *s*.
|
|
##
|
|
## .. zeek:see:: decode_base64
|
|
function encode_base64%(s: string, a: string &default=""%): string
|
|
%{
|
|
String* t = zeek::detail::encode_base64(s->AsString(), a->AsString());
|
|
if ( t )
|
|
return zeek::make_intrusive<zeek::StringVal>(t);
|
|
else
|
|
{
|
|
zeek::reporter->Error("Broker query has an invalid data store");
|
|
return zeek::val_mgr->EmptyString();
|
|
}
|
|
%}
|
|
|
|
## Decodes a Base64-encoded string.
|
|
##
|
|
## s: The Base64-encoded string.
|
|
##
|
|
## a: An optional custom alphabet. The empty string indicates the default
|
|
## alphabet. If given, the string must consist of 64 unique characters.
|
|
##
|
|
## Returns: The decoded version of *s*.
|
|
##
|
|
## .. zeek:see:: decode_base64_conn encode_base64
|
|
function decode_base64%(s: string, a: string &default=""%): string
|
|
%{
|
|
String* t = zeek::detail::decode_base64(s->AsString(), a->AsString());
|
|
if ( t )
|
|
return zeek::make_intrusive<zeek::StringVal>(t);
|
|
else
|
|
{
|
|
zeek::reporter->Error("error in decoding string %s", s->CheckString());
|
|
return zeek::val_mgr->EmptyString();
|
|
}
|
|
%}
|
|
|
|
## Decodes a Base64-encoded string that was derived from processing a connection.
|
|
## If an error is encountered decoding the string, that will be logged to
|
|
## ``weird.log`` with the associated connection.
|
|
##
|
|
## cid: The identifier of the connection that the encoding originates from.
|
|
##
|
|
## s: The Base64-encoded string.
|
|
##
|
|
## a: An optional custom alphabet. The empty string indicates the default
|
|
## alphabet. If given, the string must consist of 64 unique characters.
|
|
##
|
|
## Returns: The decoded version of *s*.
|
|
##
|
|
## .. zeek:see:: decode_base64
|
|
function decode_base64_conn%(cid: conn_id, s: string, a: string &default=""%): string
|
|
%{
|
|
Connection* conn = session_mgr->FindConnection(cid);
|
|
if ( ! conn )
|
|
{
|
|
zeek::emit_builtin_error("connection ID not a known connection", cid);
|
|
return zeek::val_mgr->EmptyString();
|
|
}
|
|
|
|
String* t = zeek::detail::decode_base64(s->AsString(), a->AsString(), conn);
|
|
if ( t )
|
|
return zeek::make_intrusive<zeek::StringVal>(t);
|
|
else
|
|
{
|
|
zeek::reporter->Error("error in decoding string %s", s->CheckString());
|
|
return zeek::val_mgr->EmptyString();
|
|
}
|
|
%}
|
|
|
|
%%{
|
|
typedef struct {
|
|
uint32_t time_low;
|
|
uint16_t time_mid;
|
|
uint16_t time_hi_and_version;
|
|
uint8_t clock_seq_hi_and_reserved;
|
|
uint8_t clock_seq_low;
|
|
uint8_t node[6];
|
|
} bro_uuid_t;
|
|
%%}
|
|
|
|
## Converts a bytes representation of a UUID into its string form. For example,
|
|
## given a string of 16 bytes, it produces an output string in this format:
|
|
## ``550e8400-e29b-41d4-a716-446655440000``.
|
|
## See `<http://en.wikipedia.org/wiki/Universally_unique_identifier>`_.
|
|
##
|
|
## uuid: The 16 bytes of the UUID.
|
|
##
|
|
## Returns: The string representation of *uuid*.
|
|
function uuid_to_string%(uuid: string%): string
|
|
%{
|
|
if ( uuid->Len() != 16 )
|
|
return zeek::make_intrusive<zeek::StringVal>("<Invalid UUID>");
|
|
|
|
bro_uuid_t* id = (bro_uuid_t*) uuid->Bytes();
|
|
|
|
static char s[1024];
|
|
char* sp = s;
|
|
|
|
sp += snprintf(sp, s + sizeof(s) - sp,
|
|
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
|
id->time_low, id->time_mid, id->time_hi_and_version,
|
|
id->clock_seq_hi_and_reserved, id->clock_seq_low,
|
|
id->node[0],
|
|
id->node[1],
|
|
id->node[2],
|
|
id->node[3],
|
|
id->node[4],
|
|
id->node[5]);
|
|
|
|
return zeek::make_intrusive<zeek::StringVal>(s);
|
|
%}
|
|
|
|
%%{
|
|
char* to_pat_str(int sn, const char* ss)
|
|
{
|
|
const char special_re_char[] = "^$-:\"\\/|*+?.(){}[]";
|
|
|
|
char* pat = new char[sn * 4 + 1];
|
|
int pat_len = 0;
|
|
|
|
for ( int i = 0; i < sn; ++i )
|
|
{
|
|
if ( ! strchr(special_re_char, ss[i]) )
|
|
pat[pat_len++] = ss[i];
|
|
else
|
|
{
|
|
pat[pat_len++] = '\\';
|
|
pat[pat_len++] = ss[i];
|
|
}
|
|
}
|
|
pat[pat_len] = '\0';
|
|
return pat;
|
|
}
|
|
%%}
|
|
|
|
## Escapes a string so that it becomes a valid :zeek:type:`pattern` and can be
|
|
## used with the :zeek:id:`string_to_pattern`. Any character from the set
|
|
## ``^$-:"\/|*+?.(){}[]`` is prefixed with a ``\``.
|
|
##
|
|
## s: The string to escape.
|
|
##
|
|
## Returns: An escaped version of *s* that has the structure of a valid
|
|
## :zeek:type:`pattern`.
|
|
##
|
|
## .. zeek:see:: string_to_pattern
|
|
##
|
|
function convert_for_pattern%(s: string%): string
|
|
%{
|
|
char* t = to_pat_str(s->Len(), (const char*)(s->Bytes()));
|
|
auto ret = zeek::make_intrusive<zeek::StringVal>(t);
|
|
delete [] t;
|
|
return ret;
|
|
%}
|
|
|
|
## Converts a :zeek:type:`string` into a :zeek:type:`pattern`.
|
|
##
|
|
## s: The string to convert.
|
|
##
|
|
## convert: If true, *s* is first passed through the function
|
|
## :zeek:id:`convert_for_pattern` to escape special characters of
|
|
## patterns.
|
|
##
|
|
## Returns: *s* as :zeek:type:`pattern`.
|
|
##
|
|
## .. zeek:see:: convert_for_pattern
|
|
function string_to_pattern%(s: string, convert: bool%): pattern
|
|
%{
|
|
const char* ss = (const char*) (s->Bytes());
|
|
int sn = s->Len();
|
|
char* pat;
|
|
|
|
if ( convert )
|
|
pat = to_pat_str(sn, ss);
|
|
else
|
|
{
|
|
pat = new char[sn+1];
|
|
memcpy(pat, ss, sn);
|
|
pat[sn] = '\0';
|
|
}
|
|
|
|
RE_Matcher* re = new RE_Matcher(pat);
|
|
delete [] pat;
|
|
re->Compile();
|
|
return zeek::make_intrusive<zeek::PatternVal>(re);
|
|
%}
|
|
|
|
## Formats a given time value according to a format string.
|
|
##
|
|
## fmt: The format string. See ``man strftime`` for the syntax.
|
|
##
|
|
## d: The time value.
|
|
##
|
|
## Returns: The time *d* formatted according to *fmt*.
|
|
function strftime%(fmt: string, d: time%) : string
|
|
%{
|
|
static char buffer[128];
|
|
|
|
time_t timeval = time_t(d);
|
|
struct tm t;
|
|
|
|
if ( ! localtime_r(&timeval, &t) ||
|
|
! strftime(buffer, 128, fmt->CheckString(), &t) )
|
|
return zeek::make_intrusive<zeek::StringVal>("<strftime error>");
|
|
|
|
return zeek::make_intrusive<zeek::StringVal>(buffer);
|
|
%}
|
|
|
|
|
|
## Parse a textual representation of a date/time value into a ``time`` type value.
|
|
##
|
|
## fmt: The format string used to parse the following *d* argument. See ``man strftime``
|
|
## for the syntax.
|
|
##
|
|
## d: The string representing the time.
|
|
##
|
|
## Returns: The time value calculated from parsing *d* with *fmt*.
|
|
function strptime%(fmt: string, d: string%) : time
|
|
%{
|
|
const time_t timeval = time_t();
|
|
struct tm t;
|
|
|
|
if ( ! localtime_r(&timeval, &t) ||
|
|
! strptime(d->CheckString(), fmt->CheckString(), &t) )
|
|
{
|
|
zeek::reporter->Warning("strptime conversion failed: fmt:%s d:%s", fmt->CheckString(), d->CheckString());
|
|
return zeek::make_intrusive<zeek::TimeVal>(0.0);
|
|
}
|
|
|
|
double ret = mktime(&t);
|
|
return zeek::make_intrusive<zeek::TimeVal>(ret);
|
|
%}
|
|
|
|
|
|
# ===========================================================================
|
|
#
|
|
# Network Type Processing
|
|
#
|
|
# ===========================================================================
|
|
|
|
## Masks an address down to the number of given upper bits. For example,
|
|
## ``mask_addr(1.2.3.4, 18)`` returns ``1.2.0.0``.
|
|
##
|
|
## a: The address to mask.
|
|
##
|
|
## top_bits_to_keep: The number of top bits to keep in *a*; must be greater
|
|
## than 0 and less than 33 for IPv4, or 129 for IPv6.
|
|
##
|
|
## Returns: The address *a* masked down to *top_bits_to_keep* bits.
|
|
##
|
|
## .. zeek:see:: remask_addr
|
|
function mask_addr%(a: addr, top_bits_to_keep: count%): subnet
|
|
%{
|
|
return zeek::make_intrusive<zeek::SubNetVal>(a->AsAddr(), top_bits_to_keep);
|
|
%}
|
|
|
|
## Takes some top bits (such as a subnet address) from one address and the other
|
|
## bits (intra-subnet part) from a second address and merges them to get a new
|
|
## address. This is useful for anonymizing at subnet level while preserving
|
|
## serial scans.
|
|
##
|
|
## a1: The address to mask with *top_bits_from_a1*.
|
|
##
|
|
## a2: The address to take the remaining bits from.
|
|
##
|
|
## top_bits_from_a1: The number of top bits to keep in *a1*; must be greater
|
|
## than 0 and less than 129. This value is always interpreted
|
|
## relative to the IPv6 bit width (v4-mapped addresses start
|
|
## at bit number 96).
|
|
##
|
|
## Returns: The address *a* masked down to *top_bits_to_keep* bits.
|
|
##
|
|
## .. zeek:see:: mask_addr
|
|
function remask_addr%(a1: addr, a2: addr, top_bits_from_a1: count%): addr
|
|
%{
|
|
zeek::IPAddr addr1(a1->AsAddr());
|
|
addr1.Mask(top_bits_from_a1);
|
|
zeek::IPAddr addr2(a2->AsAddr());
|
|
addr2.ReverseMask(top_bits_from_a1);
|
|
return zeek::make_intrusive<zeek::AddrVal>(addr1|addr2);
|
|
%}
|
|
|
|
## Checks whether a given :zeek:type:`port` has TCP as transport protocol.
|
|
##
|
|
## p: The :zeek:type:`port` to check.
|
|
##
|
|
## Returns: True iff *p* is a TCP port.
|
|
##
|
|
## .. zeek:see:: is_udp_port is_icmp_port
|
|
function is_tcp_port%(p: port%): bool
|
|
%{
|
|
return zeek::val_mgr->Bool(p->IsTCP());
|
|
%}
|
|
|
|
## Checks whether a given :zeek:type:`port` has UDP as transport protocol.
|
|
##
|
|
## p: The :zeek:type:`port` to check.
|
|
##
|
|
## Returns: True iff *p* is a UDP port.
|
|
##
|
|
## .. zeek:see:: is_icmp_port is_tcp_port
|
|
function is_udp_port%(p: port%): bool
|
|
%{
|
|
return zeek::val_mgr->Bool(p->IsUDP());
|
|
%}
|
|
|
|
## Checks whether a given :zeek:type:`port` has ICMP as transport protocol.
|
|
##
|
|
## p: The :zeek:type:`port` to check.
|
|
##
|
|
## Returns: True iff *p* is an ICMP port.
|
|
##
|
|
## .. zeek:see:: is_tcp_port is_udp_port
|
|
function is_icmp_port%(p: port%): bool
|
|
%{
|
|
return zeek::val_mgr->Bool(p->IsICMP());
|
|
%}
|
|
|
|
%%{
|
|
static zeek::EnumValPtr map_conn_type(TransportProto tp)
|
|
{
|
|
switch ( tp ) {
|
|
case TRANSPORT_UNKNOWN:
|
|
return zeek::id::transport_proto->GetEnumVal(0);
|
|
|
|
case TRANSPORT_TCP:
|
|
return zeek::id::transport_proto->GetEnumVal(1);
|
|
|
|
case TRANSPORT_UDP:
|
|
return zeek::id::transport_proto->GetEnumVal(2);
|
|
|
|
case TRANSPORT_ICMP:
|
|
return zeek::id::transport_proto->GetEnumVal(3);
|
|
|
|
default:
|
|
zeek::reporter->InternalError("bad connection type in map_conn_type()");
|
|
}
|
|
|
|
// Cannot be reached;
|
|
assert(false);
|
|
return nullptr; // Make compiler happy.
|
|
}
|
|
%%}
|
|
|
|
## Extracts the transport protocol from a connection.
|
|
##
|
|
## cid: The connection identifier.
|
|
##
|
|
## Returns: The transport protocol of the connection identified by *cid*.
|
|
##
|
|
## .. zeek:see:: get_port_transport_proto
|
|
## get_orig_seq get_resp_seq
|
|
function get_conn_transport_proto%(cid: conn_id%): transport_proto
|
|
%{
|
|
Connection* c = session_mgr->FindConnection(cid);
|
|
if ( ! c )
|
|
{
|
|
zeek::emit_builtin_error("unknown connection id in get_conn_transport_proto()", cid);
|
|
return zeek::id::transport_proto->GetEnumVal(0);
|
|
}
|
|
|
|
return map_conn_type(c->ConnTransport());
|
|
%}
|
|
|
|
## Extracts the transport protocol from a :zeek:type:`port`.
|
|
##
|
|
## p: The port.
|
|
##
|
|
## Returns: The transport protocol of the port *p*.
|
|
##
|
|
## .. zeek:see:: get_conn_transport_proto
|
|
## get_orig_seq get_resp_seq
|
|
function get_port_transport_proto%(p: port%): transport_proto
|
|
%{
|
|
return map_conn_type(p->PortType());
|
|
%}
|
|
|
|
## Checks whether a connection is (still) active.
|
|
##
|
|
## c: The connection id to check.
|
|
##
|
|
## Returns: True if the connection identified by *c* exists.
|
|
##
|
|
## .. zeek:see:: lookup_connection
|
|
function connection_exists%(c: conn_id%): bool
|
|
%{
|
|
if ( session_mgr->FindConnection(c) )
|
|
return zeek::val_mgr->True();
|
|
else
|
|
return zeek::val_mgr->False();
|
|
%}
|
|
|
|
## Returns the :zeek:type:`connection` record for a given connection identifier.
|
|
##
|
|
## cid: The connection ID.
|
|
##
|
|
## Returns: The :zeek:type:`connection` record for *cid*. If *cid* does not point
|
|
## to an existing connection, the function generates a run-time error
|
|
## and returns a dummy value.
|
|
##
|
|
## .. zeek:see:: connection_exists
|
|
function lookup_connection%(cid: conn_id%): connection
|
|
%{
|
|
Connection* conn = session_mgr->FindConnection(cid);
|
|
if ( conn )
|
|
return conn->GetVal();
|
|
|
|
zeek::emit_builtin_error("connection ID not a known connection", cid);
|
|
|
|
// Return a dummy connection record.
|
|
auto c = zeek::make_intrusive<zeek::RecordVal>(zeek::id::connection);
|
|
|
|
auto id_val = zeek::make_intrusive<zeek::RecordVal>(zeek::id::conn_id);
|
|
id_val->Assign(0, zeek::make_intrusive<zeek::AddrVal>((unsigned int) 0));
|
|
id_val->Assign(1, zeek::val_mgr->Port(ntohs(0), TRANSPORT_UDP));
|
|
id_val->Assign(2, zeek::make_intrusive<zeek::AddrVal>((unsigned int) 0));
|
|
id_val->Assign(3, zeek::val_mgr->Port(ntohs(0), TRANSPORT_UDP));
|
|
c->Assign(0, std::move(id_val));
|
|
|
|
auto orig_endp = zeek::make_intrusive<zeek::RecordVal>(zeek::id::endpoint);
|
|
orig_endp->Assign(0, 0);
|
|
orig_endp->Assign(1, 0);
|
|
|
|
auto resp_endp = zeek::make_intrusive<zeek::RecordVal>(zeek::id::endpoint);
|
|
resp_endp->Assign(0, 0);
|
|
resp_endp->Assign(1, 0);
|
|
|
|
c->Assign(1, std::move(orig_endp));
|
|
c->Assign(2, std::move(resp_endp));
|
|
|
|
c->AssignTime(3, zeek::run_state::network_time);
|
|
c->AssignInterval(4, 0.0);
|
|
c->Assign(5, zeek::make_intrusive<zeek::TableVal>(zeek::id::string_set)); // service
|
|
c->Assign(6, zeek::val_mgr->EmptyString()); // history
|
|
|
|
return c;
|
|
%}
|
|
|
|
%%{
|
|
const char* conn_id_string(zeek::Val* c)
|
|
{
|
|
auto id_r = c->As<zeek::RecordVal*>()->GetFieldAs<zeek::RecordVal>(0);
|
|
|
|
const zeek::IPAddr& orig_h = id_r->GetFieldAs<zeek::AddrVal>(0);
|
|
uint32_t orig_p = id_r->GetFieldAs<zeek::PortVal>(1)->Port();
|
|
const zeek::IPAddr& resp_h = id_r->GetFieldAs<zeek::AddrVal>(2);
|
|
uint32_t resp_p = id_r->GetFieldAs<zeek::PortVal>(3)->Port();
|
|
|
|
return zeek::util::fmt("%s/%u -> %s/%u\n", orig_h.AsString().c_str(), orig_p,
|
|
resp_h.AsString().c_str(), resp_p);
|
|
}
|
|
%%}
|
|
|
|
## Writes the current packet to a file.
|
|
##
|
|
## file_name: The name of the file to write the packet to.
|
|
##
|
|
## Returns: True on success.
|
|
##
|
|
## .. zeek:see:: dump_packet get_current_packet
|
|
function dump_current_packet%(file_name: string%) : bool
|
|
%{
|
|
const Packet* pkt;
|
|
auto* pkt_src = dynamic_cast<zeek::iosource::PktSrc*>(zeek::run_state::detail::current_iosrc);
|
|
|
|
if ( ! pkt_src || ! pkt_src->GetCurrentPacket(&pkt) )
|
|
return zeek::val_mgr->False();
|
|
|
|
if ( addl_pkt_dumper && addl_pkt_dumper->Path() != file_name->CheckString())
|
|
{
|
|
addl_pkt_dumper->Close();
|
|
addl_pkt_dumper = nullptr;
|
|
}
|
|
|
|
if ( ! addl_pkt_dumper )
|
|
addl_pkt_dumper = zeek::iosource_mgr->OpenPktDumper(file_name->CheckString(), true);
|
|
|
|
if ( addl_pkt_dumper )
|
|
{
|
|
addl_pkt_dumper->Dump(pkt);
|
|
}
|
|
|
|
return zeek::val_mgr->Bool( addl_pkt_dumper && ! addl_pkt_dumper->IsError());
|
|
%}
|
|
|
|
## Returns the currently processed PCAP packet.
|
|
##
|
|
## Returns: The currently processed packet, which is a record
|
|
## containing the timestamp, ``snaplen``, and packet data.
|
|
##
|
|
## .. zeek:see:: dump_current_packet dump_packet
|
|
function get_current_packet%(%) : pcap_packet
|
|
%{
|
|
static auto pcap_packet = zeek::id::find_type<zeek::RecordType>("pcap_packet");
|
|
const Packet* p;
|
|
auto pkt = zeek::make_intrusive<zeek::RecordVal>(pcap_packet);
|
|
zeek::iosource::PktSrc* pkt_src = zeek::run_state::detail::current_packet_source();
|
|
|
|
if ( ! pkt_src || ! pkt_src->GetCurrentPacket(&p) )
|
|
{
|
|
pkt->Assign(0, 0);
|
|
pkt->Assign(1, 0);
|
|
pkt->Assign(2, 0);
|
|
pkt->Assign(3, 0);
|
|
pkt->Assign(4, zeek::val_mgr->EmptyString());
|
|
pkt->Assign(5, zeek::BifType::Enum::link_encap->GetEnumVal(BifEnum::LINK_UNKNOWN));
|
|
return pkt;
|
|
}
|
|
|
|
pkt->Assign(0, static_cast<uint32_t>(p->ts.tv_sec));
|
|
pkt->Assign(1, static_cast<uint32_t>(p->ts.tv_usec));
|
|
pkt->Assign(2, p->cap_len);
|
|
pkt->Assign(3, p->len);
|
|
pkt->Assign(4, zeek::make_intrusive<zeek::StringVal>(p->cap_len, (const char*)p->data));
|
|
pkt->Assign(5, zeek::BifType::Enum::link_encap->GetEnumVal(p->link_type));
|
|
|
|
return pkt;
|
|
%}
|
|
|
|
## Function to get the raw headers of the currently processed packet.
|
|
##
|
|
## Returns: The :zeek:type:`raw_pkt_hdr` record containing the Layer 2, 3 and
|
|
## 4 headers of the currently processed packet.
|
|
##
|
|
## .. zeek:see:: raw_pkt_hdr get_current_packet
|
|
function get_current_packet_header%(%) : raw_pkt_hdr
|
|
%{
|
|
const Packet* p;
|
|
zeek::iosource::PktSrc* pkt_src = zeek::run_state::detail::current_packet_source();
|
|
|
|
if ( pkt_src && pkt_src->GetCurrentPacket(&p) )
|
|
{
|
|
return p->ToRawPktHdrVal();
|
|
}
|
|
|
|
static auto raw_pkt_hdr_type = zeek::id::find_type<zeek::RecordType>("raw_pkt_hdr");
|
|
auto hdr = zeek::make_intrusive<zeek::RecordVal>(raw_pkt_hdr_type);
|
|
return hdr;
|
|
%}
|
|
|
|
## Writes a given packet to a file.
|
|
##
|
|
## pkt: The PCAP packet.
|
|
##
|
|
## file_name: The name of the file to write *pkt* to.
|
|
##
|
|
## Returns: True on success
|
|
##
|
|
## .. zeek:see:: get_current_packet dump_current_packet
|
|
function dump_packet%(pkt: pcap_packet, file_name: string%) : bool
|
|
%{
|
|
if ( addl_pkt_dumper && addl_pkt_dumper->Path() != file_name->CheckString())
|
|
{
|
|
addl_pkt_dumper->Close();
|
|
addl_pkt_dumper = nullptr;
|
|
}
|
|
|
|
if ( ! addl_pkt_dumper )
|
|
addl_pkt_dumper = zeek::iosource_mgr->OpenPktDumper(file_name->CheckString(), true);
|
|
|
|
if ( ! addl_pkt_dumper->IsError() )
|
|
{
|
|
pkt_timeval ts;
|
|
uint32_t caplen, len, link_type;
|
|
u_char *data;
|
|
|
|
auto pkt_r = pkt->As<zeek::RecordVal*>();
|
|
|
|
ts.tv_sec = pkt_r->GetFieldAs<zeek::CountVal>(0);
|
|
ts.tv_usec = pkt_r->GetFieldAs<zeek::CountVal>(1);
|
|
caplen = pkt_r->GetFieldAs<zeek::CountVal>(2);
|
|
len = pkt_r->GetFieldAs<zeek::CountVal>(3);
|
|
data = pkt_r->GetFieldAs<zeek::StringVal>(4)->Bytes();
|
|
link_type = pkt_r->GetFieldAs<zeek::EnumVal>(5);
|
|
Packet p(link_type, &ts, caplen, len, data, true);
|
|
|
|
addl_pkt_dumper->Dump(&p);
|
|
}
|
|
|
|
return zeek::val_mgr->Bool(addl_pkt_dumper && ! addl_pkt_dumper->IsError());
|
|
%}
|
|
|
|
%%{
|
|
#include "zeek/DNS_Mgr.h"
|
|
#include "zeek/Trigger.h"
|
|
|
|
class LookupHostCallback : public zeek::detail::DNS_Mgr::LookupCallback {
|
|
public:
|
|
LookupHostCallback(zeek::detail::trigger::Trigger* arg_trigger, const zeek::detail::CallExpr* arg_call,
|
|
bool arg_lookup_name)
|
|
{
|
|
Ref(arg_trigger);
|
|
trigger = arg_trigger;
|
|
call = arg_call;
|
|
lookup_name = arg_lookup_name;
|
|
}
|
|
|
|
~LookupHostCallback()
|
|
{
|
|
Unref(trigger);
|
|
}
|
|
|
|
// Overridden from zeek::detail::DNS_Mgr:Lookup:Callback.
|
|
virtual void Resolved(const char* name)
|
|
{
|
|
zeek::Val* result = new zeek::StringVal(name);
|
|
trigger->Cache(call, result);
|
|
Unref(result);
|
|
trigger->Release();
|
|
}
|
|
|
|
virtual void Resolved(zeek::TableVal* addrs)
|
|
{
|
|
// No Ref() for addrs.
|
|
trigger->Cache(call, addrs);
|
|
trigger->Release();
|
|
}
|
|
|
|
virtual void Timeout()
|
|
{
|
|
if ( lookup_name )
|
|
{
|
|
zeek::Val* result = new zeek::StringVal("<\?\?\?>");
|
|
trigger->Cache(call, result);
|
|
Unref(result);
|
|
}
|
|
|
|
else
|
|
{
|
|
auto* lv = new zeek::ListVal(zeek::TYPE_ADDR);
|
|
lv->Append(zeek::make_intrusive<zeek::AddrVal>("0.0.0.0"));
|
|
auto result = lv->ToSetVal();
|
|
trigger->Cache(call, result.get());
|
|
Unref(lv);
|
|
}
|
|
|
|
trigger->Release();
|
|
}
|
|
|
|
private:
|
|
zeek::detail::trigger::Trigger* trigger;
|
|
const zeek::detail::CallExpr* call;
|
|
bool lookup_name;
|
|
};
|
|
%%}
|
|
|
|
## Issues an asynchronous reverse DNS lookup and delays the function result.
|
|
## This function can therefore only be called inside a ``when`` condition,
|
|
## e.g., ``when ( local host = lookup_addr(10.0.0.1) ) { f(host); }``.
|
|
##
|
|
## host: The IP address to lookup.
|
|
##
|
|
## Returns: The DNS name of *host*.
|
|
##
|
|
## .. zeek:see:: lookup_hostname
|
|
function lookup_addr%(host: addr%) : string
|
|
%{
|
|
// FIXME: It should be easy to adapt the function to synchronous
|
|
// lookups if we're reading a trace.
|
|
zeek::detail::trigger::Trigger* trigger = frame->GetTrigger();
|
|
|
|
if ( ! trigger)
|
|
{
|
|
zeek::emit_builtin_error("lookup_addr() can only be called inside a when-condition");
|
|
return zeek::make_intrusive<zeek::StringVal>("<error>");
|
|
}
|
|
|
|
frame->SetDelayed();
|
|
trigger->Hold();
|
|
|
|
zeek::detail::dns_mgr->AsyncLookupAddr(host->AsAddr(),
|
|
new LookupHostCallback(trigger, frame->GetCall(), true));
|
|
return nullptr;
|
|
%}
|
|
|
|
## Issues an asynchronous TEXT DNS lookup and delays the function result.
|
|
## This function can therefore only be called inside a ``when`` condition,
|
|
## e.g., ``when ( local h = lookup_hostname_txt("www.zeek.org") ) { f(h); }``.
|
|
##
|
|
## host: The hostname to lookup.
|
|
##
|
|
## Returns: The DNS TXT record associated with *host*.
|
|
##
|
|
## .. zeek:see:: lookup_hostname
|
|
function lookup_hostname_txt%(host: string%) : string
|
|
%{
|
|
// FIXME: Is should be easy to adapt the function to synchronous
|
|
// lookups if we're reading a trace.
|
|
zeek::detail::trigger::Trigger* trigger = frame->GetTrigger();
|
|
|
|
if ( ! trigger)
|
|
{
|
|
zeek::emit_builtin_error("lookup_hostname_txt() can only be called inside a when-condition");
|
|
return zeek::make_intrusive<zeek::StringVal>("<error>");
|
|
}
|
|
|
|
frame->SetDelayed();
|
|
trigger->Hold();
|
|
|
|
zeek::detail::dns_mgr->AsyncLookupNameText(host->CheckString(),
|
|
new LookupHostCallback(trigger, frame->GetCall(), true));
|
|
return nullptr;
|
|
%}
|
|
|
|
## Issues an asynchronous DNS lookup and delays the function result.
|
|
## This function can therefore only be called inside a ``when`` condition,
|
|
## e.g., ``when ( local h = lookup_hostname("www.zeek.org") ) { f(h); }``.
|
|
##
|
|
## host: The hostname to lookup.
|
|
##
|
|
## Returns: A set of DNS A and AAAA records associated with *host*.
|
|
##
|
|
## .. zeek:see:: lookup_addr
|
|
function lookup_hostname%(host: string%) : addr_set
|
|
%{
|
|
// FIXME: Is should be easy to adapt the function to synchronous
|
|
// lookups if we're reading a trace.
|
|
zeek::detail::trigger::Trigger* trigger = frame->GetTrigger();
|
|
|
|
if ( ! trigger)
|
|
{
|
|
zeek::emit_builtin_error("lookup_hostname() can only be called inside a when-condition");
|
|
return zeek::make_intrusive<zeek::StringVal>("<error>");
|
|
}
|
|
|
|
frame->SetDelayed();
|
|
trigger->Hold();
|
|
|
|
zeek::detail::dns_mgr->AsyncLookupName(host->CheckString(),
|
|
new LookupHostCallback(trigger, frame->GetCall(), false));
|
|
return nullptr;
|
|
%}
|
|
|
|
%%{
|
|
#ifdef USE_GEOIP
|
|
#include <chrono>
|
|
|
|
extern "C" {
|
|
#include <maxminddb.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip.h>
|
|
}
|
|
|
|
static int mmdb_msg_count = 0;
|
|
static constexpr int mmdb_msg_limit = 20;
|
|
static double mmdb_msg_suppression_time = 0;
|
|
static constexpr double mmdb_msg_suppression_duration = 300;
|
|
|
|
static void report_mmdb_msg(const char* format, ...)
|
|
{
|
|
if ( zeek::run_state::network_time > mmdb_msg_suppression_time + mmdb_msg_suppression_duration )
|
|
{
|
|
mmdb_msg_count = 0;
|
|
mmdb_msg_suppression_time = zeek::run_state::network_time;
|
|
}
|
|
|
|
if ( mmdb_msg_count >= mmdb_msg_limit )
|
|
return;
|
|
|
|
++mmdb_msg_count;
|
|
|
|
va_list al;
|
|
va_start(al, format);
|
|
std::string msg = zeek::util::fmt(format, al);
|
|
va_end(al);
|
|
|
|
zeek::reporter->Info("%s", msg.data());
|
|
}
|
|
|
|
class MMDB {
|
|
public:
|
|
MMDB(const char* filename, struct stat info);
|
|
|
|
~MMDB();
|
|
|
|
MMDB_lookup_result_s Lookup(const struct sockaddr* const sa);
|
|
bool StaleDB();
|
|
const char* Filename();
|
|
|
|
private:
|
|
MMDB_s mmdb;
|
|
struct stat file_info;
|
|
bool lookup_error;
|
|
std::chrono::time_point<std::chrono::steady_clock> last_check;
|
|
};
|
|
|
|
MMDB::MMDB(const char* filename, struct stat info)
|
|
: file_info(info), lookup_error{false},
|
|
last_check{std::chrono::steady_clock::now()}
|
|
{
|
|
int status = MMDB_open(filename, MMDB_MODE_MMAP, &mmdb);
|
|
|
|
if ( MMDB_SUCCESS != status )
|
|
{
|
|
throw std::runtime_error(MMDB_strerror(status));
|
|
}
|
|
}
|
|
|
|
MMDB::~MMDB()
|
|
{
|
|
MMDB_close(&mmdb);
|
|
}
|
|
|
|
MMDB_lookup_result_s MMDB::Lookup(const struct sockaddr* const sa)
|
|
{
|
|
int mmdb_error;
|
|
MMDB_lookup_result_s result = MMDB_lookup_sockaddr(&mmdb, sa, &mmdb_error);
|
|
|
|
if ( MMDB_SUCCESS != mmdb_error )
|
|
{
|
|
lookup_error = true;
|
|
throw std::runtime_error(MMDB_strerror(mmdb_error));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Check to see if the Maxmind DB should be closed and reopened. This will
|
|
// happen if there was a lookup error or if the mmap'd file has been replaced
|
|
// by an external process.
|
|
bool MMDB::StaleDB()
|
|
{
|
|
struct stat buf;
|
|
using Clock = std::chrono::steady_clock;
|
|
std::chrono::time_point<Clock> now = Clock::now();
|
|
|
|
if ( lookup_error )
|
|
return true;
|
|
|
|
// Only perform stat once per 5 minutes.
|
|
using Min = std::chrono::minutes;
|
|
if ( std::chrono::duration_cast<Min>(now - last_check).count() < 5 )
|
|
return false;
|
|
|
|
last_check = now;
|
|
|
|
if ( 0 != stat(mmdb.filename, &buf) )
|
|
return true;
|
|
|
|
if ( buf.st_ino != file_info.st_ino || buf.st_mtime != file_info.st_mtime )
|
|
{
|
|
report_mmdb_msg("Inode change detected for MaxMind DB [%s]",
|
|
mmdb.filename);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const char* MMDB::Filename()
|
|
{
|
|
return mmdb.filename;
|
|
}
|
|
|
|
std::unique_ptr<MMDB> mmdb_loc;
|
|
std::unique_ptr<MMDB> mmdb_asn;
|
|
static bool did_mmdb_loc_db_error = false;
|
|
static bool did_mmdb_asn_db_error = false;
|
|
|
|
static bool mmdb_open(const char* filename, bool asn)
|
|
{
|
|
struct stat buf;
|
|
|
|
if ( 0 != stat(filename, &buf) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
if ( asn )
|
|
{
|
|
mmdb_asn.reset(new MMDB(filename, buf));
|
|
}
|
|
else
|
|
{
|
|
mmdb_loc.reset(new MMDB(filename, buf));
|
|
}
|
|
}
|
|
|
|
catch ( const std::exception& e )
|
|
{
|
|
if ( asn )
|
|
did_mmdb_asn_db_error = false;
|
|
else
|
|
did_mmdb_loc_db_error = false;
|
|
|
|
report_mmdb_msg("Failed to open MaxMind DB: %s [%s]", filename,
|
|
e.what());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool mmdb_open_loc(const char* filename)
|
|
{
|
|
return mmdb_open(filename, false);
|
|
}
|
|
|
|
static bool mmdb_open_asn(const char* filename)
|
|
{
|
|
return mmdb_open(filename, true);
|
|
}
|
|
|
|
static void mmdb_check_loc()
|
|
{
|
|
if ( mmdb_loc && mmdb_loc->StaleDB() )
|
|
{
|
|
report_mmdb_msg("Closing stale MaxMind DB [%s]", mmdb_loc->Filename());
|
|
did_mmdb_loc_db_error = false;
|
|
mmdb_loc.release();
|
|
}
|
|
}
|
|
|
|
static void mmdb_check_asn()
|
|
{
|
|
if ( mmdb_asn && mmdb_asn->StaleDB() )
|
|
{
|
|
report_mmdb_msg("Closing stale MaxMind DB [%s]", mmdb_asn->Filename());
|
|
did_mmdb_asn_db_error = false;
|
|
mmdb_asn.release();
|
|
}
|
|
}
|
|
|
|
static bool mmdb_lookup(const zeek::IPAddr& addr, MMDB_lookup_result_s& result,
|
|
bool asn)
|
|
{
|
|
struct sockaddr_storage ss = {0};
|
|
|
|
if ( IPv4 == addr.GetFamily() )
|
|
{
|
|
struct sockaddr_in* sa = (struct sockaddr_in*)&ss;
|
|
sa->sin_family = AF_INET;
|
|
addr.CopyIPv4(&sa->sin_addr);
|
|
}
|
|
|
|
else
|
|
{
|
|
struct sockaddr_in6* sa = (struct sockaddr_in6*)&ss;
|
|
sa->sin6_family = AF_INET6;
|
|
addr.CopyIPv6(&sa->sin6_addr);
|
|
}
|
|
|
|
try
|
|
{
|
|
result = asn ? mmdb_asn->Lookup((struct sockaddr*)&ss)
|
|
: mmdb_loc->Lookup((struct sockaddr*)&ss);
|
|
}
|
|
|
|
catch ( const std::exception& e )
|
|
{
|
|
report_mmdb_msg("MaxMind DB lookup location error [%s]", e.what());
|
|
return false;
|
|
}
|
|
|
|
return result.found_entry;
|
|
}
|
|
|
|
static bool mmdb_lookup_loc(const zeek::IPAddr& addr, MMDB_lookup_result_s& result)
|
|
{
|
|
return mmdb_lookup(addr, result, false);
|
|
}
|
|
|
|
static bool mmdb_lookup_asn(const zeek::IPAddr& addr, MMDB_lookup_result_s& result)
|
|
{
|
|
return mmdb_lookup(addr, result, true);
|
|
}
|
|
|
|
static zeek::ValPtr mmdb_getvalue(MMDB_entry_data_s* entry_data, int status,
|
|
int data_type )
|
|
{
|
|
switch (status)
|
|
{
|
|
case MMDB_SUCCESS:
|
|
if ( entry_data->has_data )
|
|
{
|
|
switch (data_type)
|
|
{
|
|
case MMDB_DATA_TYPE_UTF8_STRING:
|
|
return zeek::make_intrusive<zeek::StringVal>(
|
|
entry_data->data_size, entry_data->utf8_string);
|
|
break;
|
|
|
|
case MMDB_DATA_TYPE_DOUBLE:
|
|
return zeek::make_intrusive<zeek::DoubleVal>(entry_data->double_value);
|
|
break;
|
|
|
|
case MMDB_DATA_TYPE_UINT32:
|
|
return zeek::val_mgr->Count(entry_data->uint32);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR:
|
|
// key doesn't exist, nothing to do
|
|
break;
|
|
|
|
default:
|
|
report_mmdb_msg("MaxMind DB error [%s]", MMDB_strerror(status));
|
|
break;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static bool mmdb_try_open_loc ()
|
|
{
|
|
// City database is always preferred over Country database.
|
|
const auto& mmdb_dir_val = zeek::detail::global_scope()->Find("mmdb_dir")->GetVal();
|
|
std::string mmdb_dir = mmdb_dir_val->AsString()->CheckString();
|
|
|
|
if ( ! mmdb_dir.empty() )
|
|
{
|
|
auto d = mmdb_dir + "/GeoLite2-City.mmdb";
|
|
|
|
if ( mmdb_open_loc(d.data()) )
|
|
return true;
|
|
|
|
d = mmdb_dir + "/GeoLite2-Country.mmdb";
|
|
|
|
if ( mmdb_open_loc(d.data()) )
|
|
return true;;
|
|
}
|
|
|
|
return mmdb_open_loc("/usr/share/GeoIP/GeoLite2-City.mmdb")
|
|
|| mmdb_open_loc("/var/lib/GeoIP/GeoLite2-City.mmdb")
|
|
|| mmdb_open_loc("/usr/local/share/GeoIP/GeoLite2-City.mmdb")
|
|
|| mmdb_open_loc("/usr/local/var/GeoIP/GeoLite2-City.mmdb")
|
|
|| mmdb_open_loc("/usr/share/GeoIP/GeoLite2-Country.mmdb")
|
|
|| mmdb_open_loc("/var/lib/GeoIP/GeoLite2-Country.mmdb")
|
|
|| mmdb_open_loc("/usr/local/share/GeoIP/GeoLite2-Country.mmdb")
|
|
|| mmdb_open_loc("/usr/local/var/GeoIP/GeoLite2-Country.mmdb");
|
|
}
|
|
|
|
static bool mmdb_try_open_asn ()
|
|
{
|
|
const auto& mmdb_dir_val = zeek::detail::global_scope()->Find("mmdb_dir")->GetVal();
|
|
std::string mmdb_dir = mmdb_dir_val->AsString()->CheckString();
|
|
|
|
if ( ! mmdb_dir.empty() )
|
|
{
|
|
auto d = mmdb_dir + "/GeoLite2-ASN.mmdb";
|
|
|
|
if ( mmdb_open_asn(d.data()) )
|
|
return true;
|
|
}
|
|
|
|
return mmdb_open_asn("/usr/share/GeoIP/GeoLite2-ASN.mmdb")
|
|
|| mmdb_open_asn("/var/lib/GeoIP/GeoLite2-ASN.mmdb")
|
|
|| mmdb_open_asn("/usr/local/share/GeoIP/GeoLite2-ASN.mmdb")
|
|
|| mmdb_open_asn("/usr/local/var/GeoIP/GeoLite2-ASN.mmdb");
|
|
}
|
|
|
|
#endif
|
|
%%}
|
|
|
|
## Initializes MMDB for later use of lookup_location.
|
|
## Requires Zeek to be built with ``libmaxminddb``.
|
|
##
|
|
## f: The filename of the MaxMind City or Country DB.
|
|
##
|
|
## Returns: A boolean indicating whether the db was successfully opened.
|
|
##
|
|
## .. zeek:see:: lookup_asn
|
|
function mmdb_open_location_db%(f: string%) : bool
|
|
%{
|
|
#ifdef USE_GEOIP
|
|
return zeek::val_mgr->Bool(mmdb_open_loc(f->CheckString()));
|
|
#else
|
|
return zeek::val_mgr->False();
|
|
#endif
|
|
%}
|
|
|
|
## Initializes MMDB for later use of lookup_asn.
|
|
## Requires Zeek to be built with ``libmaxminddb``.
|
|
##
|
|
## f: The filename of the MaxMind ASN DB.
|
|
##
|
|
## Returns: A boolean indicating whether the db was successfully opened.
|
|
##
|
|
## .. zeek:see:: lookup_asn
|
|
function mmdb_open_asn_db%(f: string%) : bool
|
|
%{
|
|
#ifdef USE_GEOIP
|
|
return zeek::val_mgr->Bool(mmdb_open_asn(f->CheckString()));
|
|
#else
|
|
return zeek::val_mgr->False();
|
|
#endif
|
|
%}
|
|
|
|
## Performs a geo-lookup of an IP address.
|
|
## Requires Zeek to be built with ``libmaxminddb``.
|
|
##
|
|
## a: The IP address to lookup.
|
|
##
|
|
## Returns: A record with country, region, city, latitude, and longitude.
|
|
##
|
|
## .. zeek:see:: lookup_asn
|
|
function lookup_location%(a: addr%) : geo_location
|
|
%{
|
|
static auto geo_location = zeek::id::find_type<zeek::RecordType>("geo_location");
|
|
auto location = zeek::make_intrusive<zeek::RecordVal>(geo_location);
|
|
|
|
#ifdef USE_GEOIP
|
|
mmdb_check_loc();
|
|
if ( ! mmdb_loc )
|
|
{
|
|
if ( ! mmdb_try_open_loc() )
|
|
{
|
|
if ( ! did_mmdb_loc_db_error )
|
|
{
|
|
did_mmdb_loc_db_error = true;
|
|
zeek::emit_builtin_error("Failed to open GeoIP location database");
|
|
}
|
|
|
|
return location;
|
|
}
|
|
}
|
|
|
|
MMDB_lookup_result_s result;
|
|
|
|
if ( mmdb_lookup_loc(a->AsAddr(), result) )
|
|
{
|
|
MMDB_entry_data_s entry_data;
|
|
int status;
|
|
|
|
// Get Country ISO Code
|
|
status = MMDB_get_value(&result.entry, &entry_data,
|
|
"country", "iso_code", nullptr);
|
|
location->Assign(0, mmdb_getvalue(&entry_data, status,
|
|
MMDB_DATA_TYPE_UTF8_STRING));
|
|
|
|
// Get Major Subdivision ISO Code
|
|
status = MMDB_get_value(&result.entry, &entry_data,
|
|
"subdivisions", "0", "iso_code", nullptr);
|
|
location->Assign(1, mmdb_getvalue(&entry_data, status,
|
|
MMDB_DATA_TYPE_UTF8_STRING));
|
|
|
|
// Get City English Name
|
|
status = MMDB_get_value(&result.entry, &entry_data,
|
|
"city", "names", "en", nullptr);
|
|
location->Assign(2, mmdb_getvalue(&entry_data, status,
|
|
MMDB_DATA_TYPE_UTF8_STRING));
|
|
|
|
// Get Location Latitude
|
|
status = MMDB_get_value(&result.entry, &entry_data,
|
|
"location", "latitude", nullptr);
|
|
location->Assign(3, mmdb_getvalue(&entry_data, status,
|
|
MMDB_DATA_TYPE_DOUBLE));
|
|
|
|
// Get Location Longitude
|
|
status = MMDB_get_value(&result.entry, &entry_data,
|
|
"location", "longitude", nullptr);
|
|
location->Assign(4, mmdb_getvalue(&entry_data, status,
|
|
MMDB_DATA_TYPE_DOUBLE));
|
|
|
|
return location;
|
|
}
|
|
|
|
#else // not USE_GEOIP
|
|
static int missing_geoip_reported = 0;
|
|
|
|
if ( ! missing_geoip_reported )
|
|
{
|
|
zeek::emit_builtin_error("Zeek was not configured for GeoIP support");
|
|
missing_geoip_reported = 1;
|
|
}
|
|
#endif
|
|
|
|
// We can get here even if we have MMDB support if we weren't
|
|
// able to initialize it or it didn't return any information for
|
|
// the address.
|
|
|
|
return location;
|
|
%}
|
|
|
|
## Performs an ASN lookup of an IP address.
|
|
## Requires Zeek to be built with ``libmaxminddb``.
|
|
##
|
|
## a: The IP address to lookup.
|
|
##
|
|
## Returns: The number of the ASN that contains *a*.
|
|
##
|
|
## .. zeek:see:: lookup_location
|
|
function lookup_asn%(a: addr%) : count
|
|
%{
|
|
#ifdef USE_GEOIP
|
|
mmdb_check_asn();
|
|
if ( ! mmdb_asn )
|
|
{
|
|
if ( ! mmdb_try_open_asn() )
|
|
{
|
|
if ( ! did_mmdb_asn_db_error )
|
|
{
|
|
did_mmdb_asn_db_error = true;
|
|
zeek::emit_builtin_error("Failed to open GeoIP ASN database");
|
|
}
|
|
|
|
return zeek::val_mgr->Count(0);
|
|
}
|
|
}
|
|
|
|
MMDB_lookup_result_s result;
|
|
|
|
if ( mmdb_lookup_asn(a->AsAddr(), result) )
|
|
{
|
|
MMDB_entry_data_s entry_data;
|
|
int status;
|
|
|
|
// Get Autonomous System Number
|
|
status = MMDB_get_value(&result.entry, &entry_data,
|
|
"autonomous_system_number", nullptr);
|
|
auto asn = mmdb_getvalue(&entry_data, status, MMDB_DATA_TYPE_UINT32);
|
|
return asn == nullptr ? zeek::val_mgr->Count(0) : asn;
|
|
}
|
|
|
|
#else // not USE_GEOIP
|
|
static int missing_geoip_reported = 0;
|
|
|
|
if ( ! missing_geoip_reported )
|
|
{
|
|
zeek::emit_builtin_error("Zeek was not configured for GeoIP ASN support");
|
|
missing_geoip_reported = 1;
|
|
}
|
|
#endif
|
|
|
|
// We can get here even if we have GeoIP support, if we weren't
|
|
// able to initialize it or it didn't return any information for
|
|
// the address.
|
|
return zeek::val_mgr->Count(0);
|
|
%}
|
|
|
|
## Calculates distance between two geographic locations using the haversine
|
|
## formula. Latitudes and longitudes must be given in degrees, where southern
|
|
## hemispere latitudes are negative and western hemisphere longitudes are
|
|
## negative.
|
|
##
|
|
## lat1: Latitude (in degrees) of location 1.
|
|
##
|
|
## long1: Longitude (in degrees) of location 1.
|
|
##
|
|
## lat2: Latitude (in degrees) of location 2.
|
|
##
|
|
## long2: Longitude (in degrees) of location 2.
|
|
##
|
|
## Returns: Distance in miles.
|
|
##
|
|
## .. zeek:see:: haversine_distance_ip
|
|
function haversine_distance%(lat1: double, long1: double, lat2: double, long2: double%): double
|
|
%{
|
|
const double PI = 3.14159;
|
|
const double RADIUS = 3958.8; // Earth's radius in miles.
|
|
|
|
double s1 = sin((lat2 - lat1) * PI/360);
|
|
double s2 = sin((long2 - long1) * PI/360);
|
|
double a = s1 * s1 + cos(lat1 * PI/180) * cos(lat2 * PI/180) * s2 * s2;
|
|
double distance = 2 * RADIUS * asin(sqrt(a));
|
|
|
|
return zeek::make_intrusive<zeek::DoubleVal>(distance);
|
|
%}
|
|
|
|
## Converts UNIX file permissions given by a mode to an ASCII string.
|
|
##
|
|
## mode: The permissions (an octal number like 0644 converted to decimal).
|
|
##
|
|
## Returns: A string representation of *mode* in the format
|
|
## ``rw[xsS]rw[xsS]rw[xtT]``.
|
|
function file_mode%(mode: count%): string
|
|
%{
|
|
char str[12];
|
|
char *p = str;
|
|
|
|
/* usr */
|
|
if (mode & S_IRUSR)
|
|
*p++ = 'r';
|
|
else
|
|
*p++ = '-';
|
|
|
|
if (mode & S_IWUSR)
|
|
*p++ = 'w';
|
|
else
|
|
*p++ = '-';
|
|
|
|
switch (mode & (S_IXUSR | S_ISUID)) {
|
|
case 0:
|
|
*p++ = '-';
|
|
break;
|
|
case S_IXUSR:
|
|
*p++ = 'x';
|
|
break;
|
|
case S_ISUID:
|
|
*p++ = 'S';
|
|
break;
|
|
case S_IXUSR | S_ISUID:
|
|
*p++ = 's';
|
|
break;
|
|
}
|
|
|
|
/* group */
|
|
if (mode & S_IRGRP)
|
|
*p++ = 'r';
|
|
else
|
|
*p++ = '-';
|
|
if (mode & S_IWGRP)
|
|
*p++ = 'w';
|
|
else
|
|
*p++ = '-';
|
|
|
|
switch (mode & (S_IXGRP | S_ISGID)) {
|
|
case 0:
|
|
*p++ = '-';
|
|
break;
|
|
case S_IXGRP:
|
|
*p++ = 'x';
|
|
break;
|
|
case S_ISGID:
|
|
*p++ = 'S';
|
|
break;
|
|
case S_IXGRP | S_ISGID:
|
|
*p++ = 's';
|
|
break;
|
|
}
|
|
|
|
/* other */
|
|
if (mode & S_IROTH)
|
|
*p++ = 'r';
|
|
else
|
|
*p++ = '-';
|
|
if (mode & S_IWOTH)
|
|
*p++ = 'w';
|
|
else
|
|
*p++ = '-';
|
|
|
|
switch (mode & (S_IXOTH | S_ISVTX)) {
|
|
case 0:
|
|
*p++ = '-';
|
|
break;
|
|
case S_IXOTH:
|
|
*p++ = 'x';
|
|
break;
|
|
case S_ISVTX:
|
|
*p++ = 'T';
|
|
break;
|
|
case S_IXOTH | S_ISVTX:
|
|
*p++ = 't';
|
|
break;
|
|
}
|
|
|
|
*p = '\0';
|
|
|
|
return zeek::make_intrusive<zeek::StringVal>(str);
|
|
%}
|
|
|
|
# ===========================================================================
|
|
#
|
|
# Controlling Analyzer Behavior
|
|
#
|
|
# ===========================================================================
|
|
|
|
%%{
|
|
#include "zeek/analyzer/Manager.h"
|
|
%%}
|
|
|
|
## Disables the analyzer which raised the current event (if the analyzer
|
|
## belongs to the given connection).
|
|
##
|
|
## cid: The connection identifier.
|
|
##
|
|
## aid: The analyzer ID.
|
|
##
|
|
## err_if_no_conn: Emit an error message if the connection does not exit.
|
|
##
|
|
## prevent: Prevent the same analyzer type from being attached in the future.
|
|
## This is useful for preventing the same analyzer from being
|
|
## automatically reattached in the future, e.g. as a result of a
|
|
## DPD signature suddenly matching.
|
|
##
|
|
## Returns: True if the connection identified by *cid* exists and has analyzer
|
|
## *aid* and it is scheduled for removal.
|
|
##
|
|
## .. zeek:see:: Analyzer::schedule_analyzer Analyzer::name
|
|
function disable_analyzer%(cid: conn_id, aid: count, err_if_no_conn: bool &default=T, prevent: bool &default=F%) : bool
|
|
%{
|
|
Connection* c = session_mgr->FindConnection(cid);
|
|
if ( ! c )
|
|
{
|
|
zeek::reporter->Error("cannot find connection");
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
analyzer::Analyzer* a = c->FindAnalyzer(aid);
|
|
if ( ! a )
|
|
{
|
|
if ( err_if_no_conn )
|
|
zeek::reporter->Error("connection does not have analyzer specified to disable");
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
if ( prevent )
|
|
a->Parent()->PreventChildren(a->GetAnalyzerTag());
|
|
|
|
auto rval = a->Remove();
|
|
return zeek::val_mgr->Bool(rval);
|
|
%}
|
|
|
|
## Informs Zeek that it should skip any further processing of the contents of
|
|
## a given connection. In particular, Zeek will refrain from reassembling the
|
|
## TCP byte stream and from generating events relating to any analyzers that
|
|
## have been processing the connection.
|
|
##
|
|
## cid: The connection ID.
|
|
##
|
|
## Returns: False if *cid* does not point to an active connection, and true
|
|
## otherwise.
|
|
##
|
|
## .. note::
|
|
##
|
|
## Zeek will still generate connection-oriented events such as
|
|
## :zeek:id:`connection_finished`.
|
|
function skip_further_processing%(cid: conn_id%): bool
|
|
%{
|
|
Connection* c = session_mgr->FindConnection(cid);
|
|
if ( ! c )
|
|
return zeek::val_mgr->False();
|
|
|
|
c->GetSessionAdapter()->SetSkip(1);
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## Controls whether packet contents belonging to a connection should be
|
|
## recorded (when ``-w`` option is provided on the command line).
|
|
##
|
|
## cid: The connection identifier.
|
|
##
|
|
## do_record: True to enable packet contents, and false to disable for the
|
|
## connection identified by *cid*.
|
|
##
|
|
## Returns: False if *cid* does not point to an active connection, and true
|
|
## otherwise.
|
|
##
|
|
## .. zeek:see:: skip_further_processing
|
|
##
|
|
## .. note::
|
|
##
|
|
## This is independent of whether Zeek processes the packets of this
|
|
## connection, which is controlled separately by
|
|
## :zeek:id:`skip_further_processing`.
|
|
##
|
|
## .. zeek:see:: get_contents_file set_contents_file
|
|
function set_record_packets%(cid: conn_id, do_record: bool%): bool
|
|
%{
|
|
Connection* c = session_mgr->FindConnection(cid);
|
|
if ( ! c )
|
|
return zeek::val_mgr->False();
|
|
|
|
c->SetRecordPackets(do_record);
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## Sets an individual inactivity timeout for a connection and thus
|
|
## overrides the global inactivity timeout.
|
|
##
|
|
## cid: The connection ID.
|
|
##
|
|
## t: The new inactivity timeout for the connection identified by *cid*.
|
|
##
|
|
## Returns: The previous timeout interval.
|
|
function set_inactivity_timeout%(cid: conn_id, t: interval%): interval
|
|
%{
|
|
Connection* c = session_mgr->FindConnection(cid);
|
|
if ( ! c )
|
|
return zeek::make_intrusive<zeek::IntervalVal>(0.0);
|
|
|
|
double old_timeout = c->InactivityTimeout();
|
|
c->SetInactivityTimeout(t);
|
|
|
|
return zeek::make_intrusive<zeek::IntervalVal>(old_timeout);
|
|
%}
|
|
|
|
# ===========================================================================
|
|
#
|
|
# Files and Directories
|
|
#
|
|
# ===========================================================================
|
|
|
|
## Opens a file for writing. If a file with the same name already exists, this
|
|
## function overwrites it (as opposed to :zeek:id:`open_for_append`).
|
|
##
|
|
## f: The path to the file.
|
|
##
|
|
## Returns: A :zeek:type:`file` handle for subsequent operations.
|
|
##
|
|
## .. zeek:see:: active_file open_for_append close write_file
|
|
## get_file_name set_buf flush_all mkdir enable_raw_output
|
|
## rmdir unlink rename
|
|
function open%(f: string%): file
|
|
%{
|
|
const char* file = f->CheckString();
|
|
|
|
if ( zeek::util::streq(file, "-") )
|
|
return zeek::make_intrusive<zeek::FileVal>(zeek::make_intrusive<zeek::File>(stdout, "-", "w"));
|
|
else
|
|
return zeek::make_intrusive<zeek::FileVal>(zeek::make_intrusive<zeek::File>(file, "w"));
|
|
%}
|
|
|
|
## Opens a file for writing or appending. If a file with the same name already
|
|
## exists, this function appends to it (as opposed to :zeek:id:`open`).
|
|
##
|
|
## f: The path to the file.
|
|
##
|
|
## Returns: A :zeek:type:`file` handle for subsequent operations.
|
|
##
|
|
## .. zeek:see:: active_file open close write_file
|
|
## get_file_name set_buf flush_all mkdir enable_raw_output
|
|
## rmdir unlink rename
|
|
function open_for_append%(f: string%): file
|
|
%{
|
|
return zeek::make_intrusive<zeek::FileVal>(zeek::make_intrusive<zeek::File>(f->CheckString(), "a"));
|
|
%}
|
|
|
|
## Closes an open file and flushes any buffered content.
|
|
##
|
|
## f: A :zeek:type:`file` handle to an open file.
|
|
##
|
|
## Returns: True on success.
|
|
##
|
|
## .. zeek:see:: active_file open open_for_append write_file
|
|
## get_file_name set_buf flush_all mkdir enable_raw_output
|
|
## rmdir unlink rename
|
|
function close%(f: file%): bool
|
|
%{
|
|
return zeek::val_mgr->Bool(f->Close());
|
|
%}
|
|
|
|
## Writes data to an open file.
|
|
##
|
|
## f: A :zeek:type:`file` handle to an open file.
|
|
##
|
|
## data: The data to write to *f*.
|
|
##
|
|
## Returns: True on success.
|
|
##
|
|
## .. zeek:see:: active_file open open_for_append close
|
|
## get_file_name set_buf flush_all mkdir enable_raw_output
|
|
## rmdir unlink rename
|
|
function write_file%(f: file, data: string%): bool
|
|
%{
|
|
if ( ! f )
|
|
return zeek::val_mgr->False();
|
|
|
|
return zeek::val_mgr->Bool(f->Write((const char*) data->Bytes(), data->Len()));
|
|
%}
|
|
|
|
## Alters the buffering behavior of a file.
|
|
##
|
|
## f: A :zeek:type:`file` handle to an open file.
|
|
##
|
|
## buffered: When true, *f* is fully buffered, i.e., bytes are saved in a
|
|
## buffer until the block size has been reached. When
|
|
## false, *f* is line buffered, i.e., bytes are saved up until a
|
|
## newline occurs.
|
|
##
|
|
## .. zeek:see:: active_file open open_for_append close
|
|
## get_file_name write_file flush_all mkdir enable_raw_output
|
|
## rmdir unlink rename
|
|
function set_buf%(f: file, buffered: bool%): any
|
|
%{
|
|
f->SetBuf(buffered);
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## Flushes all open files to disk.
|
|
##
|
|
## Returns: True on success.
|
|
##
|
|
## .. zeek:see:: active_file open open_for_append close
|
|
## get_file_name write_file set_buf mkdir enable_raw_output
|
|
## rmdir unlink rename
|
|
function flush_all%(%): bool
|
|
%{
|
|
return zeek::val_mgr->Bool(fflush(0) == 0);
|
|
%}
|
|
|
|
## Creates a new directory.
|
|
##
|
|
## f: The directory name.
|
|
##
|
|
## Returns: True if the operation succeeds or if *f* already exists,
|
|
## and false if the file creation fails.
|
|
##
|
|
## .. zeek:see:: active_file open_for_append close write_file
|
|
## get_file_name set_buf flush_all enable_raw_output
|
|
## rmdir unlink rename
|
|
function mkdir%(f: string%): bool
|
|
%{
|
|
const char* filename = f->CheckString();
|
|
|
|
if ( mkdir(filename, 0777) < 0 )
|
|
{
|
|
int error = errno;
|
|
struct stat filestat;
|
|
// check if already exists and is directory.
|
|
if ( errno == EEXIST && stat(filename, &filestat) == 0
|
|
&& S_ISDIR(filestat.st_mode) )
|
|
return zeek::val_mgr->True();
|
|
|
|
zeek::emit_builtin_error(zeek::util::fmt("cannot create directory '%s': %s", filename,
|
|
strerror(error)));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
else
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
|
|
## Removes a directory.
|
|
##
|
|
## d: The directory name.
|
|
##
|
|
## Returns: True if the operation succeeds, and false if the
|
|
## directory delete operation fails.
|
|
##
|
|
## .. zeek:see:: active_file open_for_append close write_file
|
|
## get_file_name set_buf flush_all enable_raw_output
|
|
## mkdir unlink rename
|
|
function rmdir%(d: string%): bool
|
|
%{
|
|
const char* dirname = d->CheckString();
|
|
|
|
if ( rmdir(dirname) < 0 )
|
|
{
|
|
zeek::emit_builtin_error(zeek::util::fmt("cannot remove directory '%s': %s", dirname,
|
|
strerror(errno)));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
else
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## Removes a file from a directory.
|
|
##
|
|
## f: the file to delete.
|
|
##
|
|
## Returns: True if the operation succeeds and the file was deleted,
|
|
## and false if the deletion fails.
|
|
##
|
|
## .. zeek:see:: active_file open_for_append close write_file
|
|
## get_file_name set_buf flush_all enable_raw_output
|
|
## mkdir rmdir rename
|
|
function unlink%(f: string%): bool
|
|
%{
|
|
const char* filename = f->CheckString();
|
|
|
|
if ( unlink(filename) < 0 )
|
|
{
|
|
zeek::emit_builtin_error(zeek::util::fmt("cannot unlink file '%s': %s", filename,
|
|
strerror(errno)));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
else
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## Renames a file from src_f to dst_f.
|
|
##
|
|
## src_f: the name of the file to rename.
|
|
##
|
|
## dest_f: the name of the file after the rename operation.
|
|
##
|
|
## Returns: True if the rename succeeds and false otherwise.
|
|
##
|
|
## .. zeek:see:: active_file open_for_append close write_file
|
|
## get_file_name set_buf flush_all enable_raw_output
|
|
## mkdir rmdir unlink
|
|
function rename%(src_f: string, dst_f: string%): bool
|
|
%{
|
|
const char* src_filename = src_f->CheckString();
|
|
const char* dst_filename = dst_f->CheckString();
|
|
|
|
if ( rename(src_filename, dst_filename) < 0 )
|
|
{
|
|
zeek::emit_builtin_error(zeek::util::fmt("cannot rename file '%s' to '%s': %s", src_filename,
|
|
dst_filename, strerror(errno)));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
else
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## Checks whether a given file is open.
|
|
##
|
|
## f: The file to check.
|
|
##
|
|
## Returns: True if *f* is an open :zeek:type:`file`.
|
|
##
|
|
## .. todo:: Rename to ``is_open``.
|
|
function active_file%(f: file%): bool
|
|
%{
|
|
return zeek::val_mgr->Bool(f->IsOpen());
|
|
%}
|
|
|
|
## Gets the filename associated with a file handle.
|
|
##
|
|
## f: The file handle to inquire the name for.
|
|
##
|
|
## Returns: The filename associated with *f*.
|
|
##
|
|
## .. zeek:see:: open
|
|
function get_file_name%(f: file%): string
|
|
%{
|
|
if ( ! f )
|
|
return zeek::val_mgr->EmptyString();
|
|
|
|
return zeek::make_intrusive<zeek::StringVal>(f->Name());
|
|
%}
|
|
|
|
## Rotates a file.
|
|
##
|
|
## f: An open file handle.
|
|
##
|
|
## Returns: Rotation statistics which include the original file name, the name
|
|
## after the rotation, and the time when *f* was opened/closed.
|
|
##
|
|
## .. zeek:see:: rotate_file_by_name calc_next_rotate
|
|
function rotate_file%(f: file%): rotate_info
|
|
%{
|
|
zeek::RecordValPtr info{zeek::AdoptRef{}, f->Rotate()};
|
|
|
|
if ( info )
|
|
return info;
|
|
|
|
// Record indicating error.
|
|
static auto rotate_info = zeek::id::find_type<zeek::RecordType>("rotate_info");
|
|
info = zeek::make_intrusive<zeek::RecordVal>(rotate_info);
|
|
info->Assign(0, zeek::val_mgr->EmptyString());
|
|
info->Assign(1, zeek::val_mgr->EmptyString());
|
|
info->AssignTime(2, 0.0);
|
|
info->AssignTime(3, 0.0);
|
|
|
|
return info;
|
|
%}
|
|
|
|
## Rotates a file identified by its name.
|
|
##
|
|
## f: The name of the file to rotate
|
|
##
|
|
## Returns: Rotation statistics which include the original file name, the name
|
|
## after the rotation, and the time when *f* was opened/closed.
|
|
##
|
|
## .. zeek:see:: rotate_file calc_next_rotate
|
|
function rotate_file_by_name%(f: string%): rotate_info
|
|
%{
|
|
static auto rotate_info = zeek::id::find_type<zeek::RecordType>("rotate_info");
|
|
auto info = zeek::make_intrusive<zeek::RecordVal>(rotate_info);
|
|
|
|
bool is_pkt_dumper = false;
|
|
bool is_addl_pkt_dumper = false;
|
|
|
|
// Special case: one of current dump files.
|
|
if ( zeek::run_state::detail::pkt_dumper &&
|
|
zeek::util::streq(zeek::run_state::detail::pkt_dumper->Path().c_str(), f->CheckString()) )
|
|
{
|
|
is_pkt_dumper = true;
|
|
zeek::run_state::detail::pkt_dumper->Close();
|
|
}
|
|
|
|
if ( addl_pkt_dumper &&
|
|
zeek::util::streq(addl_pkt_dumper->Path().c_str(), f->CheckString()) )
|
|
{
|
|
is_addl_pkt_dumper = true;
|
|
addl_pkt_dumper->Close();
|
|
}
|
|
|
|
FILE* file = zeek::util::detail::rotate_file(f->CheckString(), info.get());
|
|
if ( ! file )
|
|
{
|
|
// Record indicating error.
|
|
info->Assign(0, zeek::val_mgr->EmptyString());
|
|
info->Assign(1, zeek::val_mgr->EmptyString());
|
|
info->AssignTime(2, 0.0);
|
|
info->AssignTime(3, 0.0);
|
|
return info;
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
if ( is_pkt_dumper )
|
|
{
|
|
info->AssignTime(2, zeek::run_state::detail::pkt_dumper->OpenTime());
|
|
zeek::run_state::detail::pkt_dumper->Open();
|
|
}
|
|
|
|
if ( is_addl_pkt_dumper )
|
|
info->AssignTime(2, addl_pkt_dumper->OpenTime());
|
|
|
|
return info;
|
|
%}
|
|
|
|
## Calculates the duration until the next time a file is to be rotated, based
|
|
## on a given rotate interval.
|
|
##
|
|
## i: The rotate interval to base the calculation on.
|
|
##
|
|
## Returns: The duration until the next file rotation time.
|
|
##
|
|
## .. zeek:see:: rotate_file rotate_file_by_name
|
|
function calc_next_rotate%(i: interval%) : interval
|
|
%{
|
|
static auto log_rotate_base_time = zeek::id::find_val<zeek::StringVal>("log_rotate_base_time");
|
|
static auto base_time = log_rotate_base_time->AsString()->CheckString();
|
|
|
|
double base = zeek::util::detail::parse_rotate_base_time(base_time);
|
|
return zeek::make_intrusive<zeek::IntervalVal>(zeek::util::detail::calc_next_rotate(zeek::run_state::network_time, i, base));
|
|
%}
|
|
|
|
## Returns the size of a given file.
|
|
##
|
|
## f: The name of the file whose size to lookup.
|
|
##
|
|
## Returns: The size of *f* in bytes.
|
|
function file_size%(f: string%) : double
|
|
%{
|
|
struct stat s;
|
|
|
|
if ( stat(f->CheckString(), &s) < 0 )
|
|
return zeek::make_intrusive<zeek::DoubleVal>(-1.0);
|
|
|
|
return zeek::make_intrusive<zeek::DoubleVal>(double(s.st_size));
|
|
%}
|
|
|
|
## Prevents escaping of non-ASCII characters when writing to a file.
|
|
## This function is equivalent to :zeek:attr:`&raw_output`.
|
|
##
|
|
## f: The file to disable raw output for.
|
|
function enable_raw_output%(f: file%): any
|
|
%{
|
|
f->EnableRawOutput();
|
|
return nullptr;
|
|
%}
|
|
|
|
# ===========================================================================
|
|
#
|
|
# Packet Filtering
|
|
#
|
|
# ===========================================================================
|
|
|
|
## Installs a filter to drop packets from a given IP source address with
|
|
## a certain probability if none of a given set of TCP flags are set.
|
|
## Note that for IPv6 packets with a Destination options header that has
|
|
## the Home Address option, this filters out against that home address.
|
|
##
|
|
## ip: The IP address to drop.
|
|
##
|
|
## tcp_flags: If none of these TCP flags are set, drop packets from *ip* with
|
|
## probability *prob*.
|
|
##
|
|
## prob: The probability [0.0, 1.0] used to drop packets from *ip*.
|
|
##
|
|
## Returns: True (unconditionally).
|
|
##
|
|
## .. zeek:see:: Pcap::precompile_pcap_filter
|
|
## Pcap::install_pcap_filter
|
|
## install_src_net_filter
|
|
## uninstall_src_addr_filter
|
|
## uninstall_src_net_filter
|
|
## install_dst_addr_filter
|
|
## install_dst_net_filter
|
|
## uninstall_dst_addr_filter
|
|
## uninstall_dst_net_filter
|
|
## Pcap::error
|
|
##
|
|
## .. todo:: The return value should be changed to any.
|
|
function install_src_addr_filter%(ip: addr, tcp_flags: count, prob: double%) : bool
|
|
%{
|
|
packet_mgr->GetPacketFilter()->AddSrc(ip->AsAddr(), tcp_flags, prob);
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## Installs a filter to drop packets originating from a given subnet with
|
|
## a certain probability if none of a given set of TCP flags are set.
|
|
##
|
|
## snet: The subnet to drop packets from.
|
|
##
|
|
## tcp_flags: If none of these TCP flags are set, drop packets from *snet* with
|
|
## probability *prob*.
|
|
##
|
|
## prob: The probability [0.0, 1.0] used to drop packets from *snet*.
|
|
##
|
|
## Returns: True (unconditionally).
|
|
##
|
|
## .. zeek:see:: Pcap::precompile_pcap_filter
|
|
## Pcap::install_pcap_filter
|
|
## install_src_addr_filter
|
|
## uninstall_src_addr_filter
|
|
## uninstall_src_net_filter
|
|
## install_dst_addr_filter
|
|
## install_dst_net_filter
|
|
## uninstall_dst_addr_filter
|
|
## uninstall_dst_net_filter
|
|
## Pcap::error
|
|
##
|
|
## .. todo:: The return value should be changed to any.
|
|
function install_src_net_filter%(snet: subnet, tcp_flags: count, prob: double%) : bool
|
|
%{
|
|
packet_mgr->GetPacketFilter()->AddSrc(snet, tcp_flags, prob);
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## Removes a source address filter.
|
|
##
|
|
## ip: The IP address for which a source filter was previously installed.
|
|
##
|
|
## Returns: True on success.
|
|
##
|
|
## .. zeek:see:: Pcap::precompile_pcap_filter
|
|
## Pcap::install_pcap_filter
|
|
## install_src_addr_filter
|
|
## install_src_net_filter
|
|
## uninstall_src_net_filter
|
|
## install_dst_addr_filter
|
|
## install_dst_net_filter
|
|
## uninstall_dst_addr_filter
|
|
## uninstall_dst_net_filter
|
|
## Pcap::error
|
|
function uninstall_src_addr_filter%(ip: addr%) : bool
|
|
%{
|
|
return zeek::val_mgr->Bool(packet_mgr->GetPacketFilter()->RemoveSrc(ip->AsAddr()));
|
|
%}
|
|
|
|
## Removes a source subnet filter.
|
|
##
|
|
## snet: The subnet for which a source filter was previously installed.
|
|
##
|
|
## Returns: True on success.
|
|
##
|
|
## .. zeek:see:: Pcap::precompile_pcap_filter
|
|
## Pcap::install_pcap_filter
|
|
## install_src_addr_filter
|
|
## install_src_net_filter
|
|
## uninstall_src_addr_filter
|
|
## install_dst_addr_filter
|
|
## install_dst_net_filter
|
|
## uninstall_dst_addr_filter
|
|
## uninstall_dst_net_filter
|
|
## Pcap::error
|
|
function uninstall_src_net_filter%(snet: subnet%) : bool
|
|
%{
|
|
return zeek::val_mgr->Bool(packet_mgr->GetPacketFilter()->RemoveSrc(snet));
|
|
%}
|
|
|
|
## Installs a filter to drop packets destined to a given IP address with
|
|
## a certain probability if none of a given set of TCP flags are set.
|
|
## Note that for IPv6 packets with a routing type header and non-zero
|
|
## segments left, this filters out against the final destination of the
|
|
## packet according to the routing extension header.
|
|
##
|
|
## ip: Drop packets to this IP address.
|
|
##
|
|
## tcp_flags: If none of these TCP flags are set, drop packets to *ip* with
|
|
## probability *prob*.
|
|
##
|
|
## prob: The probability [0.0, 1.0] used to drop packets to *ip*.
|
|
##
|
|
## Returns: True (unconditionally).
|
|
##
|
|
## .. zeek:see:: Pcap::precompile_pcap_filter
|
|
## Pcap::install_pcap_filter
|
|
## install_src_addr_filter
|
|
## install_src_net_filter
|
|
## uninstall_src_addr_filter
|
|
## uninstall_src_net_filter
|
|
## install_dst_net_filter
|
|
## uninstall_dst_addr_filter
|
|
## uninstall_dst_net_filter
|
|
## Pcap::error
|
|
##
|
|
## .. todo:: The return value should be changed to any.
|
|
function install_dst_addr_filter%(ip: addr, tcp_flags: count, prob: double%) : bool
|
|
%{
|
|
packet_mgr->GetPacketFilter()->AddDst(ip->AsAddr(), tcp_flags, prob);
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## Installs a filter to drop packets destined to a given subnet with
|
|
## a certain probability if none of a given set of TCP flags are set.
|
|
##
|
|
## snet: Drop packets to this subnet.
|
|
##
|
|
## tcp_flags: If none of these TCP flags are set, drop packets to *snet* with
|
|
## probability *prob*.
|
|
##
|
|
## prob: The probability [0.0, 1.0] used to drop packets to *snet*.
|
|
##
|
|
## Returns: True (unconditionally).
|
|
##
|
|
## .. zeek:see:: Pcap::precompile_pcap_filter
|
|
## Pcap::install_pcap_filter
|
|
## install_src_addr_filter
|
|
## install_src_net_filter
|
|
## uninstall_src_addr_filter
|
|
## uninstall_src_net_filter
|
|
## install_dst_addr_filter
|
|
## uninstall_dst_addr_filter
|
|
## uninstall_dst_net_filter
|
|
## Pcap::error
|
|
##
|
|
## .. todo:: The return value should be changed to any.
|
|
function install_dst_net_filter%(snet: subnet, tcp_flags: count, prob: double%) : bool
|
|
%{
|
|
packet_mgr->GetPacketFilter()->AddDst(snet, tcp_flags, prob);
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## Removes a destination address filter.
|
|
##
|
|
## ip: The IP address for which a destination filter was previously installed.
|
|
##
|
|
## Returns: True on success.
|
|
##
|
|
## .. zeek:see:: Pcap::precompile_pcap_filter
|
|
## Pcap::install_pcap_filter
|
|
## install_src_addr_filter
|
|
## install_src_net_filter
|
|
## uninstall_src_addr_filter
|
|
## uninstall_src_net_filter
|
|
## install_dst_addr_filter
|
|
## install_dst_net_filter
|
|
## uninstall_dst_net_filter
|
|
## Pcap::error
|
|
function uninstall_dst_addr_filter%(ip: addr%) : bool
|
|
%{
|
|
return zeek::val_mgr->Bool(packet_mgr->GetPacketFilter()->RemoveDst(ip->AsAddr()));
|
|
%}
|
|
|
|
## Removes a destination subnet filter.
|
|
##
|
|
## snet: The subnet for which a destination filter was previously installed.
|
|
##
|
|
## Returns: True on success.
|
|
##
|
|
## .. zeek:see:: Pcap::precompile_pcap_filter
|
|
## Pcap::install_pcap_filter
|
|
## install_src_addr_filter
|
|
## install_src_net_filter
|
|
## uninstall_src_addr_filter
|
|
## uninstall_src_net_filter
|
|
## install_dst_addr_filter
|
|
## install_dst_net_filter
|
|
## uninstall_dst_addr_filter
|
|
## Pcap::error
|
|
function uninstall_dst_net_filter%(snet: subnet%) : bool
|
|
%{
|
|
return zeek::val_mgr->Bool(packet_mgr->GetPacketFilter()->RemoveDst(snet));
|
|
%}
|
|
|
|
## Checks whether the last raised event came from a remote peer.
|
|
##
|
|
## Returns: True if the last raised event came from a remote peer.
|
|
function is_remote_event%(%) : bool
|
|
%{
|
|
return zeek::val_mgr->Bool(zeek::event_mgr.CurrentSource() != zeek::util::detail::SOURCE_LOCAL);
|
|
%}
|
|
|
|
## Stops Zeek's packet processing. This function is used to synchronize
|
|
## distributed trace processing with communication enabled
|
|
## (*pseudo-realtime* mode).
|
|
##
|
|
## .. zeek:see:: continue_processing
|
|
function suspend_processing%(%) : any
|
|
%{
|
|
zeek::run_state::suspend_processing();
|
|
return nullptr;
|
|
%}
|
|
|
|
## Resumes Zeek's packet processing.
|
|
##
|
|
## .. zeek:see:: suspend_processing
|
|
function continue_processing%(%) : any
|
|
%{
|
|
zeek::run_state::continue_processing();
|
|
return nullptr;
|
|
%}
|
|
|
|
# ===========================================================================
|
|
#
|
|
# Internal Functions
|
|
#
|
|
# ===========================================================================
|
|
|
|
## Manually triggers the signature engine for a given connection.
|
|
## This is an internal function.
|
|
function match_signatures%(c: connection, pattern_type: int, s: string,
|
|
bol: bool, eol: bool,
|
|
from_orig: bool, clear: bool%) : bool
|
|
%{
|
|
if ( ! zeek::detail::rule_matcher )
|
|
return zeek::val_mgr->False();
|
|
|
|
c->Match((zeek::detail::Rule::PatternType) pattern_type, s->Bytes(), s->Len(),
|
|
from_orig, bol, eol, clear);
|
|
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## By default, zeek does not generate (raise) events that have not handled by
|
|
## any scripts. This means that these events will be invisible to a lot of other
|
|
## event handlers - and will not raise :zeek:id:`new_event`.
|
|
##
|
|
## Calling this function will cause all event handlers to be raised. This is, likely,
|
|
## only useful for debugging and causes reduced performance.
|
|
function generate_all_events%(%) : bool
|
|
%{
|
|
auto event_names = event_registry->AllHandlers();
|
|
for ( const auto& name: event_names )
|
|
{
|
|
auto event = event_registry->Lookup(name);
|
|
if ( event == nullptr )
|
|
continue;
|
|
|
|
event->SetGenerateAlways();
|
|
}
|
|
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
%%{
|
|
// Autogenerated from CMake bif_target()
|
|
#include "__all__.bif.cc"
|
|
#include "__all__.bif.register.cc"
|
|
|
|
static void init_secondary_bifs()
|
|
{
|
|
#include "__all__.bif.init.cc"
|
|
}
|
|
%%}
|
|
|
|
## An internal function that helps initialize BIFs.
|
|
function __init_secondary_bifs%(%): bool
|
|
%{
|
|
init_secondary_bifs();
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
# ===========================================================================
|
|
#
|
|
# Anonymization Functions
|
|
# (Not Fully Functional)
|
|
#
|
|
# ===========================================================================
|
|
|
|
|
|
|
|
%%{
|
|
#include "zeek/Anon.h"
|
|
%%}
|
|
|
|
## Preserves the prefix of an IP address in anonymization.
|
|
##
|
|
## a: The address to preserve.
|
|
##
|
|
## width: The number of bits from the top that should remain intact.
|
|
##
|
|
## .. zeek:see:: preserve_subnet anonymize_addr
|
|
##
|
|
## .. todo:: Currently dysfunctional.
|
|
function preserve_prefix%(a: addr, width: count%): any
|
|
%{
|
|
zeek::detail::AnonymizeIPAddr* ip_anon = zeek::detail::ip_anonymizer[zeek::detail::PREFIX_PRESERVING_A50];
|
|
if ( ip_anon )
|
|
{
|
|
if ( a->AsAddr().GetFamily() == IPv6 )
|
|
zeek::emit_builtin_error("preserve_prefix() not supported for IPv6 addresses");
|
|
else
|
|
{
|
|
const uint32_t* bytes;
|
|
a->AsAddr().GetBytes(&bytes);
|
|
ip_anon->PreservePrefix(*bytes, width);
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
%}
|
|
|
|
## Preserves the prefix of a subnet in anonymization.
|
|
##
|
|
## a: The subnet to preserve.
|
|
##
|
|
## .. zeek:see:: preserve_prefix anonymize_addr
|
|
##
|
|
## .. todo:: Currently dysfunctional.
|
|
function preserve_subnet%(a: subnet%): any
|
|
%{
|
|
DEBUG_MSG("%s/%d\n", a->Prefix().AsString().c_str(), a->Width());
|
|
zeek::detail::AnonymizeIPAddr* ip_anon = zeek::detail::ip_anonymizer[zeek::detail::PREFIX_PRESERVING_A50];
|
|
if ( ip_anon )
|
|
{
|
|
if ( a->AsSubNet().Prefix().GetFamily() == IPv6 )
|
|
zeek::emit_builtin_error("preserve_subnet() not supported for IPv6 addresses");
|
|
else
|
|
{
|
|
const uint32_t* bytes;
|
|
a->AsSubNet().Prefix().GetBytes(&bytes);
|
|
ip_anon->PreservePrefix(*bytes, a->AsSubNet().Length());
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
%}
|
|
|
|
## Anonymizes an IP address.
|
|
##
|
|
## a: The address to anonymize.
|
|
##
|
|
## cl: The anonymization class, which can take on three different values:
|
|
##
|
|
## - ``ORIG_ADDR``: Tag *a* as an originator address.
|
|
##
|
|
## - ``RESP_ADDR``: Tag *a* as an responder address.
|
|
##
|
|
## - ``OTHER_ADDR``: Tag *a* as an arbitrary address.
|
|
##
|
|
## Returns: An anonymized version of *a*.
|
|
##
|
|
## .. zeek:see:: preserve_prefix preserve_subnet
|
|
##
|
|
## .. todo:: Currently dysfunctional.
|
|
function anonymize_addr%(a: addr, cl: IPAddrAnonymizationClass%): addr
|
|
%{
|
|
int anon_class = cl->InternalInt();
|
|
if ( anon_class < 0 || anon_class >= zeek::detail::NUM_ADDR_ANONYMIZATION_CLASSES )
|
|
zeek::emit_builtin_error("anonymize_addr(): invalid ip addr anonymization class");
|
|
|
|
if ( a->AsAddr().GetFamily() == IPv6 )
|
|
{
|
|
zeek::emit_builtin_error("anonymize_addr() not supported for IPv6 addresses");
|
|
return nullptr;
|
|
}
|
|
else
|
|
{
|
|
const uint32_t* bytes;
|
|
a->AsAddr().GetBytes(&bytes);
|
|
return zeek::make_intrusive<zeek::AddrVal>(zeek::detail::anonymize_ip(*bytes,
|
|
static_cast<zeek::detail::ip_addr_anonymization_class_t>(anon_class)));
|
|
}
|
|
%}
|
|
|
|
## A function to convert arbitrary Zeek data into a JSON string.
|
|
##
|
|
## v: The value to convert to JSON. Typically a record.
|
|
##
|
|
## only_loggable: If the v value is a record this will only cause
|
|
## fields with the &log attribute to be included in the JSON.
|
|
##
|
|
## returns: a JSON formatted string.
|
|
##
|
|
## .. zeek:see:: fmt cat cat_sep string_cat print_raw
|
|
function to_json%(val: any, only_loggable: bool &default=F, field_escape_pattern: pattern &default=/^_/%): string
|
|
%{
|
|
return val->ToJSON(only_loggable, field_escape_pattern);
|
|
%}
|
|
|
|
## Compresses a given path by removing '..'s and the parent directory it
|
|
## references and also removing dual '/'s and extraneous '/./'s.
|
|
##
|
|
## dir: a path string, either relative or absolute.
|
|
##
|
|
## Returns: a compressed version of the input path.
|
|
function compress_path%(dir: string%): string
|
|
%{
|
|
return zeek::make_intrusive<zeek::StringVal>(zeek::util::detail::normalize_path(dir->ToStdString()));
|
|
%}
|