diff --git a/scripts/spicy/zeek.spicy b/scripts/spicy/zeek.spicy index 4af1621a59..cc24f96117 100644 --- a/scripts/spicy/zeek.spicy +++ b/scripts/spicy/zeek.spicy @@ -199,3 +199,312 @@ public function forward_packet(identifier: uint32): void &cxxname="zeek::spicy:: ## Gets the network time from Zeek. public function network_time(): time &cxxname="zeek::spicy::rt::network_time"; +## Opaque handle for a Zeek-side value. +public type ZeekVal = __library_type("::zeek::ValPtr"); + +## Opaque handle for a Zeek-side record value. +public type ZeekRecord = __library_type("::zeek::spicy::rt::ValRecordPtr"); + +## Opaque handle for a Zeek-side set value. +public type ZeekSet = __library_type("::zeek::spicy::rt::ValSetPtr"); + +## Opaque handle for a Zeek-side table value. +public type ZeekTable = __library_type("::zeek::spicy::rt::ValTablePtr"); + +## Opaque handle for a Zeek-side vector value. +public type ZeekVector = __library_type("::zeek::spicy::rt::ValVectorPtr"); + +## Returns the value of a global Zeek script variable of Zeek type ``addr``. +## Throws an exception if there's no such Zeek of that name, or if it's not of +## the expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_address(id: string): addr &cxxname="zeek::spicy::rt::get_address"; + +## Returns the value of a global Zeek script variable of Zeek type ``bool``. +## Throws an exception if there's no such Zeek of that name, or if it's not of +## the expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_bool(id: string): bool &cxxname="zeek::spicy::rt::get_bool"; + +## Returns the value of a global Zeek script variable of Zeek type ``count``. +## Throws an exception if there's no such Zeek of that name, or if it's not of +## the expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_count(id: string): uint64 &cxxname="zeek::spicy::rt::get_count"; + +## Returns the value of a global Zeek script variable of Zeek type ``double``. +## Throws an exception if there's no such Zeek of that name, or if it's not of +## the expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_double(id: string): real &cxxname="zeek::spicy::rt::get_double"; + +## Returns the value of a global Zeek script variable of Zeek type ``enum``. +## The value is returned as a string containing the enum's label name, without +## any scope. Throws an exception if there's no such Zeek of that name, or if +## it's not of the expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_enum(id: string): string &cxxname="zeek::spicy::rt::get_enum"; + +## Returns the value of a global Zeek script variable of Zeek type ``int``. +## Throws an exception if there's no such Zeek of that name, or if it's not of +## the expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_int(id: string): int64 &cxxname="zeek::spicy::rt::get_int"; + +## Returns the value of a global Zeek script variable of Zeek type +## ``interval``. Throws an exception if there's no such Zeek of that name, or +## if it's not of the expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_interval(id: string): interval &cxxname="zeek::spicy::rt::get_interval"; + +## Returns the value of a global Zeek script variable of Zeek type ``port``. +## Throws an exception if there's no such Zeek of that name, or if it's not of +## the expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_port(id: string): port &cxxname="zeek::spicy::rt::get_port"; + +## Returns the value of a global Zeek script variable of Zeek type ``record``. +## The value is returned as an opaque handle to the record, which can be used +## with the ``zeek::record_*()`` functions to access the record's fields. +## Throws an exception if there's no such Zeek of that name, or if it's not of +## the expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_record(id: string): ZeekRecord &cxxname="zeek::spicy::rt::get_record"; + +## Returns the value of a global Zeek script variable of Zeek type ``set``. The +## value is returned as an opaque handle to the set, which can be used with the +## ``zeek::set_*()`` functions to access the set's content. Throws an exception +## if there's no such Zeek of that name, or if it's not of the expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_set(id: string): ZeekSet &cxxname="zeek::spicy::rt::get_set"; + +## Returns the value of a global Zeek script variable of Zeek type ``string``. +## The string's value is returned as a Spicy ``bytes`` value. Throws an +## exception if there's no such Zeek of that name, or if it's not of the +## expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_string(id: string): bytes &cxxname="zeek::spicy::rt::get_string"; + +## Returns the value of a global Zeek script variable of Zeek type ``subnet``. +## Throws an exception if there's no such Zeek of that name, or if it's not of +## the expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_subnet(id: string): network &cxxname="zeek::spicy::rt::get_subnet"; + +## Returns the value of a global Zeek script variable of Zeek type ``table``. +## The value is returned as an opaque handle to the set, which can be used with +## the ``zeek::set_*()`` functions to access the set's content. Throws an +## exception if there's no such Zeek of that name, or if it's not of the +## expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_table(id: string): ZeekTable &cxxname="zeek::spicy::rt::get_table"; + +## Returns the value of a global Zeek script variable of Zeek type ``time``. +## Throws an exception if there's no such Zeek of that name, or if it's not of +## the expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_time(id: string): time &cxxname="zeek::spicy::rt::get_time"; + +## Returns the value of a global Zeek script variable of Zeek type ``vector``. +## The value is returned as an opaque handle to the vector, which can be used +## with the ``zeek::vector_*()`` functions to access the vector's content. +## Throws an exception if there's no such Zeek of that name, or if it's not of +## the expected type. +## +## id: fully-qualified name of the global Zeek variable to retrieve +public function get_vector(id: string): ZeekVector &cxxname="zeek::spicy::rt::get_vector"; + +## Returns an opaque handle to a global Zeek script variable. The handle can be +## used with the ``zeek::as_*()`` functions to access the variable's value. +## Throws an exception if there's no Zeek variable of that name. +public function get_value(id: string): ZeekVal &cxxname="zeek::spicy::rt::get_value"; + +## Returns a Zeek ``addr`` value refereced by an opaque handle. Throws an +## exception if the referenced value is not of the expected type. +public function as_address(v: ZeekVal): addr &cxxname="zeek::spicy::rt::as_address"; + +## Returns a Zeek ``bool`` value refereced by an opaque handle. Throws an +## exception if the referenced value is not of the expected type. +public function as_bool(v: ZeekVal): bool &cxxname="zeek::spicy::rt::as_bool"; + +## Returns a Zeek ``count`` value refereced by an opaque handle. Throws an +## exception if the referenced value is not of the expected type. +public function as_count(v: ZeekVal): uint64 &cxxname="zeek::spicy::rt::as_count"; + +## Returns a Zeek ``double`` value refereced by an opaque handle. Throws an +## exception if the referenced value is not of the expected type. +public function as_double(v: ZeekVal): real &cxxname="zeek::spicy::rt::as_double"; + +## Returns a Zeek ``enum`` value refereced by an opaque handle. Throws an +## exception if the referenced value is not of the expected type. +public function as_enum(v: ZeekVal): string &cxxname="zeek::spicy::rt::as_enum"; + +## Returns a Zeek ``int`` value refereced by an opaque handle. Throws an +## exception if the referenced value is not of the expected type. +public function as_int(v: ZeekVal): int64 &cxxname="zeek::spicy::rt::as_int"; + +## Returns a Zeek ``interval`` value refereced by an opaque handle. Throws an +## exception if the referenced value is not of the expected type. +public function as_interval(v: ZeekVal): interval &cxxname="zeek::spicy::rt::as_interval"; + +## Returns a Zeek ``port`` value refereced by an opaque handle. Throws an +## exception if the referenced value is not of the expected type. +public function as_port(v: ZeekVal): port &cxxname="zeek::spicy::rt::as_port"; + +## Returns a Zeek ``record`` value refereced by an opaque handle. Throws an +## exception if the referenced value is not of the expected type. +public function as_record(v: ZeekVal): ZeekRecord &cxxname="zeek::spicy::rt::as_record"; + +## Returns a Zeek ``set`` value refereced by an opaque handle. Throws an +## exception if the referenced value is not of the expected type. +public function as_set(v: ZeekVal): ZeekSet &cxxname="zeek::spicy::rt::as_set"; + +## Returns a Zeek ``string`` value refereced by an opaque handle. The string's +## value is returned as a Spicy ``bytes`` value. Throws an exception if the +## referenced value is not of the expected type. +public function as_string(v: ZeekVal): bytes &cxxname="zeek::spicy::rt::as_string"; + +## Returns a Zeek ``subnet`` value refereced by an opaque handle. Throws an +## exception if the referenced value is not of the expected type. +public function as_subnet(v: ZeekVal): network &cxxname="zeek::spicy::rt::as_subnet"; + +## Returns a Zeek ``table`` value refereced by an opaque handle. Throws an +## exception if the referenced value is not of the expected type. +public function as_table(v: ZeekVal): ZeekTable &cxxname="zeek::spicy::rt::as_table"; + +## Returns a Zeek ``time`` value refereced by an opaque handle. Throws an +## exception if the referenced value is not of the expected type. +public function as_time(v: ZeekVal): time &cxxname="zeek::spicy::rt::as_time"; + +## Returns a Zeek ``vector`` value refereced by an opaque handle. Throws an +## exception if the referenced value is not of the expected type. +public function as_vector(v: ZeekVal): ZeekVector &cxxname="zeek::spicy::rt::as_vector"; + +## Returns true if a Zeek set contains a given value. Throws an exception if +## the given ID does not exist, or does not have the expected type. +## +## id: fully-qualified name of the global Zeek set to check +## v: value to check for, which must be of the Spicy-side equivalent of the set's key type +public function set_contains(id: string, v: any): bool &cxxname="zeek::spicy::rt::set_contains"; + +## Returns true if a Zeek set contains a given value. Throws an exception if +## the set does not have the expected type. +## +## s: opaque handle to the Zeek set, as returned by other functions +## v: value to check for, which must be of the Spicy-side equivalent of the set's key type +public function set_contains(s: ZeekSet, v: any): bool &cxxname="zeek::spicy::rt::set_contains"; + +## Returns true if a Zeek table contains a given value. Throws an exception if +## the given ID does not exist, or does not have the expected type. +## +## id: fully-qualified name of the global Zeek table to check +## v: value to check for, which must be of the Spicy-side equivalent of the table's key type +public function table_contains(id: string, v: any): bool &cxxname="zeek::spicy::rt::table_contains"; + +## Returns true if a Zeek table contains a given value. Throws an exception if +## the given ID does not exist, or does not have the expected type. +## +## t: opaque handle to the Zeek table, as returned by other functions +## v: value to check for, which must be of the Spicy-side equivalent of the table's key type +public function table_contains(t: ZeekTable, v: any): bool &cxxname="zeek::spicy::rt::table_contains"; + +## Returns the value associated with a key in a Zeek table. Returns an error +## result if the key does not exist in the table. Throws an exception if the +## given table ID does not exist, or does not have the expected type. +## +## id: fully-qualified name of the global Zeek table to check +## v: value to lookup, which must be of the Spicy-side equivalent of the table's key type +public function table_lookup(id: string, v: any): optional &cxxname="zeek::spicy::rt::table_lookup"; + +## Returns the value associated with a key in a Zeek table. Returns an error +## result if the key does not exist in the table. Throws an exception if the +## given table ID does not exist, or does not have the expected type. +## +## t: opaque handle to the Zeek table, as returned by other functions +## v: value to lookup, which must be of the Spicy-side equivalent of the table's key type +public function table_lookup(t: ZeekTable, v: any): optional &cxxname="zeek::spicy::rt::table_lookup"; + +## Returns true if a Zeek record provides a value for a given field. This +## includes fields with `&default` values. Throws an exception if the given ID +## does not exist, or does not have the expected type. +## +## id: fully-qualified name of the global Zeek record to check field: name of +## the field to check +public function record_has_value(id: string, field: string): bool &cxxname="zeek::spicy::rt::record_has_field"; + +## Returns true if a Zeek record provides a value for a given field. +## This includes fields with `&default` values. +## +## r: opaque handle to the Zeek record, as returned by other functions +## field: name of the field to check +public function record_has_value(r: ZeekRecord, field: string): bool &cxxname="zeek::spicy::rt::record_has_field"; + +## Returns true if the type of a Zeek record has a field of a given name. +## Throws an exception if the given ID does not exist, or does not have the +## expected type. +## +## id: fully-qualified name of the global Zeek record to check +## field: name of the field to check +public function record_has_field(id: string, field: string): bool &cxxname="zeek::spicy::rt::record_has_field"; + +## Returns true if the type of a Zeek record has a field of a given name. +## +## r: opaque handle to the Zeek record, as returned by other functions +## field: name of the field to check +public function record_has_field(r: ZeekRecord, field: string): bool &cxxname="zeek::spicy::rt::record_has_field"; + +## Returns a field's value from a Zeek record. Throws an exception if the given +## ID does not exist, or does not have the expected type; or if there's no such +## field in the record type, or if the field does not have a value. +## +## id: fully-qualified name of the global Zeek record to check +## field: name of the field to retrieve +public function record_field(id: string, field: string): ZeekVal &cxxname="zeek::spicy::rt::record_field"; + +## Returns a field's value from a Zeek record. Throws an exception if the given +## record does not have such a field, or if the field does not have a value. +## +## r: opaque handle to the Zeek record, as returned by other functions +## field: name of the field to retrieve +public function record_field(r: ZeekRecord, field: string): ZeekVal &cxxname="zeek::spicy::rt::record_field"; + +## Returns the value of an index in a Zeek vector. Throws an exception if the +## given ID does not exist, or does not have the expected type; or if the index +## is out of bounds. +## +## id: fully-qualified name of the global Zeek vector to check +## index: index of the element to retrieve +public function vector_index(id: string, index: uint64): ZeekVal &cxxname="zeek::spicy::rt::vector_index"; + +## Returns the value of an index in a Zeek vector. Throws an exception if the +## index is out of bounds. +## +## v: opaque handle to the Zeek vector, as returned by other functions +## index: index of the element to retrieve +public function vector_index(v: ZeekVector, index: uint64): ZeekVal &cxxname="zeek::spicy::rt::vector_index"; + +## Returns the size of a Zeek vector. Throws an exception if the given ID does +## not exist, or does not have the expected type. +## +## id: fully-qualified name of the global Zeek vector to check +public function vector_size(id: string): uint64 &cxxname="zeek::spicy::rt::vector_size"; + +## Returns the size of a Zeek vector. +## +## v: opaque handle to the Zeek vector, as returned by other functions +public function vector_size(v: ZeekVector): uint64 &cxxname="zeek::spicy::rt::vector_size"; + diff --git a/src/spicy/runtime-support.cc b/src/spicy/runtime-support.cc index 610e0a0153..f5afd37461 100644 --- a/src/spicy/runtime-support.cc +++ b/src/spicy/runtime-support.cc @@ -133,15 +133,15 @@ TypePtr rt::create_record_type(const std::string& ns, const std::string& id, auto decls = std::make_unique(); for ( const auto& f : fields ) { - auto attrs = make_intrusive(nullptr, true, false); + auto attrs = make_intrusive<::zeek::detail::Attributes>(nullptr, true, false); if ( f.is_optional ) { - auto optional_ = make_intrusive(detail::ATTR_OPTIONAL); + auto optional_ = make_intrusive<::zeek::detail::Attr>(::zeek::detail::ATTR_OPTIONAL); attrs->AddAttr(std::move(optional_)); } if ( f.is_log ) { - auto log_ = make_intrusive(detail::ATTR_LOG); + auto log_ = make_intrusive<::zeek::detail::Attr>(::zeek::detail::ATTR_LOG); attrs->AddAttr(std::move(log_)); } diff --git a/src/spicy/runtime-support.h b/src/spicy/runtime-support.h index cae3fa322c..3c82f3004c 100644 --- a/src/spicy/runtime-support.h +++ b/src/spicy/runtime-support.h @@ -19,7 +19,10 @@ #include #include #include +#include +#include "IntrusivePtr.h" +#include "Type.h" #include "zeek/Desc.h" #include "zeek/Val.h" #include "zeek/spicy/cookie.h" @@ -71,9 +74,9 @@ class TypeMismatch : public UsageError { */ class ParameterMismatch : public TypeMismatch { public: - ParameterMismatch(const std::string_view& msg, std::string_view location = "") + ParameterMismatch(std::string_view msg, std::string_view location = "") : TypeMismatch(hilti::rt::fmt("Event parameter mismatch, %s", msg)) {} - ParameterMismatch(const std::string_view& have, const TypePtr& want, std::string_view location = "") + ParameterMismatch(std::string_view have, const TypePtr& want, std::string_view location = "") : ParameterMismatch(_fmt(have, want)) {} private: @@ -97,13 +100,13 @@ public: * Begins registration of a Spicy EVT module. All subsequent, other `register_*()` * function call will be associated with this module for documentation purposes. */ -void register_spicy_module_begin(const std::string& name, const std::string& description); +void register_spicy_module_begin(const std::string& id, const std::string& description); /** * Registers a Spicy protocol analyzer with its EVT meta information with the * plugin's runtime. */ -void register_protocol_analyzer(const std::string& name, hilti::rt::Protocol proto, +void register_protocol_analyzer(const std::string& id, hilti::rt::Protocol proto, const hilti::rt::Vector<::zeek::spicy::rt::PortRange>& ports, const std::string& parser_orig, const std::string& parser_resp, const std::string& replaces, const std::string& linker_scope); @@ -112,7 +115,7 @@ void register_protocol_analyzer(const std::string& name, hilti::rt::Protocol pro * Registers a Spicy file analyzer with its EVT meta information with the * plugin's runtime. */ -void register_file_analyzer(const std::string& name, const hilti::rt::Vector& mime_types, +void register_file_analyzer(const std::string& id, const hilti::rt::Vector& mime_types, const std::string& parser, const std::string& replaces, const std::string& linker_scope); /** Reports a Zeek-side "weird". */ @@ -122,7 +125,7 @@ void weird(const std::string& id, const std::string& addl); * Registers a Spicy packet analyzer with its EVT meta information with the * plugin's runtime. */ -void register_packet_analyzer(const std::string& name, const std::string& parser, const std::string& replaces, +void register_packet_analyzer(const std::string& id, const std::string& parser, const std::string& replaces, const std::string& linker_scope); /** Registers a Spicy-generated type to make it available inside Zeek. */ @@ -991,4 +994,328 @@ inline ValPtr to_val(const T& t, const TypePtr& target) { return target->AsEnumType()->GetEnumVal(bt); } + +/** + * Returns the Zeek value associated with a global Zeek-side ID. Throws if the + * ID does not exist. + */ +inline ValPtr get_value(const std::string& name) { + if ( auto id = zeek::detail::global_scope()->Find(name) ) + return id->GetVal(); + else + throw InvalidValue(util::fmt("no such Zeek variable: '%s'", name.c_str())); +} + +namespace detail { +/** Helper to raise a ``TypeMismatch`` exception. */ +inline auto type_mismatch(const ValPtr& v, const char* expected) { + throw TypeMismatch(util::fmt("type mismatch in Zeek value: expected %s, but got %s", expected, + ::zeek::type_name(v->GetType()->Tag()))); +} + +/** + * Helper to check the type of Zeek value against an expected type tag, raising + * a ``TypeMismatch`` exception on mismatch. + */ +inline auto check_type(const ValPtr& v, ::zeek::TypeTag type_tag, const char* expected) { + if ( v->GetType()->Tag() != type_tag ) + type_mismatch(v, expected); +} + +} // namespace detail + +/** Type for a Zeek record value. */ +using ValRecordPtr = ::zeek::IntrusivePtr<::zeek::RecordVal>; + +/** Type for a Zeek set value. */ +using ValSetPtr = ::zeek::IntrusivePtr<::zeek::TableVal>; + +/** Type for a Zeek table value. */ +using ValTablePtr = ::zeek::IntrusivePtr<::zeek::TableVal>; + +/** Type for a Zeek vector value. */ +using ValVectorPtr = ::zeek::IntrusivePtr<::zeek::VectorVal>; + +/** Converts a Zeek `addr` value to its Spicy equivalent. Throws on error. */ +inline ::hilti::rt::Address as_address(const ValPtr& v) { + detail::check_type(v, TYPE_ADDR, "address"); + return ::hilti::rt::Address(v->AsAddr()); +} + +/** Converts a Zeek `bool` value to its Spicy equivalent. Throws on error. */ +inline ::hilti::rt::Bool as_bool(const ValPtr& v) { + detail::check_type(v, TYPE_BOOL, "bool"); + return ::hilti::rt::Bool(v->AsBool()); +} + +/** Converts a Zeek `count` value to its Spicy equivalent. Throws on error. */ +inline hilti::rt::integer::safe as_count(const ValPtr& v) { + detail::check_type(v, TYPE_COUNT, "count"); + return v->AsCount(); +} + +/** Converts a Zeek `double` value to its Spicy equivalent. Throws on error. */ +inline double as_double(const ValPtr& v) { + detail::check_type(v, TYPE_DOUBLE, "double"); + return v->AsDouble(); +} + +/** + * Converts a Zeek `enum` value to a string containing the (unscoped) label + * name. Throws on error. + */ +inline std::string as_enum(const ValPtr& v) { + detail::check_type(v, TYPE_ENUM, "enum"); + // Zeek returns the name as "::", we just want the enum name. + return hilti::rt::rsplit1(v->GetType()->AsEnumType()->Lookup(v->AsEnum()), "::").second; +} + +/** Converts a Zeek `int` value to its Spicy equivalent. Throws on error. */ +inline hilti::rt::integer::safe as_int(const ValPtr& v) { + detail::check_type(v, TYPE_INT, "int"); + return v->AsInt(); +} + +/** Converts a Zeek `interval` value to its Spicy equivalent. Throws on error. */ +inline ::hilti::rt::Interval as_interval(const ValPtr& v) { + detail::check_type(v, TYPE_INTERVAL, "interval"); + return ::hilti::rt::Interval(v->AsInterval(), hilti::rt::Interval::SecondTag{}); +} + +/** Converts a Zeek `port` value to its Spicy equivalent. Throws on error. */ +inline ::hilti::rt::Port as_port(const ValPtr& v) { + detail::check_type(v, TYPE_PORT, "port"); + auto p = v->AsPortVal(); + // Wrap port number into safe integer to catch any overflows (Zeek returns + // an uint32, while HILTI wants an uint16). + return ::hilti::rt::Port(hilti::rt::integer::safe(p->Port()), p->PortType()); +} + +/** Converts a Zeek `record` value to its Spicy equivalent. Throws on error. */ +inline ValRecordPtr as_record(const ValPtr& v) { + detail::check_type(v, TYPE_RECORD, "record"); + return ::zeek::cast_intrusive<::zeek::RecordVal>(v); +} + +/** Converts a Zeek `set` value to its Spicy equivalent. Throws on error. */ +inline ValSetPtr as_set(const ValPtr& v) { + detail::check_type(v, TYPE_TABLE, "set"); + + if ( ! v->AsTableVal()->GetType()->IsSet() ) + detail::type_mismatch(v, "set"); + + return ::zeek::cast_intrusive<::zeek::TableVal>(v); +} + +/** Converts a Zeek `string` value to its Spicy equivalent. Throws on error. */ +inline hilti::rt::Bytes as_string(const ValPtr& v) { + detail::check_type(v, TYPE_STRING, "string"); + auto str = v->AsString(); + return hilti::rt::Bytes(reinterpret_cast(str->Bytes()), str->Len()); +} + +/** Converts a Zeek `subnet` value to its Spicy equivalent. Throws on error. */ +inline ::hilti::rt::Network as_subnet(const ValPtr& v) { + detail::check_type(v, TYPE_SUBNET, "subnet"); + auto subnet = v->AsSubNet(); + return ::hilti::rt::Network(subnet.Prefix(), subnet.Length()); +} + +/** Converts a Zeek `table` value to its Spicy equivalent. Throws on error. */ +inline ValTablePtr as_table(const ValPtr& v) { + detail::check_type(v, TYPE_TABLE, "table"); + + if ( v->AsTableVal()->GetType()->IsSet() ) + detail::type_mismatch(v, "table"); + + return ::zeek::cast_intrusive<::zeek::TableVal>(v); +} + +/** Converts a Zeek `time` value to its Spicy equivalent. Throws on error. */ +inline ::hilti::rt::Time as_time(const ValPtr& v) { + detail::check_type(v, TYPE_TIME, "time"); + return ::hilti::rt::Time(v->AsTime(), hilti::rt::Time::SecondTag{}); +} + +/** Converts a Zeek `vector` value to its Spicy equivalent. Throws on error. */ +inline ValVectorPtr as_vector(const ValPtr& v) { + detail::check_type(v, TYPE_VECTOR, "vector"); + return ::zeek::cast_intrusive<::zeek::VectorVal>(v); +} + + +/** Retrieves a global Zeek variable of assumed type `addr`. Throws on error. */ +inline hilti::rt::Address get_address(const std::string& name) { return as_address(get_value(name)); } + +/** Retrieves a global Zeek variable of assumed type `bool`. Throws on error. */ +inline hilti::rt::Bool get_bool(const std::string& name) { return as_bool(get_value(name)); } + +/** Retrieves a global Zeek variable of assumed type `count`. Throws on error. */ +inline hilti::rt::integer::safe get_count(const std::string& name) { return as_count(get_value(name)); } + +/** Retrieves a global Zeek variable of assumed type `double`. Throws on error. */ +inline double get_double(const std::string& name) { return as_double(get_value(name)); } + +/** + * Retrieves a global Zeek variable of assumed type `enum` as a string + * containing the (unscoped) label name. Throws on error. + */ +inline std::string get_enum(const std::string& name) { return as_enum(get_value(name)); } + +/** Retrieves a global Zeek variable of assumed type `int`. Throws on error. */ +inline hilti::rt::integer::safe get_int(const std::string& name) { return as_int(get_value(name)); } + +/** Retrieves a global Zeek variable of assumed type `interval`. Throws on error. */ +inline hilti::rt::Interval get_interval(const std::string& name) { return as_interval(get_value(name)); } + +/** Retrieves a global Zeek variable of assumed type `port`. Throws on error. */ +inline hilti::rt::Port get_port(const std::string& name) { return as_port(get_value(name)); } + +/** Retrieves a global Zeek variable of assumed type `record`. Throws on error. */ +inline ValRecordPtr get_record(const std::string& name) { return as_record(get_value(name)); } + +/** Retrieves a global Zeek variable of assumed type `set`. Throws on error. */ +inline ValSetPtr get_set(const std::string& name) { return as_set(get_value(name)); } + +/** Retrieves a global Zeek variable of assumed type `string`. Throws on error. */ +inline hilti::rt::Bytes get_string(const std::string& name) { return as_string(get_value(name)); } + +/** Retrieves a global Zeek variable of assumed type `subnet`. Throws on error. */ +inline hilti::rt::Network get_subnet(const std::string& name) { return as_subnet(get_value(name)); } + +/** Retrieves a global Zeek variable of assumed type `table`. Throws on error. */ +inline ValTablePtr get_table(const std::string& name) { return as_table(get_value(name)); } + +/** Retrieves a global Zeek variable of assumed type `time`. Throws on error. */ +inline hilti::rt::Time get_time(const std::string& name) { return as_time(get_value(name)); } + +/** Retrieves a global Zeek variable of assumed type `vector`. Throws on error. */ +inline ValVectorPtr get_vector(const std::string& name) { return as_vector(get_value(name)); } + +/** Retrieves the value of Zeek record field. Throws on error. */ +inline ::zeek::ValPtr record_field(const zeek::spicy::rt::ValRecordPtr& v, const std::string& field) { + auto index = v->GetType()->AsRecordType()->FieldOffset(field.c_str()); + if ( index < 0 ) + throw InvalidValue(util::fmt("no such record field: %s", field.c_str())); + + if ( auto x = v->GetFieldOrDefault(index) ) + return x; + else + throw InvalidValue(util::fmt("record field is not set: %s", field.c_str())); +} + +/** Retrieves the value of Zeek record field. Throws on error. */ +inline ::zeek::ValPtr record_field(const std::string& name, const std::string& index) { + return record_field(get_record(name), index); +} + +/** Check if a Zeek record has a field's value set. Throws on errors. */ +inline hilti::rt::Bool record_has_value(const zeek::spicy::rt::ValRecordPtr& v, const std::string& field) { + auto index = v->GetType()->AsRecordType()->FieldOffset(field.c_str()); + if ( index < 0 ) + throw InvalidValue(util::fmt("no such field in record type: %s", field.c_str())); + + return v->HasField(index); +} + +/** Checks if a Zeek record has a field's value set. Throws on errors. */ +inline hilti::rt::Bool record_has_value(const std::string& name, const std::string& index) { + return record_has_value(get_record(name), index); +} + +/** Check if a Zeek record type has a field of a give name. Throws on errors. */ +inline hilti::rt::Bool record_has_field(const zeek::spicy::rt::ValRecordPtr& v, const std::string& field) { + return v->GetType()->AsRecordType()->FieldOffset(field.c_str()) >= 0; +} + +/** Check if a Zeek record type has a field of a give name. Throws on errors. */ +inline hilti::rt::Bool record_has_field(const std::string& name, const std::string& index) { + return record_has_value(get_record(name), index); +} + +/** Checks if a Zeek set contains a given element. Throws on errors. */ +template +::hilti::rt::Bool set_contains(const ValSetPtr& v, const T& key) { + auto index = v->GetType()->AsTableType()->GetIndexTypes()[0]; + return (v->Find(to_val(key, index)) != nullptr); +} + +/** Checks if a Zeek set contains a given element. Throws on errors. */ +template +::hilti::rt::Bool set_contains(const std::string& name, const T& key) { + return set_contains(get_set(name), key); +} + +/** Checks if a Zeek table contains a given element. Throws on errors. */ +template +::hilti::rt::Bool table_contains(const ValTablePtr& v, const T& key) { + auto index = v->GetType()->AsTableType()->GetIndexTypes()[0]; + return (v->Find(to_val(key, index)) != nullptr); +} + +/** Check if a Zeek table contains a given element. Throws on errors. */ +template +::hilti::rt::Bool table_contains(const std::string& name, const T& key) { + return table_contains(get_table(name), key); +} + +/** + * Retrieves a value from a Zeek table. Returns an error value if the key does + * not exist. Throws on other errors. + */ +template +std::optional<::zeek::ValPtr> table_lookup(const zeek::spicy::rt::ValTablePtr& v, const T& key) { + auto index = v->GetType()->AsTableType()->GetIndexTypes()[0]; + if ( auto x = v->FindOrDefault(to_val(key, index)) ) + return x; + else + return {}; +} + +/** + * Retrieves a value from a Zeek table. Returns an error value if the key does + * not exist. Throws on other errors. + */ +template +std::optional<::zeek::ValPtr> table_lookup(const std::string& name, const T& key) { + return table_lookup(get_table(name), key); +} + +/** Returns a Zeek vector element. Throws on errors. */ +inline ::zeek::ValPtr vector_index(const zeek::spicy::rt::ValVectorPtr& v, + const hilti::rt::integer::safe& index) { + if ( index >= v->Size() ) + throw InvalidValue(util::fmt("vector index out of bounds: %" PRIu64, index.Ref())); + + return v->ValAt(index); +} + +/** Returns a Zeek vector element. Throws on errors. */ +inline ::zeek::ValPtr vector_index(const std::string& name, const hilti::rt::integer::safe& index) { + return vector_index(get_vector(name), index); +} + +/** Returns the size of a Zeek vector. Throws on errors. */ +inline hilti::rt::integer::safe vector_size(const zeek::spicy::rt::ValVectorPtr& v) { return v->Size(); } + +/** Returns the size of a Zeek vector. Throws on errors. */ +inline hilti::rt::integer::safe vector_size(const std::string& name) { return vector_size(get_vector(name)); } + } // namespace zeek::spicy::rt + +namespace hilti::rt::detail::adl { +// Stringification for opaque type handles. +inline std::string to_string(const zeek::ValPtr& v, detail::adl::tag /* unused */) { return ""; } + +inline std::string to_string(const zeek::spicy::rt::ValRecordPtr& v, detail::adl::tag /* unused */) { + return ""; +} + +inline std::string to_string(const zeek::spicy::rt::ValTablePtr& v, detail::adl::tag /* unused */) { + return ""; +} + +inline std::string to_string(const zeek::spicy::rt::ValVectorPtr& v, detail::adl::tag /* unused */) { + return ""; +} +} // namespace hilti::rt::detail::adl diff --git a/testing/btest/Baseline/spicy.get-values/output b/testing/btest/Baseline/spicy.get-values/output new file mode 100644 index 0000000000..4e01755636 --- /dev/null +++ b/testing/btest/Baseline/spicy.get-values/output @@ -0,0 +1,6 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. + + + + + diff --git a/testing/btest/spicy/get-values.spicy b/testing/btest/spicy/get-values.spicy new file mode 100644 index 0000000000..e0e5ab6170 --- /dev/null +++ b/testing/btest/spicy/get-values.spicy @@ -0,0 +1,99 @@ +# @TEST-REQUIRES: have-spicy +# +# @TEST-EXEC: spicyz -d -o test.hlto %INPUT +# @TEST-EXEC: zeek globals.zeek test.hlto Spicy::enable_print=T >output +# @TEST-EXEC: btest-diff output +# +# @TEST-DOC: Test access to Zeek-side globals. + +module Foo; + +import zeek; + +assert zeek::get_address("Bar::address_") == 1.2.3.4; +assert zeek::get_bool("Bar::bool_") == True; +assert zeek::get_count("Bar::count_") == 42; +assert zeek::get_double("Bar::double_") == 42.0; +assert zeek::get_enum("Bar::enum_") == "Foo"; +assert zeek::get_int("Bar::int_") == 42; +assert zeek::get_interval("Bar::interval_") == interval(42); +assert zeek::get_port("Bar::port_") == 42/tcp; +assert zeek::get_string("Bar::string_") == b"xxx"; +assert zeek::get_subnet("Bar::subnet_") == 1.2.3.4/16; +assert zeek::get_time("Bar::time_") == time(42.0); + +assert zeek::as_address(zeek::get_value("Bar::address_")) == 1.2.3.4; +assert zeek::as_bool(zeek::get_value("Bar::bool_")) == True; +assert zeek::as_count(zeek::get_value("Bar::count_")) == 42; +assert zeek::as_double(zeek::get_value("Bar::double_")) == 42.0; +assert zeek::as_enum(zeek::get_value("Bar::enum_")) == "Foo"; +assert zeek::as_int(zeek::get_value("Bar::int_")) == 42; +assert zeek::as_interval(zeek::get_value("Bar::interval_")) == interval(42); +assert zeek::as_port(zeek::get_value("Bar::port_")) == 42/tcp; +assert zeek::as_string(zeek::get_value("Bar::string_")) == b"xxx"; +assert zeek::as_subnet(zeek::get_value("Bar::subnet_")) == 1.2.3.4/16; +assert zeek::as_time(zeek::get_value("Bar::time_")) == time(42.0); + +assert zeek::as_string(zeek::record_field("Bar::record_", "x")) == b"foo"; +assert zeek::as_int(zeek::record_field("Bar::record_", "y")) == 42; +assert zeek::as_int(zeek::record_field(zeek::get_record("Bar::record_"), "y")) == 42; +assert zeek::record_has_value("Bar::record_", "x"); +assert zeek::record_has_value(zeek::get_record("Bar::record_"), "y"); +assert zeek::record_has_value("Bar::record_", "y"); +assert ! zeek::record_has_value("Bar::record_", "z"); +assert zeek::record_has_field("Bar::record_", "x"); +assert ! zeek::record_has_field("Bar::record_", "z"); +assert-exception zeek::record_field("Bar::record_", "z"); # not set + +assert zeek::set_contains("Bar::set_", "foo"); +assert ! zeek::set_contains("Bar::set_", "xxx"); +assert zeek::set_contains(zeek::get_set("Bar::set_"), "foo"); + +assert zeek::table_contains("Bar::table_", "foo"); +assert ! zeek::table_contains("Bar::table_", "xxx"); +assert zeek::table_contains(zeek::get_table("Bar::table_"), "foo"); +assert zeek::as_string(*zeek::table_lookup("Bar::table_", "foo")) == b"bar"; +assert zeek::as_string(*zeek::table_lookup(zeek::get_table("Bar::table_"), "foo")) == b"bar"; +assert ! zeek::table_lookup("Bar::table_", "does-not-exist"); + +assert zeek::as_count(zeek::vector_index("Bar::vector_", 2)) == 2; +assert zeek::as_count(zeek::vector_index(zeek::get_vector("Bar::vector_"), 2)) == 2; + +assert-exception zeek::get_bool("Bar::does_not_exist"); +assert-exception zeek::get_bool("Bar::string_"); + +# Test stringifcation. +print zeek::get_value("Bar::bool_"); +print zeek::get_record("Bar::record_"); +print zeek::get_set("Bar::set_"); +print zeek::get_table("Bar::table_"); +print zeek::get_vector("Bar::vector_"); + +# @TEST-START-FILE globals.zeek +module Bar; + +type Record: record { + x: string; + y: int &default=42; + z: bool &optional; +}; + +type Enum: enum { Foo, Bar }; + +const address_: addr = 1.2.3.4; +const bool_: bool = T; +const count_: count = 42; +const double_: double = 42.0; +const enum_: Enum = Foo; +const int_: int = 42; +const interval_: interval = 42sec; +const port_: port = 42/tcp; +const record_: Record = [$x="foo"]; +const set_: set[string] = set("foo", "bar"); +const string_: string = "xxx"; +const subnet_: subnet = 1.2.3.4/16; +const table_: table[string] of string = table(["foo"] = "bar"); +const time_: time = double_to_time(42.0); +const vector_: vector of count = vector(0, 1, 2); + +# @TEST-END-FILE