##! 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 #include #include #include #include #include #include #include "digest.h" #include "Reporter.h" #include "IPAddr.h" #include "util.h" #include "file_analysis/Manager.h" #include "iosource/Manager.h" #include "iosource/Packet.h" #include "iosource/PktSrc.h" #include "iosource/PktDumper.h" #include "IntrusivePtr.h" #include "input.h" #include "Hash.h" using namespace std; TableType* var_sizes; static iosource::PktDumper* addl_pkt_dumper = 0; 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 TypeTag ok_d_fmt[] = { TYPE_BOOL, TYPE_ENUM, TYPE_INT, TYPE_COUNT, TYPE_COUNTER, TYPE_PORT, TYPE_SUBNET, TYPE_ERROR }; static TypeTag ok_f_fmt[] = { TYPE_DOUBLE, TYPE_TIME, TYPE_INTERVAL, TYPE_ERROR }; static int check_fmt_type(TypeTag t, TypeTag ok[]) { for ( int i = 0; ok[i] != TYPE_ERROR; ++i ) if ( ok[i] == t ) return 1; return 0; } static void do_fmt(const char*& fmt, Val* v, ODesc* d) { TypeTag t = v->GetType()->Tag(); 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 ) { 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]; ODesc s; s.SetStyle(RAW_STYLE); if ( precision >= 0 && *fmt != 'e' && *fmt != 'f' && *fmt != 'g' ) builtin_error("precision specified for non-floating point"); switch ( *fmt ) { case 'D': case 'T': // ISO Timestamp with microsecond precision. { if ( t != TYPE_TIME ) { 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(""); if ( ! strftime(out_buf, sizeof(out_buf), is_time_fmt ? "%Y-%m-%d-%H:%M" : "%Y-%m-%d-%H:%M:%S", &t) ) s.AddSP(""); 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 == 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) ) { builtin_error("bad type for %d/%x format", v); break; } else if ( it == TYPE_INTERNAL_UNSIGNED ) { bro_uint_t u = v->CoerceToUnsigned(); if ( v->GetType()->IsNetworkOrder() ) { if ( v->GetType()->Tag() == 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) ) { 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: 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, 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(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 make_intrusive(current_time(), TYPE_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 make_intrusive(network_time, TYPE_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 = zeekenv(var->CheckString()); if ( ! env_val ) env_val = ""; // ### return make_intrusive(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 val_mgr->False(); return 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 ( terminating ) return val_mgr->False(); terminate_processing(); return 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(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() != TYPE_STRING || val->GetType()->Tag() != TYPE_STRING ) { builtin_error("system_env() needs a table[string] of string"); return false; } char* tmp = copy_string(key->AsString()->CheckString()); to_upper(tmp); std::string var1 = fmt("ZEEK_ARG_%s", tmp); std::string var2 = 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 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() != TYPE_TABLE ) { builtin_error("system_env() requires a table argument"); return val_mgr->Int(-1); } if ( ! prepare_environment(env->AsTableVal(), true) ) return val_mgr->Int(-1); int result = do_system(str->CheckString()); prepare_environment(env->AsTableVal(), false); return 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 ) { reporter->Error("Failed to popen %s", prog); return 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 ) { reporter->Error("Failed to write all given data to %s", prog); return val_mgr->False(); } return val_mgr->True(); %} %%{ #include "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 make_intrusive(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 make_intrusive(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 make_intrusive(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@, KeyedHash::shared_hmac_md5_key, hmac); return make_intrusive(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 = make_intrusive(); 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 = make_intrusive(); 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 = make_intrusive(); 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(handle)->Feed(data->Bytes(), data->Len()); return 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(handle)->Feed(data->Bytes(), data->Len()); return 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(handle)->Feed(data->Bytes(), data->Len()); return 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(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(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(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() != TYPE_VECTOR || v->GetType()->Yield()->Tag() != TYPE_STRING ) { // reporter->Error will throw an exception. reporter->Error("paraglob requires a vector of strings for initialization."); return nullptr; } std::vector patterns; VectorVal* vv = v->AsVectorVal(); for ( unsigned int i = 0; i < vv->Size(); ++i ) { const BroString* s = vv->At(i)->AsString(); patterns.push_back(std::string(reinterpret_cast(s->Bytes()), s->Len())); } try { std::unique_ptr p (new paraglob::Paraglob(patterns)); return make_intrusive(std::move(p)); } // Thrown if paraglob fails to add a pattern. catch (const paraglob::add_error& e) { 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(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 val_mgr->Bool( *(static_cast(p_one)) == *(static_cast(p_two)) ); %} ## Returns 32-bit digest of arbitrary input values using FNV-1a hash algorithm. ## See ``_. ## ## input: The desired input value to hash. ## ## Returns: The hashed value. ## ## .. zeek:see:: hrw_weight function fnv1a32%(input: any%): count %{ 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 val_mgr->Count(rval); %} ## Calculates a weight value for use in a Rendezvous Hashing algorithm. ## See ``_. ## The weight function used is the one recommended in the original ## paper: ``_. ## ## 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 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(bro_random()) / (RAND_MAX + 1.0)); return 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 %{ bro_srandom(seed); return nullptr; %} %%{ #include %%} ## Send a string to syslog. ## ## s: The string to log via syslog function syslog%(s: string%): any %{ 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 "" 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 ) reporter->Warning("identify_data() builtin-function only returns MIME types, but verbose file info requested"); string strongest_match = file_mgr->DetectMIME(data->Bytes(), data->Len()); if ( strongest_match.empty() ) return make_intrusive(""); return make_intrusive(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 %{ RuleMatcher::MIME_Matches matches; 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("entropy_test_result"); auto ent_result = make_intrusive(entropy_test_result); ent_result->Assign(0, make_intrusive(ent, TYPE_DOUBLE)); ent_result->Assign(1, make_intrusive(chisq, TYPE_DOUBLE)); ent_result->Assign(2, make_intrusive(mean, TYPE_DOUBLE)); ent_result->Assign(3, make_intrusive(montepi, TYPE_DOUBLE)); ent_result->Assign(4, make_intrusive(scc, TYPE_DOUBLE)); 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 make_intrusive(); %} ## 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(handle)->Feed(data->Bytes(), data->Len()); return 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(handle)->Get(&ent, &chisq, &mean, &montepi, &scc); static auto entropy_test_result = zeek::id::find_type("entropy_test_result"); auto ent_result = make_intrusive(entropy_test_result); ent_result->Assign(0, make_intrusive(ent, TYPE_DOUBLE)); ent_result->Assign(1, make_intrusive(chisq, TYPE_DOUBLE)); ent_result->Assign(2, make_intrusive(mean, TYPE_DOUBLE)); ent_result->Assign(3, make_intrusive(montepi, TYPE_DOUBLE)); ent_result->Assign(4, make_intrusive(scc, TYPE_DOUBLE)); 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 = calculate_unique_id(UID_POOL_DEFAULT_SCRIPT); return make_intrusive(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 = calculate_unique_id(pool); return make_intrusive(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() == TYPE_TABLE ) v->AsTableVal()->RemoveAll(); else 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() != TYPE_TABLE || ! t->GetType()->AsTableType()->IsSubNetIndex() ) { 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() != TYPE_TABLE || ! t->GetType()->AsTableType()->IsSubNetIndex() ) { 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() != TYPE_TABLE || ! t->GetType()->AsTableType()->IsSubNetIndex() ) { reporter->Error("check_subnet needs to be called on a set[subnet]/table[subnet]."); return nullptr; } const PrefixTable* pt = t->AsTableVal()->Subnets(); if ( ! pt ) { reporter->Error("check_subnet encountered nonexisting prefix table."); return nullptr; } void* res = pt->Lookup(search, true); return 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 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 %{ return val_mgr->Count(v->MemoryAllocation()); %} ## 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() != TYPE_VECTOR ) { builtin_error("resize() operates on vectors"); return nullptr; } return 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() != TYPE_VECTOR || v->GetType()->Yield()->Tag() != TYPE_BOOL ) { builtin_error("any_set() requires vector of bool"); return val_mgr->False(); } VectorVal* vv = v->AsVectorVal(); for ( unsigned int i = 0; i < vv->Size(); ++i ) if ( vv->At(i) && vv->At(i)->AsBool() ) return val_mgr->True(); return 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() != TYPE_VECTOR || v->GetType()->Yield()->Tag() != TYPE_BOOL ) { builtin_error("all_set() requires vector of bool"); return val_mgr->False(); } VectorVal* vv = v->AsVectorVal(); for ( unsigned int i = 0; i < vv->Size(); ++i ) if ( ! vv->At(i) || ! vv->At(i)->AsBool() ) return val_mgr->False(); return val_mgr->True(); %} %%{ static Func* sort_function_comp = nullptr; static std::vector*> index_map; // used for indirect sorting to support order() bool sort_function(const IntrusivePtr& a, const IntrusivePtr& b) { // Sort missing values as "high". if ( ! a ) return 0; if ( ! b ) return 1; auto result = sort_function_comp->Invoke(a, b); int int_result = result->CoerceToInt(); return int_result < 0; } bool indirect_sort_function(size_t a, size_t b) { return sort_function(*index_map[a], *index_map[b]); } bool signed_sort_function (const IntrusivePtr& a, const IntrusivePtr& b) { if ( ! a ) return 0; if ( ! b ) return 1; auto ia = a->CoerceToInt(); auto ib = b->CoerceToInt(); return ia < ib; } bool unsigned_sort_function (const IntrusivePtr& a, const IntrusivePtr& b) { if ( ! a ) return 0; if ( ! b ) return 1; auto ia = a->CoerceToUnsigned(); auto ib = b->CoerceToUnsigned(); return ia < ib; } bool indirect_signed_sort_function(size_t a, size_t b) { return signed_sort_function(*index_map[a], *index_map[b]); } bool indirect_unsigned_sort_function(size_t a, size_t b) { return unsigned_sort_function(*index_map[a], *index_map[b]); } %%} ## 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 an integral type (int, count, 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 %{ IntrusivePtr rval{NewRef{}, v}; if ( v->GetType()->Tag() != TYPE_VECTOR ) { builtin_error("sort() requires vector"); return rval; } const auto& elt_type = v->GetType()->Yield(); Func* comp = 0; if ( @ARG@.size() > 2 ) builtin_error("sort() called with extraneous argument"); if ( @ARG@.size() == 2 ) { Val* comp_val = @ARG@[1].get(); if ( ! IsFunc(comp_val->GetType()->Tag()) ) { builtin_error("second argument to sort() needs to be comparison function"); return rval; } comp = comp_val->AsFunc(); } if ( ! comp && ! IsIntegral(elt_type->Tag()) ) builtin_error("comparison function required for sort() with non-integral types"); auto& vv = *v->AsVector(); if ( comp ) { const auto& comp_type = comp->GetType(); if ( comp_type->Yield()->Tag() != TYPE_INT || ! comp_type->ParamList()->AllMatch(elt_type, 0) ) { builtin_error("invalid comparison function in call to sort()"); return rval; } sort_function_comp = comp; sort(vv.begin(), vv.end(), sort_function); } else { if ( elt_type->InternalType() == TYPE_INTERNAL_UNSIGNED ) sort(vv.begin(), vv.end(), unsigned_sort_function); else sort(vv.begin(), vv.end(), signed_sort_function); } 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 result_v = make_intrusive(zeek::id::index_vec); if ( v->GetType()->Tag() != TYPE_VECTOR ) { builtin_error("order() requires vector"); return result_v; } const auto& elt_type = v->GetType()->Yield(); Func* comp = 0; if ( @ARG@.size() > 2 ) builtin_error("order() called with extraneous argument"); if ( @ARG@.size() == 2 ) { Val* comp_val = @ARG@[1].get(); if ( ! IsFunc(comp_val->GetType()->Tag()) ) { builtin_error("second argument to order() needs to be comparison function"); return IntrusivePtr{NewRef{}, v}; } comp = comp_val->AsFunc(); } if ( ! comp && ! IsIntegral(elt_type->Tag()) ) builtin_error("comparison function required for order() with non-integral types"); auto& vv = *v->AsVector(); auto n = vv.size(); // Set up initial mapping of indices directly to corresponding // elements. vector ind_vv(n); index_map.reserve(n); size_t i; for ( i = 0; i < n; ++i ) { ind_vv[i] = i; index_map.emplace_back(&vv[i]); } if ( comp ) { const auto& comp_type = comp->GetType(); if ( comp_type->Yield()->Tag() != TYPE_INT || ! comp_type->ParamList()->AllMatch(elt_type, 0) ) { builtin_error("invalid comparison function in call to order()"); return IntrusivePtr{NewRef{}, v}; } sort_function_comp = comp; sort(ind_vv.begin(), ind_vv.end(), indirect_sort_function); } else { if ( elt_type->InternalType() == TYPE_INTERNAL_UNSIGNED ) sort(ind_vv.begin(), ind_vv.end(), indirect_unsigned_sort_function); else sort(ind_vv.begin(), ind_vv.end(), indirect_signed_sort_function); } index_map = {}; // Now spin through ind_vv to read out the rearrangement. for ( i = 0; i < n; ++i ) { int ind = ind_vv[i]; result_v->Assign(i, val_mgr->Count(ind)); } return result_v; %} # =========================================================================== # # 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 %{ ODesc d; d.SetStyle(RAW_STYLE); for ( const auto& a : @ARG@ ) a->Describe(&d); BroString* s = new BroString(1, d.TakeBytes(), d.Len()); s->SetUseFreeToDelete(true); return make_intrusive(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 %{ 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() == TYPE_STRING && ! v->AsString()->Len() ) v = def; v->Describe(&d); } BroString* s = new BroString(1, d.TakeBytes(), d.Len()); s->SetUseFreeToDelete(true); return make_intrusive(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 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(); ODesc d; d.SetStyle(RAW_STYLE); int n = 0; while ( next_fmt(fmt, @ARGS@, &d, n) ) ; if ( n < static_cast(@ARGC@) - 1 ) { builtin_error("too many arguments for format", fmt_v); return val_mgr->EmptyString(); } else if ( n >= static_cast(@ARGC@) ) { builtin_error("too few arguments for format", fmt_v); return val_mgr->EmptyString(); } BroString* s = new BroString(1, d.TakeBytes(), d.Len()); s->SetUseFreeToDelete(true); return make_intrusive(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 %{ ODesc d(DESC_READABLE); d.SetStyle(RAW_STYLE); describe_vals(@ARG@, &d, 0); printf("%.*s", d.Len(), d.Description()); return 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 make_intrusive(floor(d), TYPE_DOUBLE); %} ## 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 ) { reporter->Error("negative sqrt argument"); return make_intrusive(-1.0, TYPE_DOUBLE); } return make_intrusive(sqrt(x), TYPE_DOUBLE); %} ## 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 make_intrusive(exp(d), TYPE_DOUBLE); %} ## 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 make_intrusive(log(d), TYPE_DOUBLE); %} ## 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 make_intrusive(log10(d), TYPE_DOUBLE); %} # =========================================================================== # # 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 val_mgr->Count(mgr.CurrentAnalyzer()); %} ## Returns Zeek's process ID. ## ## Returns: Zeek's process ID. function getpid%(%) : count %{ return val_mgr->Count(getpid()); %} %%{ extern const char* zeek_version(); %%} ## Returns the Zeek version string. ## ## Returns: Zeek's version, e.g., 2.0-beta-47-debug. function zeek_version%(%): string %{ return make_intrusive(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 = make_intrusive(zeek::id::string_vec); RecordType* type = zeek::id::find_type(rt->CheckString())->AsRecordType(); for ( int i = 0; i < type->NumFields(); ++i ) result->Assign(i+1, make_intrusive(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 %{ ODesc d; t->GetType()->Describe(&d); BroString* s = new BroString(1, d.TakeBytes(), d.Len()); s->SetUseFreeToDelete(true); return make_intrusive(s); %} ## Returns: list of command-line arguments (``argv``) used to run Zeek. function zeek_args%(%): string_vec %{ auto sv = zeek::id::string_vec; auto rval = make_intrusive(std::move(sv)); for ( auto i = 0; i < bro_argc; ++i ) rval->Assign(rval->Size(), make_intrusive(bro_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 val_mgr->Bool(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 val_mgr->Bool(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("PacketSource"); auto ps = iosource_mgr->GetPktSrc(); auto r = make_intrusive(ps_type); if ( ps ) { r->Assign(0, val_mgr->Bool(ps->IsLive())); r->Assign(1, make_intrusive(ps->Path())); r->Assign(2, val_mgr->Int(ps->LinkType())); r->Assign(3, val_mgr->Count(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 %{ auto sizes = make_intrusive(IntrusivePtr{NewRef{}, var_sizes}); const auto& globals = global_scope()->Vars(); for ( const auto& global : globals ) { ID* id = global.second.get(); if ( id->HasVal() ) { auto id_name = make_intrusive(id->Name()); auto id_size = val_mgr->Count(id->GetVal()->MemoryAllocation()); 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("id_table"); auto ids = make_intrusive(id_table); const auto& globals = global_scope()->Vars(); for ( const auto& global : globals ) { ID* id = global.second.get(); static auto script_id = zeek::id::find_type("script_id"); auto rec = make_intrusive(script_id); rec->Assign(0, make_intrusive(type_name(id->GetType()->Tag()))); rec->Assign(1, val_mgr->Bool(id->IsExport())); rec->Assign(2, val_mgr->Bool(id->IsConst())); rec->Assign(3, val_mgr->Bool(id->IsEnumConst())); rec->Assign(4, val_mgr->Bool(id->IsOption())); rec->Assign(5, val_mgr->Bool(id->IsRedefinable())); if ( id->HasVal() ) rec->Assign(6, id->GetVal()); auto id_name = make_intrusive(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 ``""`` or ``""`` is returned. function lookup_ID%(id: string%) : any %{ const auto& i = global_scope()->Find(id->CheckString()); if ( ! i ) return make_intrusive(""); if ( ! i->GetVal() ) return make_intrusive(""); 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("record_field_table"); if ( rec->GetType()->Tag() == TYPE_STRING ) { const auto& id = global_scope()->Find(rec->AsStringVal()->ToStdString()); if ( ! id || ! id->IsType() || id->GetType()->Tag() != TYPE_RECORD ) { reporter->Error("record_fields string argument does not name a record type"); return make_intrusive(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 ( profiling_logger ) 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 val_mgr->True(); list 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(IPAddr(IPv4, (uint32_t*)ent->h_addr_list[len], IPAddr::Network)); } ent = gethostbyname2(host, AF_INET6); if ( ent ) { for ( unsigned int len = 0; ent->h_addr_list[len]; ++len ) addrs.push_back(IPAddr(IPv6, (uint32_t*)ent->h_addr_list[len], IPAddr::Network)); } list::const_iterator it; for ( it = addrs.begin(); it != addrs.end(); ++it ) { if ( *it == ip->AsAddr() ) return val_mgr->True(); } return 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 ( rule_matcher ) rule_matcher->DumpStats(f); return 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 val_mgr->Bool(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, ""); buffer[MAXHOSTNAMELEN-1] = '\0'; return make_intrusive(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 val_mgr->True(); else return 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 val_mgr->True(); else return 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 val_mgr->True(); else return 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 val_mgr->True(); else return val_mgr->False(); %} # =========================================================================== # # 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 = make_intrusive(zeek::id::find_type("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 ) reporter->Warning("Bad ip6_routing data length: %d", s->Len()); while ( len > 0 ) { IPAddr a(IPv6, (const uint32_t*) bytes, IPAddr::Network); rval->Assign(rval->Size(), make_intrusive(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 = make_intrusive(zeek::id::index_vec); const uint32_t* bytes; int len = a->AsAddr().GetBytes(&bytes); for ( int i = 0; i < len; ++i ) rval->Assign(i, 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 %{ if ( v->AsVector()->size() == 1 ) { return make_intrusive(htonl((*v->AsVector())[0]->AsCount())); } else if ( v->AsVector()->size() == 4 ) { uint32_t bytes[4]; for ( int i = 0; i < 4; ++i ) bytes[i] = htonl((*v->AsVector())[i]->AsCount()); return make_intrusive(bytes); } else { builtin_error("invalid vector size", @ARG@[0]); uint32_t bytes[4]; memset(bytes, 0, sizeof(bytes)); return make_intrusive(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() != TYPE_ENUM ) { builtin_error("enum_to_int() requires enum value"); return val_mgr->Int(-1); } return 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' ) builtin_error("bad conversion to integer", @ARG@[0]); #endif return 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 ) { builtin_error("bad conversion to count", @ARG@[0]); n = 0; } return 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 ) builtin_error("bad conversion to count", @ARG@[0]); return 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' ) { builtin_error("bad conversion to count", @ARG@[0]); u = 0; } return 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 make_intrusive(i, TYPE_DOUBLE); %} ## 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 make_intrusive(t, TYPE_DOUBLE); %} ## 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 make_intrusive(d, TYPE_TIME); %} ## 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 make_intrusive(d, Seconds); %} ## 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 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 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(); IntrusivePtr ret; in6_addr tmp; if ( IPAddr::ConvertString(s, &tmp) ) ret = make_intrusive(IPAddr(tmp)); else { ret = make_intrusive(IPAddr()); 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 = IPAddr::IsValid(s); delete [] s; return 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) ) builtin_error("failed converting string to IP prefix", sn); auto ret = make_intrusive(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 make_intrusive(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 make_intrusive(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 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' ) { builtin_error("bad conversion to double", @ARG@[0]); d = 0; } return make_intrusive(d, TYPE_DOUBLE); %} ## 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 ) { builtin_error("conversion of non-IPv4 count to addr", @ARG@[0]); return make_intrusive(uint32_t(0)); } return make_intrusive(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 ) 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 make_intrusive(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 ( streq(slash, "tcp") ) return val_mgr->Port(port, TRANSPORT_TCP); else if ( streq(slash, "udp") ) return val_mgr->Port(port, TRANSPORT_UDP); else if ( streq(slash, "icmp") ) return val_mgr->Port(port, TRANSPORT_ICMP); } } builtin_error("wrong port format, must be /[0-9]{1,5}\\/(tcp|udp|icmp)/"); return 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) ) { builtin_error("bad conversion to double"); return make_intrusive(0.0, TYPE_DOUBLE); } // See #908 for a discussion of portability. double d; memcpy(&d, s->Bytes(), sizeof(double)); return make_intrusive(ntohd(d), TYPE_DOUBLE); %} ## 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 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 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 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 val_mgr->Count(value); } } builtin_error("unsupported byte length for bytestring_to_count"); return 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 ) { builtin_error("bad PTR name", @ARG@[0]); addr = 0; } else addr = (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]; return make_intrusive(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 ) { 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 make_intrusive(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 make_intrusive(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((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 make_intrusive(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 ) { reporter->Error("Hex string '%s' has invalid length (not divisible by 2)", hexstr->CheckString()); return val_mgr->EmptyString(); } const char* bytes = hexstr->AsString()->CheckString(); int outlen = (len/2); auto bytestring_buf = std::make_unique(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 ) { reporter->Error("Hex string %s contains invalid input: %s", hexstr->CheckString(), strerror(errno)); return val_mgr->EmptyString(); } else if ( res != 1 ) { reporter->Error("Could not read hex element from input %s", hexstr->CheckString()); return val_mgr->EmptyString(); } } return make_intrusive(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 %{ BroString* t = encode_base64(s->AsString(), a->AsString()); if ( t ) return make_intrusive(t); else { reporter->Error("Broker query has an invalid data store"); return 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 %{ BroString* t = decode_base64(s->AsString(), a->AsString()); if ( t ) return make_intrusive(t); else { reporter->Error("error in decoding string %s", s->CheckString()); return 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 = sessions->FindConnection(cid); if ( ! conn ) { builtin_error("connection ID not a known connection", cid); return val_mgr->EmptyString(); } BroString* t = decode_base64(s->AsString(), a->AsString(), conn); if ( t ) return make_intrusive(t); else { reporter->Error("error in decoding string %s", s->CheckString()); return 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 ``_. ## ## 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 make_intrusive(""); 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 make_intrusive(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 = make_intrusive(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 make_intrusive(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 make_intrusive(""); return make_intrusive(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) ) { reporter->Warning("strptime conversion failed: fmt:%s d:%s", fmt->CheckString(), d->CheckString()); return make_intrusive(0.0, TYPE_TIME); } double ret = mktime(&t); return make_intrusive(ret, TYPE_TIME); %} # =========================================================================== # # 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 make_intrusive(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 %{ IPAddr addr1(a1->AsAddr()); addr1.Mask(top_bits_from_a1); IPAddr addr2(a2->AsAddr()); addr2.ReverseMask(top_bits_from_a1); return make_intrusive(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 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 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 val_mgr->Bool(p->IsICMP()); %} %%{ static IntrusivePtr map_conn_type(TransportProto tp) { switch ( tp ) { case TRANSPORT_UNKNOWN: return zeek::id::transport_proto->GetVal(0); case TRANSPORT_TCP: return zeek::id::transport_proto->GetVal(1); case TRANSPORT_UDP: return zeek::id::transport_proto->GetVal(2); case TRANSPORT_ICMP: return zeek::id::transport_proto->GetVal(3); default: 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 = sessions->FindConnection(cid); if ( ! c ) { builtin_error("unknown connection id in get_conn_transport_proto()", cid); return zeek::id::transport_proto->GetVal(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 ( sessions->FindConnection(c) ) return val_mgr->True(); else return 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 = sessions->FindConnection(cid); if ( conn ) return conn->ConnVal(); builtin_error("connection ID not a known connection", cid); // Return a dummy connection record. auto c = make_intrusive(zeek::id::connection); auto id_val = make_intrusive(zeek::id::conn_id); id_val->Assign(0, make_intrusive((unsigned int) 0)); id_val->Assign(1, val_mgr->Port(ntohs(0), TRANSPORT_UDP)); id_val->Assign(2, make_intrusive((unsigned int) 0)); id_val->Assign(3, val_mgr->Port(ntohs(0), TRANSPORT_UDP)); c->Assign(0, std::move(id_val)); auto orig_endp = make_intrusive(zeek::id::endpoint); orig_endp->Assign(0, val_mgr->Count(0)); orig_endp->Assign(1, val_mgr->Count(int(0))); auto resp_endp = make_intrusive(zeek::id::endpoint); resp_endp->Assign(0, val_mgr->Count(0)); resp_endp->Assign(1, val_mgr->Count(int(0))); c->Assign(1, std::move(orig_endp)); c->Assign(2, std::move(resp_endp)); c->Assign(3, make_intrusive(network_time, TYPE_TIME)); c->Assign(4, make_intrusive(0.0, TYPE_INTERVAL)); c->Assign(5, make_intrusive(zeek::id::string_set)); // service c->Assign(6, val_mgr->EmptyString()); // history return c; %} %%{ const char* conn_id_string(Val* c) { const auto& id = (*(c->AsRecord()))[0]; auto vl = id->AsRecord(); const IPAddr& orig_h = (*vl)[0]->AsAddr(); uint32_t orig_p = (*vl)[1]->AsPortVal()->Port(); const IPAddr& resp_h = (*vl)[2]->AsAddr(); uint32_t resp_p = (*vl)[3]->AsPortVal()->Port(); return 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; if ( ! current_pktsrc || ! current_pktsrc->GetCurrentPacket(&pkt) ) return 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 = iosource_mgr->OpenPktDumper(file_name->CheckString(), true); if ( addl_pkt_dumper ) { addl_pkt_dumper->Dump(pkt); } return 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("pcap_packet"); const Packet* p; auto pkt = make_intrusive(pcap_packet); if ( ! current_pktsrc || ! current_pktsrc->GetCurrentPacket(&p) ) { pkt->Assign(0, val_mgr->Count(0)); pkt->Assign(1, val_mgr->Count(0)); pkt->Assign(2, val_mgr->Count(0)); pkt->Assign(3, val_mgr->Count(0)); pkt->Assign(4, val_mgr->EmptyString()); pkt->Assign(5, zeek::BifType::Enum::link_encap->GetVal(BifEnum::LINK_UNKNOWN)); return pkt; } pkt->Assign(0, val_mgr->Count(uint32_t(p->ts.tv_sec))); pkt->Assign(1, val_mgr->Count(uint32_t(p->ts.tv_usec))); pkt->Assign(2, val_mgr->Count(p->cap_len)); pkt->Assign(3, val_mgr->Count(p->len)); pkt->Assign(4, make_intrusive(p->cap_len, (const char*)p->data)); pkt->Assign(5, zeek::BifType::Enum::link_encap->GetVal(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; if ( current_pktsrc && current_pktsrc->GetCurrentPacket(&p) ) { return p->ToRawPktHdrVal(); } static auto raw_pkt_hdr_type = zeek::id::find_type("raw_pkt_hdr"); auto hdr = make_intrusive(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 = 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_vl = pkt->AsRecord(); ts.tv_sec = (*pkt_vl)[0]->AsCount(); ts.tv_usec = (*pkt_vl)[1]->AsCount(); caplen = (*pkt_vl)[2]->AsCount(); len = (*pkt_vl)[3]->AsCount(); data = (*pkt_vl)[4]->AsString()->Bytes(); link_type = (*pkt_vl)[5]->AsEnum(); Packet p(link_type, &ts, caplen, len, data, true); addl_pkt_dumper->Dump(&p); } return val_mgr->Bool(addl_pkt_dumper && ! addl_pkt_dumper->IsError()); %} %%{ #include "DNS_Mgr.h" #include "Trigger.h" class LookupHostCallback : public DNS_Mgr::LookupCallback { public: LookupHostCallback(trigger::Trigger* arg_trigger, const 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 DNS_Mgr:Lookup:Callback. virtual void Resolved(const char* name) { Val* result = new StringVal(name); trigger->Cache(call, result); Unref(result); trigger->Release(); } virtual void Resolved(TableVal* addrs) { // No Ref() for addrs. trigger->Cache(call, addrs); trigger->Release(); } virtual void Timeout() { if ( lookup_name ) { Val* result = new StringVal("<\?\?\?>"); trigger->Cache(call, result); Unref(result); } else { ListVal* lv = new ListVal(TYPE_ADDR); lv->Append(make_intrusive("0.0.0.0")); auto result = lv->ToSetVal(); trigger->Cache(call, result.get()); Unref(lv); } trigger->Release(); } private: trigger::Trigger* trigger; const 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. trigger::Trigger* trigger = frame->GetTrigger(); if ( ! trigger) { builtin_error("lookup_addr() can only be called inside a when-condition"); return make_intrusive(""); } frame->SetDelayed(); trigger->Hold(); 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. trigger::Trigger* trigger = frame->GetTrigger(); if ( ! trigger) { builtin_error("lookup_hostname_txt() can only be called inside a when-condition"); return make_intrusive(""); } frame->SetDelayed(); trigger->Hold(); 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. trigger::Trigger* trigger = frame->GetTrigger(); if ( ! trigger) { builtin_error("lookup_hostname() can only be called inside a when-condition"); return make_intrusive(""); } frame->SetDelayed(); trigger->Hold(); dns_mgr->AsyncLookupName(host->CheckString(), new LookupHostCallback(trigger, frame->GetCall(), false)); return nullptr; %} %%{ #ifdef USE_GEOIP #include extern "C" { #include #include #include #include #include } 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 ( network_time > mmdb_msg_suppression_time + mmdb_msg_suppression_duration ) { mmdb_msg_count = 0; mmdb_msg_suppression_time = network_time; } if ( mmdb_msg_count >= mmdb_msg_limit ) return; ++mmdb_msg_count; va_list al; va_start(al, format); std::string msg = fmt(format, al); va_end(al); 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 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 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(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_loc; std::unique_ptr 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 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 IPAddr& addr, MMDB_lookup_result_s& result) { return mmdb_lookup(addr, result, false); } static bool mmdb_lookup_asn(const IPAddr& addr, MMDB_lookup_result_s& result) { return mmdb_lookup(addr, result, true); } static IntrusivePtr 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 make_intrusive(entry_data->data_size, entry_data->utf8_string); break; case MMDB_DATA_TYPE_DOUBLE: return make_intrusive(entry_data->double_value, TYPE_DOUBLE); break; case MMDB_DATA_TYPE_UINT32: return 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 = 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 = 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 val_mgr->Bool(mmdb_open_loc(f->CheckString())); #else return 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 val_mgr->Bool(mmdb_open_asn(f->CheckString())); #else return 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("geo_location"); auto location = make_intrusive(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; 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 ) { 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; builtin_error("Failed to open GeoIP ASN database"); } return 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 ? val_mgr->Count(0) : asn; } #else // not USE_GEOIP static int missing_geoip_reported = 0; if ( ! missing_geoip_reported ) { 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 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 make_intrusive(distance, TYPE_DOUBLE); %} ## 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 make_intrusive(str); %} # =========================================================================== # # Controlling Analyzer Behavior # # =========================================================================== %%{ #include "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 = sessions->FindConnection(cid); if ( ! c ) { reporter->Error("cannot find connection"); return val_mgr->False(); } analyzer::Analyzer* a = c->FindAnalyzer(aid); if ( ! a ) { if ( err_if_no_conn ) reporter->Error("connection does not have analyzer specified to disable"); return val_mgr->False(); } if ( prevent ) a->Parent()->PreventChildren(a->GetAnalyzerTag()); auto rval = a->Remove(); return 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 = sessions->FindConnection(cid); if ( ! c ) return val_mgr->False(); c->SetSkip(1); return 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 = sessions->FindConnection(cid); if ( ! c ) return val_mgr->False(); c->SetRecordPackets(do_record); return 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 = sessions->FindConnection(cid); if ( ! c ) return make_intrusive(0.0, TYPE_INTERVAL); double old_timeout = c->InactivityTimeout(); c->SetInactivityTimeout(t); return make_intrusive(old_timeout, TYPE_INTERVAL); %} # =========================================================================== # # 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 ( streq(file, "-") ) return make_intrusive(make_intrusive(stdout, "-", "w")); else return make_intrusive(make_intrusive(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 make_intrusive(make_intrusive(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 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 val_mgr->False(); return 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 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 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 val_mgr->True(); builtin_error(fmt("cannot create directory '%s': %s", filename, strerror(error))); return val_mgr->False(); } else return 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 ) { builtin_error(fmt("cannot remove directory '%s': %s", dirname, strerror(errno))); return val_mgr->False(); } else return 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 ) { builtin_error(fmt("cannot unlink file '%s': %s", filename, strerror(errno))); return val_mgr->False(); } else return 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 ) { builtin_error(fmt("cannot rename file '%s' to '%s': %s", src_filename, dst_filename, strerror(errno))); return val_mgr->False(); } else return 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 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 val_mgr->EmptyString(); return make_intrusive(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 %{ IntrusivePtr info{AdoptRef{}, f->Rotate()}; if ( info ) return info; // Record indicating error. static auto rotate_info = zeek::id::find_type("rotate_info"); info = make_intrusive(rotate_info); info->Assign(0, val_mgr->EmptyString()); info->Assign(1, val_mgr->EmptyString()); info->Assign(2, make_intrusive(0.0, TYPE_TIME)); info->Assign(3, make_intrusive(0.0, TYPE_TIME)); 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("rotate_info"); auto info = make_intrusive(rotate_info); bool is_pkt_dumper = false; bool is_addl_pkt_dumper = false; // Special case: one of current dump files. if ( pkt_dumper && streq(pkt_dumper->Path().c_str(), f->CheckString()) ) { is_pkt_dumper = true; pkt_dumper->Close(); } if ( addl_pkt_dumper && streq(addl_pkt_dumper->Path().c_str(), f->CheckString()) ) { is_addl_pkt_dumper = true; addl_pkt_dumper->Close(); } FILE* file = rotate_file(f->CheckString(), info.get()); if ( ! file ) { // Record indicating error. info->Assign(0, val_mgr->EmptyString()); info->Assign(1, val_mgr->EmptyString()); info->Assign(2, make_intrusive(0.0, TYPE_TIME)); info->Assign(3, make_intrusive(0.0, TYPE_TIME)); return info; } fclose(file); if ( is_pkt_dumper ) { info->Assign(2, make_intrusive(pkt_dumper->OpenTime(), TYPE_TIME)); pkt_dumper->Open(); } if ( is_addl_pkt_dumper ) info->Assign(2, make_intrusive(addl_pkt_dumper->OpenTime(), TYPE_TIME)); 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("log_rotate_base_time"); static auto base_time = log_rotate_base_time->AsString()->CheckString(); double base = parse_rotate_base_time(base_time); return make_intrusive(calc_next_rotate(network_time, i, base), TYPE_INTERVAL); %} ## 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 make_intrusive(-1.0, TYPE_DOUBLE); return make_intrusive(double(s.st_size), TYPE_DOUBLE); %} ## 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 %{ sessions->GetPacketFilter()->AddSrc(ip->AsAddr(), tcp_flags, prob); return 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 %{ sessions->GetPacketFilter()->AddSrc(snet, tcp_flags, prob); return 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 val_mgr->Bool(sessions->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 val_mgr->Bool(sessions->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 %{ sessions->GetPacketFilter()->AddDst(ip->AsAddr(), tcp_flags, prob); return 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 %{ sessions->GetPacketFilter()->AddDst(snet, tcp_flags, prob); return 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 val_mgr->Bool(sessions->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 val_mgr->Bool(sessions->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 val_mgr->Bool(mgr.CurrentSource() != 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 %{ net_suspend_processing(); return nullptr; %} ## Resumes Zeek's packet processing. ## ## .. zeek:see:: suspend_processing function continue_processing%(%) : any %{ net_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 ( ! rule_matcher ) return val_mgr->False(); c->Match((Rule::PatternType) pattern_type, s->Bytes(), s->Len(), from_orig, bol, eol, clear); return val_mgr->True(); %} # =========================================================================== # # Anonymization Functions # (Not Fully Functional) # # =========================================================================== %%{ #include "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 %{ AnonymizeIPAddr* ip_anon = ip_anonymizer[PREFIX_PRESERVING_A50]; if ( ip_anon ) { if ( a->AsAddr().GetFamily() == IPv6 ) 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()); AnonymizeIPAddr* ip_anon = ip_anonymizer[PREFIX_PRESERVING_A50]; if ( ip_anon ) { if ( a->AsSubNet().Prefix().GetFamily() == IPv6 ) 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 >= NUM_ADDR_ANONYMIZATION_CLASSES ) builtin_error("anonymize_addr(): invalid ip addr anonymization class"); if ( a->AsAddr().GetFamily() == IPv6 ) { builtin_error("anonymize_addr() not supported for IPv6 addresses"); return nullptr; } else { const uint32_t* bytes; a->AsAddr().GetBytes(&bytes); return make_intrusive(anonymize_ip(*bytes, (enum 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); %}