Merge remote-tracking branch 'origin/topic/robin/gh-3521-zeek-val'

* origin/topic/robin/gh-3521-zeek-val:
  Bump Spicy and documentation submodules.
  Spicy: Provide runtime API to access Zeek-side globals.
  Spicy: Reformat `zeek.spicy` with `spicy-format`.
  Spicy: Extend exception hierarchy.
This commit is contained in:
Robin Sommer 2024-06-20 15:51:42 +02:00
commit b5206f818a
No known key found for this signature in database
GPG key ID: D8187293B3FFE5D0
9 changed files with 905 additions and 72 deletions

84
CHANGES
View file

@ -1,3 +1,87 @@
7.0.0-dev.392 | 2024-06-20 15:51:42 +0200
* Spicy: Provide runtime API to access Zeek-side globals. (Robin Sommer, Corelight)
This allows to read Zeek global variables from inside Spicy code. The
main challenge here is supporting all of Zeek's data type in a
type-safe manner.
The most straight-forward API is a set of functions
`get_<type>(<id>)`, where `<type>` is the Zeek-side type
name (e.g., `count`, `string`, `bool`) and `<id>` is the fully scoped
name of the Zeek-side global (e.g., `MyModule::Boolean`). These
functions then return the corresponding Zeek value, converted in an
appropriate Spicy type. Example:
Zeek:
module Foo;
const x: count = 42;
const y: string = "xxx";
Spicy:
import zeek;
assert zeek::get_count("Foo::x") == 42;
assert zeek::get_string("Foo::y") == b"xxx"; # returns bytes(!)
For container types, the `get_*` function returns an opaque types that
can be used to access the containers' values. An additional set of
functions `as_<type>` allows converting opaque values of atomic
types to Spicy equivalents. Example:
Zeek:
module Foo;
const s: set[count] = { 1, 2 };
const t: table[count] of string = { [1] = "One", [2] = "Two" }
Spicy:
# Check set membership.
local set_ = zeek::get_set("Foo::s");
assert zeek::set_contains(set_, 1) == True
# Look up table element.
local table_ = zeek::get_table("Foo::t");
local value = zeek::table_lookup(t, 1);
assert zeek::as_string(value) == b"One"
There are also functions for accessing elements of Zeek-side vectors
and records.
If any of these `zeek::*` conversion functions fails (e.g., due to a
global of that name not existing), it will throw an exception.
The documentation has more information on all of this.
Design considerations:
- We support only reading Zeek variables, not writing. This is
both to simplify the API, and also conceptually to avoid
offering backdoors into Zeek state that could end up with a very
tight coupling of Spicy and Zeek code.
- We accept that a single access might be relatively slow due to
name lookup and data conversion. This is primarily meant for
configuration-style data, not for transferring lots of dynamic
state over.
- In that spirit, we don't support deep-copying complex data types
from Zeek over to Spicy. This is (1) to avoid performance
problems when accidentally copying large containers over,
potentially even at every access; and (2) to avoid the two sides
getting out of sync if one ends up modifying a container without
the other being able to see it.
* Spicy: Reformat `zeek.spicy` with `spicy-format`. (Robin Sommer, Corelight)
* Spicy: Extend exception hierarchy. (Robin Sommer, Corelight)
We move the current `TypeMismatch` into a new `ParameterMismatch`
exception that's derived from a more general `TypeMismatch` now that
can also be used for other, non-parameter mismatches.
7.0.0-dev.387 | 2024-06-18 10:52:25 +0200 7.0.0-dev.387 | 2024-06-18 10:52:25 +0200
* CMakeLists: Disable -Werror for 3rdparty/sqlite3.c (Arne Welzel, Corelight) * CMakeLists: Disable -Werror for 3rdparty/sqlite3.c (Arne Welzel, Corelight)

View file

@ -1 +1 @@
7.0.0-dev.387 7.0.0-dev.392

@ -1 +1 @@
Subproject commit 5ff0cfea39ece44d1ef94f9762926b4bb4138d58 Subproject commit 588c79a8c4d3cb7e7e8265a9e85d59cb4bd5f972

2
doc

@ -1 +1 @@
Subproject commit 5c377d232043cfcaf23df260880ca1613b19a9f4 Subproject commit 44651a45261613b14dbd44e0ea8376346a689bd8

View file

@ -12,13 +12,13 @@ import spicy;
## ##
## This function has been deprecated and will be removed. Use ``spicy::accept_input`` ## This function has been deprecated and will be removed. Use ``spicy::accept_input``
## instead, which will have the same effect with Zeek. ## instead, which will have the same effect with Zeek.
public function confirm_protocol() : void &cxxname="zeek::spicy::rt::confirm_protocol"; public function confirm_protocol(): void &cxxname="zeek::spicy::rt::confirm_protocol";
## [Deprecated] Triggers a DPD protocol violation for the current connection. ## [Deprecated] Triggers a DPD protocol violation for the current connection.
## ##
## This function has been deprecated and will be removed. Use ``spicy::decline_input`` ## This function has been deprecated and will be removed. Use ``spicy::decline_input``
## instead, which will have the same effect with Zeek. ## instead, which will have the same effect with Zeek.
public function reject_protocol(reason: string) : void &cxxname="zeek::spicy::rt::reject_protocol"; public function reject_protocol(reason: string): void &cxxname="zeek::spicy::rt::reject_protocol";
## Reports a "weird" to Zeek. This should be used with similar semantics as in ## Reports a "weird" to Zeek. This should be used with similar semantics as in
## Zeek: something quite unexpected happening at the protocol level, which however ## Zeek: something quite unexpected happening at the protocol level, which however
@ -31,19 +31,19 @@ public function reject_protocol(reason: string) : void &cxxname="zeek::spicy::rt
public function weird(id: string, addl: string = "") &cxxname="zeek::spicy::rt::weird"; public function weird(id: string, addl: string = "") &cxxname="zeek::spicy::rt::weird";
## Returns true if we're currently parsing the originator side of a connection. ## Returns true if we're currently parsing the originator side of a connection.
public function is_orig() : bool &cxxname="zeek::spicy::rt::is_orig"; public function is_orig(): bool &cxxname="zeek::spicy::rt::is_orig";
## Returns the current connection's UID. ## Returns the current connection's UID.
public function uid() : string &cxxname="zeek::spicy::rt::uid"; public function uid(): string &cxxname="zeek::spicy::rt::uid";
## Returns the current connection's 4-tuple ID to make IP address and port information available. ## Returns the current connection's 4-tuple ID to make IP address and port information available.
public function conn_id() : tuple<orig_h: addr, orig_p: port, resp_h: addr, resp_p: port> &cxxname="zeek::spicy::rt::conn_id"; public function conn_id(): tuple<orig_h: addr, orig_p: port, resp_h: addr, resp_p: port> &cxxname="zeek::spicy::rt::conn_id";
## Instructs Zeek to flip the directionality of the current connection. ## Instructs Zeek to flip the directionality of the current connection.
public function flip_roles() : void &cxxname="zeek::spicy::rt::flip_roles"; public function flip_roles(): void &cxxname="zeek::spicy::rt::flip_roles";
## Returns the number of packets seen so far on the current side of the current connection. ## Returns the number of packets seen so far on the current side of the current connection.
public function number_packets() : uint64 &cxxname="zeek::spicy::rt::number_packets"; public function number_packets(): uint64 &cxxname="zeek::spicy::rt::number_packets";
## Opaque handle to a protocol analyzer. ## Opaque handle to a protocol analyzer.
public type ProtocolHandle = __library_type("zeek::spicy::rt::ProtocolHandle"); public type ProtocolHandle = __library_type("zeek::spicy::rt::ProtocolHandle");
@ -65,7 +65,7 @@ public type ProtocolHandle = __library_type("zeek::spicy::rt::ProtocolHandle");
## Note: For backwards compatibility, the analyzer argument can be left unset to add ## Note: For backwards compatibility, the analyzer argument can be left unset to add
## a DPD analyzer. This use is deprecated, though; use the single-argument version of ## a DPD analyzer. This use is deprecated, though; use the single-argument version of
## `protocol_begin` for that instead. ## `protocol_begin` for that instead.
public function protocol_begin(analyzer: optional<string>, protocol: spicy::Protocol = spicy::Protocol::TCP) : void &cxxname="zeek::spicy::rt::protocol_begin"; public function protocol_begin(analyzer: optional<string>, protocol: spicy::Protocol = spicy::Protocol::TCP): void &cxxname="zeek::spicy::rt::protocol_begin";
## Adds a Zeek-side DPD child protocol analyzer performing dynamic protocol detection ## Adds a Zeek-side DPD child protocol analyzer performing dynamic protocol detection
## on subsequently provided data. ## on subsequently provided data.
@ -78,7 +78,7 @@ public function protocol_begin(analyzer: optional<string>, protocol: spicy::Prot
## ##
## protocol: the transport-layer protocol on which to perform protocol detection; ## protocol: the transport-layer protocol on which to perform protocol detection;
## only TCP is currently supported here ## only TCP is currently supported here
public function protocol_begin(protocol: spicy::Protocol = spicy::Protocol::TCP) : void &cxxname="zeek::spicy::rt::protocol_begin"; public function protocol_begin(protocol: spicy::Protocol = spicy::Protocol::TCP): void &cxxname="zeek::spicy::rt::protocol_begin";
## Gets a handle to a Zeek-side child protocol analyzer for the current connection. ## Gets a handle to a Zeek-side child protocol analyzer for the current connection.
## ##
@ -98,7 +98,7 @@ public function protocol_begin(protocol: spicy::Protocol = spicy::Protocol::TCP)
## protocol: the transport-layer protocol that the analyser uses; only TCP is ## protocol: the transport-layer protocol that the analyser uses; only TCP is
## currently supported here ## currently supported here
## ##
public function protocol_handle_get_or_create(analyzer: string, protocol: spicy::Protocol = spicy::Protocol::TCP) : ProtocolHandle &cxxname="zeek::spicy::rt::protocol_handle_get_or_create"; public function protocol_handle_get_or_create(analyzer: string, protocol: spicy::Protocol = spicy::Protocol::TCP): ProtocolHandle &cxxname="zeek::spicy::rt::protocol_handle_get_or_create";
## Forwards protocol data to all previously instantiated Zeek-side child protocol analyzers of a given transport-layer. ## Forwards protocol data to all previously instantiated Zeek-side child protocol analyzers of a given transport-layer.
## ##
@ -107,7 +107,7 @@ public function protocol_handle_get_or_create(analyzer: string, protocol: spicy:
## data: chunk of data to forward to child analyzer ## data: chunk of data to forward to child analyzer
## ##
## protocol: the transport-layer protocol of the children to forward to; only TCP is currently supported here ## protocol: the transport-layer protocol of the children to forward to; only TCP is currently supported here
public function protocol_data_in(is_orig: bool, data: bytes, protocol: spicy::Protocol = spicy::Protocol::TCP) : void &cxxname="zeek::spicy::rt::protocol_data_in"; public function protocol_data_in(is_orig: bool, data: bytes, protocol: spicy::Protocol = spicy::Protocol::TCP): void &cxxname="zeek::spicy::rt::protocol_data_in";
## Forwards protocol data to a specific previously instantiated Zeek-side child analyzer. ## Forwards protocol data to a specific previously instantiated Zeek-side child analyzer.
## ##
@ -116,7 +116,7 @@ public function protocol_data_in(is_orig: bool, data: bytes, protocol: spicy::Pr
## data: chunk of data to forward to child analyzer ## data: chunk of data to forward to child analyzer
## ##
## h: handle to the child analyzer to forward data into ## h: handle to the child analyzer to forward data into
public function protocol_data_in(is_orig: bool, data: bytes, h: ProtocolHandle) : void &cxxname="zeek::spicy::rt::protocol_data_in"; public function protocol_data_in(is_orig: bool, data: bytes, h: ProtocolHandle): void &cxxname="zeek::spicy::rt::protocol_data_in";
## Signals a gap in input data to all previously instantiated Zeek-side child protocol analyzers. ## Signals a gap in input data to all previously instantiated Zeek-side child protocol analyzers.
## ##
@ -127,11 +127,11 @@ public function protocol_data_in(is_orig: bool, data: bytes, h: ProtocolHandle)
## len: size of gap ## len: size of gap
## ##
## h: optional handle to the child analyzer signal a gap to, else signal to all child analyzers ## h: optional handle to the child analyzer signal a gap to, else signal to all child analyzers
public function protocol_gap(is_orig: bool, offset: uint64, len: uint64, h: optional<ProtocolHandle> = Null) : void &cxxname="zeek::spicy::rt::protocol_gap"; public function protocol_gap(is_orig: bool, offset: uint64, len: uint64, h: optional<ProtocolHandle> = Null): void &cxxname="zeek::spicy::rt::protocol_gap";
## Signals end-of-data to all previously instantiated Zeek-side child protocol ## Signals end-of-data to all previously instantiated Zeek-side child protocol
## analyzers and removes them. ## analyzers and removes them.
public function protocol_end() : void &cxxname="zeek::spicy::rt::protocol_end"; public function protocol_end(): void &cxxname="zeek::spicy::rt::protocol_end";
## Signals end-of-data to the given child analyzer and removes it. ## Signals end-of-data to the given child analyzer and removes it.
## ##
@ -147,54 +147,364 @@ public function protocol_handle_close(handle: ProtocolHandle): void &cxxname="ze
## Optionally, a mime type can be provided. It will be passed on to Zeek's file analysis framework. ## Optionally, a mime type can be provided. It will be passed on to Zeek's file analysis framework.
## Optionally, a file ID can be provided. It will be passed on to Zeek's file analysis framework. ## Optionally, a file ID can be provided. It will be passed on to Zeek's file analysis framework.
## Returns the Zeek-side file ID of the new file. ## Returns the Zeek-side file ID of the new file.
public function file_begin(mime_type: optional<string> = Null, fuid: optional<string> = Null) : string &cxxname="zeek::spicy::rt::file_begin"; public function file_begin(mime_type: optional<string> = Null, fuid: optional<string> = Null): string &cxxname="zeek::spicy::rt::file_begin";
## Returns the current file's FUID. ## Returns the current file's FUID.
public function fuid() : string &cxxname="zeek::spicy::rt::fuid"; public function fuid(): string &cxxname="zeek::spicy::rt::fuid";
## Terminates the currently active Zeek-side session, flushing all state. Any ## Terminates the currently active Zeek-side session, flushing all state. Any
## subsequent activity will start a new session from scratch. This can only be ## subsequent activity will start a new session from scratch. This can only be
## called from inside a protocol analyzer. ## called from inside a protocol analyzer.
public function terminate_session() : void &cxxname="zeek::spicy::rt::terminate_session"; public function terminate_session(): void &cxxname="zeek::spicy::rt::terminate_session";
## Tells Zeek to skip sending any further input data to the current analyzer. ## Tells Zeek to skip sending any further input data to the current analyzer.
## This is supported for protocol and file analyzers. ## This is supported for protocol and file analyzers.
public function skip_input() : void &cxxname="zeek::spicy::rt::skip_input"; public function skip_input(): void &cxxname="zeek::spicy::rt::skip_input";
## Signals the expected size of a file to Zeek's file analysis. ## Signals the expected size of a file to Zeek's file analysis.
## ##
## size: expected size of file ## size: expected size of file
## fid: Zeek-side ID of the file to operate on; if not given, the file started by the most recent file_begin() will be used ## fid: Zeek-side ID of the file to operate on; if not given, the file started by the most recent file_begin() will be used
public function file_set_size(size: uint64, fid: optional<string> = Null) : void &cxxname="zeek::spicy::rt::file_set_size"; public function file_set_size(size: uint64, fid: optional<string> = Null): void &cxxname="zeek::spicy::rt::file_set_size";
## Passes file content on to Zeek's file analysis. ## Passes file content on to Zeek's file analysis.
## ##
## data: chunk of raw data to pass into analysis ## data: chunk of raw data to pass into analysis
## fid: Zeek-side ID of the file to operate on; if not given, the file started by the most recent file_begin() will be used ## fid: Zeek-side ID of the file to operate on; if not given, the file started by the most recent file_begin() will be used
public function file_data_in(data: bytes, fid: optional<string> = Null) : void &cxxname="zeek::spicy::rt::file_data_in"; public function file_data_in(data: bytes, fid: optional<string> = Null): void &cxxname="zeek::spicy::rt::file_data_in";
## Passes file content at a specific offset on to Zeek's file analysis. ## Passes file content at a specific offset on to Zeek's file analysis.
## ##
## data: chunk of raw data to pass into analysis ## data: chunk of raw data to pass into analysis
## offset: position in file where data starts ## offset: position in file where data starts
## fid: Zeek-side ID of the file to operate on; if not given, the file started by the most recent file_begin() will be used ## fid: Zeek-side ID of the file to operate on; if not given, the file started by the most recent file_begin() will be used
public function file_data_in_at_offset(data: bytes, offset: uint64, fid: optional<string> = Null) : void &cxxname="zeek::spicy::rt::file_data_in_at_offset"; public function file_data_in_at_offset(data: bytes, offset: uint64, fid: optional<string> = Null): void &cxxname="zeek::spicy::rt::file_data_in_at_offset";
## Signals a gap in a file to Zeek's file analysis. ## Signals a gap in a file to Zeek's file analysis.
## ##
## offset: position in file where gap starts ## offset: position in file where gap starts
## len: size of gap ## len: size of gap
## fid: Zeek-side ID of the file to operate on; if not given, the file started by the most recent file_begin() will be used ## fid: Zeek-side ID of the file to operate on; if not given, the file started by the most recent file_begin() will be used
public function file_gap(offset: uint64, len: uint64, fid: optional<string> = Null) : void &cxxname="zeek::spicy::rt::file_gap"; public function file_gap(offset: uint64, len: uint64, fid: optional<string> = Null): void &cxxname="zeek::spicy::rt::file_gap";
## Signals the end of a file to Zeek's file analysis. ## Signals the end of a file to Zeek's file analysis.
## ##
## fid: Zeek-side ID of the file to operate on; if not given, the file started by the most recent file_begin() will be used ## fid: Zeek-side ID of the file to operate on; if not given, the file started by the most recent file_begin() will be used
public function file_end(fid: optional<string> = Null) : void &cxxname="zeek::spicy::rt::file_end"; public function file_end(fid: optional<string> = Null): void &cxxname="zeek::spicy::rt::file_end";
## Inside a packet analyzer, forwards what data remains after parsing the top-level unit ## Inside a packet analyzer, forwards what data remains after parsing the top-level unit
## on to another analyzer. The index specifies the target, per the current dispatcher table. ## on to another analyzer. The index specifies the target, per the current dispatcher table.
public function forward_packet(identifier: uint32) : void &cxxname="zeek::spicy::rt::forward_packet"; public function forward_packet(identifier: uint32): void &cxxname="zeek::spicy::rt::forward_packet";
## Gets the network time from Zeek. ## Gets the network time from Zeek.
public function network_time() : time &cxxname="zeek::spicy::rt::network_time"; 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<ZeekVal> &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<ZeekVal> &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";

View file

@ -133,15 +133,15 @@ TypePtr rt::create_record_type(const std::string& ns, const std::string& id,
auto decls = std::make_unique<type_decl_list>(); auto decls = std::make_unique<type_decl_list>();
for ( const auto& f : fields ) { for ( const auto& f : fields ) {
auto attrs = make_intrusive<detail::Attributes>(nullptr, true, false); auto attrs = make_intrusive<::zeek::detail::Attributes>(nullptr, true, false);
if ( f.is_optional ) { if ( f.is_optional ) {
auto optional_ = make_intrusive<detail::Attr>(detail::ATTR_OPTIONAL); auto optional_ = make_intrusive<::zeek::detail::Attr>(::zeek::detail::ATTR_OPTIONAL);
attrs->AddAttr(std::move(optional_)); attrs->AddAttr(std::move(optional_));
} }
if ( f.is_log ) { if ( f.is_log ) {
auto log_ = make_intrusive<detail::Attr>(detail::ATTR_LOG); auto log_ = make_intrusive<::zeek::detail::Attr>(::zeek::detail::ATTR_LOG);
attrs->AddAttr(std::move(log_)); attrs->AddAttr(std::move(log_));
} }

View file

@ -19,7 +19,10 @@
#include <hilti/rt/extension-points.h> #include <hilti/rt/extension-points.h>
#include <hilti/rt/fmt.h> #include <hilti/rt/fmt.h>
#include <hilti/rt/types/all.h> #include <hilti/rt/types/all.h>
#include <hilti/rt/util.h>
#include "IntrusivePtr.h"
#include "Type.h"
#include "zeek/Desc.h" #include "zeek/Desc.h"
#include "zeek/Val.h" #include "zeek/Val.h"
#include "zeek/spicy/cookie.h" #include "zeek/spicy/cookie.h"
@ -59,15 +62,22 @@ public:
}; };
/** /**
* Exception thrown by event generation code if there's a type mismatch * Exception thrown if there's a type mismatch between Spicy and Zeek side.
* between the Spicy-side value and what the Zeek event expects.
*/ */
class TypeMismatch : public UsageError { class TypeMismatch : public UsageError {
using UsageError::UsageError;
};
/**
* Exception thrown by event generation code if there's a type mismatch between
* a Spicy-side parameter value and what the Zeek event expects.
*/
class ParameterMismatch : public TypeMismatch {
public: public:
TypeMismatch(const std::string_view& msg, std::string_view location = "") ParameterMismatch(std::string_view msg, std::string_view location = "")
: UsageError(hilti::rt::fmt("Event parameter mismatch, %s", msg)) {} : TypeMismatch(hilti::rt::fmt("Event parameter mismatch, %s", msg)) {}
TypeMismatch(const std::string_view& have, const TypePtr& want, std::string_view location = "") ParameterMismatch(std::string_view have, const TypePtr& want, std::string_view location = "")
: TypeMismatch(_fmt(have, want)) {} : ParameterMismatch(_fmt(have, want)) {}
private: private:
std::string _fmt(const std::string_view& have, const TypePtr& want) { std::string _fmt(const std::string_view& have, const TypePtr& want) {
@ -90,13 +100,13 @@ public:
* Begins registration of a Spicy EVT module. All subsequent, other `register_*()` * Begins registration of a Spicy EVT module. All subsequent, other `register_*()`
* function call will be associated with this module for documentation purposes. * 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 * Registers a Spicy protocol analyzer with its EVT meta information with the
* plugin's runtime. * 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 hilti::rt::Vector<::zeek::spicy::rt::PortRange>& ports,
const std::string& parser_orig, const std::string& parser_resp, const std::string& parser_orig, const std::string& parser_resp,
const std::string& replaces, const std::string& linker_scope); const std::string& replaces, const std::string& linker_scope);
@ -105,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 * Registers a Spicy file analyzer with its EVT meta information with the
* plugin's runtime. * plugin's runtime.
*/ */
void register_file_analyzer(const std::string& name, const hilti::rt::Vector<std::string>& mime_types, void register_file_analyzer(const std::string& id, const hilti::rt::Vector<std::string>& mime_types,
const std::string& parser, const std::string& replaces, const std::string& linker_scope); const std::string& parser, const std::string& replaces, const std::string& linker_scope);
/** Reports a Zeek-side "weird". */ /** Reports a Zeek-side "weird". */
@ -115,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 * Registers a Spicy packet analyzer with its EVT meta information with the
* plugin's runtime. * 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); const std::string& linker_scope);
/** Registers a Spicy-generated type to make it available inside Zeek. */ /** Registers a Spicy-generated type to make it available inside Zeek. */
@ -538,7 +548,7 @@ inline ValPtr to_val(const hilti::rt::DeferredExpression<T, E>& t, const TypePtr
*/ */
inline ValPtr to_val(const std::string& s, const TypePtr& target) { inline ValPtr to_val(const std::string& s, const TypePtr& target) {
if ( target->Tag() != TYPE_STRING ) if ( target->Tag() != TYPE_STRING )
throw TypeMismatch("string", target); throw ParameterMismatch("string", target);
return make_intrusive<StringVal>(s); return make_intrusive<StringVal>(s);
} }
@ -549,7 +559,7 @@ inline ValPtr to_val(const std::string& s, const TypePtr& target) {
*/ */
inline ValPtr to_val(const hilti::rt::Bytes& b, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Bytes& b, const TypePtr& target) {
if ( target->Tag() != TYPE_STRING ) if ( target->Tag() != TYPE_STRING )
throw TypeMismatch("string", target); throw ParameterMismatch("string", target);
return make_intrusive<StringVal>(b.str()); return make_intrusive<StringVal>(b.str());
} }
@ -568,7 +578,7 @@ inline ValPtr to_val(hilti::rt::integer::safe<T> i, const TypePtr& target) {
if ( target->Tag() == TYPE_INT ) if ( target->Tag() == TYPE_INT )
return val_mgr->Int(i); return val_mgr->Int(i);
throw TypeMismatch("uint64", target); throw ParameterMismatch("uint64", target);
} }
else { else {
if ( target->Tag() == TYPE_INT ) if ( target->Tag() == TYPE_INT )
@ -578,10 +588,10 @@ inline ValPtr to_val(hilti::rt::integer::safe<T> i, const TypePtr& target) {
if ( i >= 0 ) if ( i >= 0 )
return val_mgr->Count(i); return val_mgr->Count(i);
else else
throw TypeMismatch("negative int64", target); throw ParameterMismatch("negative int64", target);
} }
throw TypeMismatch("int64", target); throw ParameterMismatch("int64", target);
} }
} }
@ -599,7 +609,7 @@ ValPtr to_val(const hilti::rt::ValueReference<T>& t, const TypePtr& target) {
*/ */
inline ValPtr to_val(const hilti::rt::Bool& b, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Bool& b, const TypePtr& target) {
if ( target->Tag() != TYPE_BOOL ) if ( target->Tag() != TYPE_BOOL )
throw TypeMismatch("bool", target); throw ParameterMismatch("bool", target);
return val_mgr->Bool(b); return val_mgr->Bool(b);
} }
@ -610,7 +620,7 @@ inline ValPtr to_val(const hilti::rt::Bool& b, const TypePtr& target) {
*/ */
inline ValPtr to_val(double r, const TypePtr& target) { inline ValPtr to_val(double r, const TypePtr& target) {
if ( target->Tag() != TYPE_DOUBLE ) if ( target->Tag() != TYPE_DOUBLE )
throw TypeMismatch("double", target); throw ParameterMismatch("double", target);
return make_intrusive<DoubleVal>(r); return make_intrusive<DoubleVal>(r);
} }
@ -621,7 +631,7 @@ inline ValPtr to_val(double r, const TypePtr& target) {
*/ */
inline ValPtr to_val(const hilti::rt::Address& d, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Address& d, const TypePtr& target) {
if ( target->Tag() != TYPE_ADDR ) if ( target->Tag() != TYPE_ADDR )
throw TypeMismatch("addr", target); throw ParameterMismatch("addr", target);
auto in_addr = d.asInAddr(); auto in_addr = d.asInAddr();
if ( auto v4 = std::get_if<struct in_addr>(&in_addr) ) if ( auto v4 = std::get_if<struct in_addr>(&in_addr) )
@ -638,7 +648,7 @@ inline ValPtr to_val(const hilti::rt::Address& d, const TypePtr& target) {
*/ */
inline ValPtr to_val(const hilti::rt::Port& p, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Port& p, const TypePtr& target) {
if ( target->Tag() != TYPE_PORT ) if ( target->Tag() != TYPE_PORT )
throw TypeMismatch("port", target); throw ParameterMismatch("port", target);
switch ( p.protocol().value() ) { switch ( p.protocol().value() ) {
case hilti::rt::Protocol::TCP: return val_mgr->Port(p.port(), ::TransportProto::TRANSPORT_TCP); case hilti::rt::Protocol::TCP: return val_mgr->Port(p.port(), ::TransportProto::TRANSPORT_TCP);
@ -657,7 +667,7 @@ inline ValPtr to_val(const hilti::rt::Port& p, const TypePtr& target) {
*/ */
inline ValPtr to_val(const hilti::rt::Interval& i, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Interval& i, const TypePtr& target) {
if ( target->Tag() != TYPE_INTERVAL ) if ( target->Tag() != TYPE_INTERVAL )
throw TypeMismatch("interval", target); throw ParameterMismatch("interval", target);
return make_intrusive<IntervalVal>(i.seconds()); return make_intrusive<IntervalVal>(i.seconds());
} }
@ -668,7 +678,7 @@ inline ValPtr to_val(const hilti::rt::Interval& i, const TypePtr& target) {
*/ */
inline ValPtr to_val(const hilti::rt::Time& t, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Time& t, const TypePtr& target) {
if ( target->Tag() != TYPE_TIME ) if ( target->Tag() != TYPE_TIME )
throw TypeMismatch("time", target); throw ParameterMismatch("time", target);
return make_intrusive<TimeVal>(t.seconds()); return make_intrusive<TimeVal>(t.seconds());
} }
@ -680,7 +690,7 @@ inline ValPtr to_val(const hilti::rt::Time& t, const TypePtr& target) {
template<typename T> template<typename T>
inline ValPtr to_val(const hilti::rt::Vector<T>& v, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Vector<T>& v, const TypePtr& target) {
if ( target->Tag() != TYPE_VECTOR && target->Tag() != TYPE_LIST ) if ( target->Tag() != TYPE_VECTOR && target->Tag() != TYPE_LIST )
throw TypeMismatch("expected vector or list", target); throw ParameterMismatch("expected vector or list", target);
auto vt = cast_intrusive<VectorType>(target); auto vt = cast_intrusive<VectorType>(target);
auto zv = make_intrusive<VectorVal>(vt); auto zv = make_intrusive<VectorVal>(vt);
@ -697,17 +707,17 @@ inline ValPtr to_val(const hilti::rt::Vector<T>& v, const TypePtr& target) {
template<typename K, typename V> template<typename K, typename V>
inline ValPtr to_val(const hilti::rt::Map<K, V>& m, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Map<K, V>& m, const TypePtr& target) {
if constexpr ( hilti::rt::is_tuple<K>::value ) if constexpr ( hilti::rt::is_tuple<K>::value )
throw TypeMismatch("internal error: sets with tuples not yet supported in to_val()"); throw ParameterMismatch("internal error: sets with tuples not yet supported in to_val()");
if ( target->Tag() != TYPE_TABLE ) if ( target->Tag() != TYPE_TABLE )
throw TypeMismatch("map", target); throw ParameterMismatch("map", target);
auto tt = cast_intrusive<TableType>(target); auto tt = cast_intrusive<TableType>(target);
if ( tt->IsSet() ) if ( tt->IsSet() )
throw TypeMismatch("map", target); throw ParameterMismatch("map", target);
if ( tt->GetIndexTypes().size() != 1 ) if ( tt->GetIndexTypes().size() != 1 )
throw TypeMismatch("map with non-tuple elements", target); throw ParameterMismatch("map with non-tuple elements", target);
auto zv = make_intrusive<TableVal>(tt); auto zv = make_intrusive<TableVal>(tt);
@ -727,20 +737,20 @@ inline ValPtr to_val(const hilti::rt::Map<K, V>& m, const TypePtr& target) {
template<typename T> template<typename T>
inline ValPtr to_val(const hilti::rt::Set<T>& s, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Set<T>& s, const TypePtr& target) {
if ( target->Tag() != TYPE_TABLE ) if ( target->Tag() != TYPE_TABLE )
throw TypeMismatch("set", target); throw ParameterMismatch("set", target);
auto tt = cast_intrusive<TableType>(target); auto tt = cast_intrusive<TableType>(target);
if ( ! tt->IsSet() ) if ( ! tt->IsSet() )
throw TypeMismatch("set", target); throw ParameterMismatch("set", target);
auto zv = make_intrusive<TableVal>(tt); auto zv = make_intrusive<TableVal>(tt);
for ( const auto& i : s ) { for ( const auto& i : s ) {
if constexpr ( hilti::rt::is_tuple<T>::value ) if constexpr ( hilti::rt::is_tuple<T>::value )
throw TypeMismatch("internal error: sets with tuples not yet supported in to_val()"); throw ParameterMismatch("internal error: sets with tuples not yet supported in to_val()");
else { else {
if ( tt->GetIndexTypes().size() != 1 ) if ( tt->GetIndexTypes().size() != 1 )
throw TypeMismatch("set with non-tuple elements", target); throw ParameterMismatch("set with non-tuple elements", target);
auto idx = to_val(i, tt->GetIndexTypes()[0]); auto idx = to_val(i, tt->GetIndexTypes()[0]);
zv->Assign(std::move(idx), nullptr); zv->Assign(std::move(idx), nullptr);
@ -821,7 +831,7 @@ inline void set_record_field(RecordVal* rval, const IntrusivePtr<RecordType>& rt
// Field must be &optional or &default. // Field must be &optional or &default.
if ( auto attrs = rtype->FieldDecl(idx)->attrs; if ( auto attrs = rtype->FieldDecl(idx)->attrs;
! attrs || ! (attrs->Find(detail::ATTR_DEFAULT) || attrs->Find(detail::ATTR_OPTIONAL)) ) ! attrs || ! (attrs->Find(detail::ATTR_DEFAULT) || attrs->Find(detail::ATTR_OPTIONAL)) )
throw TypeMismatch(hilti::rt::fmt("missing initialization for field '%s'", rtype->FieldName(idx))); throw ParameterMismatch(hilti::rt::fmt("missing initialization for field '%s'", rtype->FieldName(idx)));
} }
} }
} }
@ -833,12 +843,12 @@ inline void set_record_field(RecordVal* rval, const IntrusivePtr<RecordType>& rt
template<typename T, typename std::enable_if_t<hilti::rt::is_tuple<T>::value>*> template<typename T, typename std::enable_if_t<hilti::rt::is_tuple<T>::value>*>
inline ValPtr to_val(const T& t, const TypePtr& target) { inline ValPtr to_val(const T& t, const TypePtr& target) {
if ( target->Tag() != TYPE_RECORD ) if ( target->Tag() != TYPE_RECORD )
throw TypeMismatch("tuple", target); throw ParameterMismatch("tuple", target);
auto rtype = cast_intrusive<RecordType>(target); auto rtype = cast_intrusive<RecordType>(target);
if ( std::tuple_size<T>::value != rtype->NumFields() ) if ( std::tuple_size<T>::value != rtype->NumFields() )
throw TypeMismatch("tuple", target); throw ParameterMismatch("tuple", target);
auto rval = make_intrusive<RecordVal>(rtype); auto rval = make_intrusive<RecordVal>(rtype);
size_t idx = 0; size_t idx = 0;
@ -856,12 +866,12 @@ inline ValPtr to_val(const hilti::rt::Bitfield<Ts...>& v, const TypePtr& target)
using Bitfield = hilti::rt::Bitfield<Ts...>; using Bitfield = hilti::rt::Bitfield<Ts...>;
if ( target->Tag() != TYPE_RECORD ) if ( target->Tag() != TYPE_RECORD )
throw TypeMismatch("bitfield", target); throw ParameterMismatch("bitfield", target);
auto rtype = cast_intrusive<RecordType>(target); auto rtype = cast_intrusive<RecordType>(target);
if ( sizeof...(Ts) - 1 != rtype->NumFields() ) if ( sizeof...(Ts) - 1 != rtype->NumFields() )
throw TypeMismatch("bitfield", target); throw ParameterMismatch("bitfield", target);
auto rval = make_intrusive<RecordVal>(rtype); auto rval = make_intrusive<RecordVal>(rtype);
size_t idx = 0; size_t idx = 0;
@ -887,7 +897,7 @@ constexpr bool is_optional = is_optional_impl<std::remove_cv_t<std::remove_refer
template<typename T, typename std::enable_if_t<std::is_base_of<::hilti::rt::trait::isStruct, T>::value>*> template<typename T, typename std::enable_if_t<std::is_base_of<::hilti::rt::trait::isStruct, T>::value>*>
inline ValPtr to_val(const T& t, const TypePtr& target) { inline ValPtr to_val(const T& t, const TypePtr& target) {
if ( target->Tag() != TYPE_RECORD ) if ( target->Tag() != TYPE_RECORD )
throw TypeMismatch("struct", target); throw ParameterMismatch("struct", target);
auto rtype = cast_intrusive<RecordType>(target); auto rtype = cast_intrusive<RecordType>(target);
@ -898,7 +908,7 @@ inline ValPtr to_val(const T& t, const TypePtr& target) {
t.__visit([&](std::string_view name, const auto& val) { t.__visit([&](std::string_view name, const auto& val) {
if ( idx >= num_fields ) if ( idx >= num_fields )
throw TypeMismatch(hilti::rt::fmt("no matching record field for field '%s'", name)); throw ParameterMismatch(hilti::rt::fmt("no matching record field for field '%s'", name));
// Special-case: Lift up anonymous bitfields (which always come as std::optionals). // Special-case: Lift up anonymous bitfields (which always come as std::optionals).
if ( name == "<anon>" ) { if ( name == "<anon>" ) {
@ -924,7 +934,7 @@ inline ValPtr to_val(const T& t, const TypePtr& target) {
std::string field_name = rtype->FieldName(idx); std::string field_name = rtype->FieldName(idx);
if ( field_name != name ) if ( field_name != name )
throw TypeMismatch( throw ParameterMismatch(
hilti::rt::fmt("mismatch in field name: expected '%s', found '%s'", name, field_name)); hilti::rt::fmt("mismatch in field name: expected '%s', found '%s'", name, field_name));
set_record_field(rval.get(), rtype, idx++, val); set_record_field(rval.get(), rtype, idx++, val);
@ -934,7 +944,7 @@ inline ValPtr to_val(const T& t, const TypePtr& target) {
// We already check above that all Spicy-side fields are mapped so we // We already check above that all Spicy-side fields are mapped so we
// can only hit this if there are uninitialized Zeek-side fields left. // can only hit this if there are uninitialized Zeek-side fields left.
if ( idx != num_fields ) if ( idx != num_fields )
throw TypeMismatch(hilti::rt::fmt("missing initialization for field '%s'", rtype->FieldName(idx + 1))); throw ParameterMismatch(hilti::rt::fmt("missing initialization for field '%s'", rtype->FieldName(idx + 1)));
return rval; return rval;
} }
@ -959,7 +969,7 @@ inline ValPtr to_val_for_transport_proto(int64_t val, const TypePtr& target) {
template<typename T, typename std::enable_if_t<std::is_enum<typename T::Value>::value>*> template<typename T, typename std::enable_if_t<std::is_enum<typename T::Value>::value>*>
inline ValPtr to_val(const T& t, const TypePtr& target) { inline ValPtr to_val(const T& t, const TypePtr& target) {
if ( target->Tag() != TYPE_ENUM ) if ( target->Tag() != TYPE_ENUM )
throw TypeMismatch("enum", target); throw ParameterMismatch("enum", target);
// We'll usually be getting an int64_t for T, but allow other signed ints // We'll usually be getting an int64_t for T, but allow other signed ints
// as well. // as well.
@ -969,7 +979,7 @@ inline ValPtr to_val(const T& t, const TypePtr& target) {
// Special case: map enum values to Zeek's semantics. // Special case: map enum values to Zeek's semantics.
if ( target->GetName() == "transport_proto" ) { if ( target->GetName() == "transport_proto" ) {
if ( ! std::is_same_v<T, hilti::rt::Protocol> ) if ( ! std::is_same_v<T, hilti::rt::Protocol> )
throw TypeMismatch(hilti::rt::demangle(typeid(t).name()), target); throw ParameterMismatch(hilti::rt::demangle(typeid(t).name()), target);
return to_val_for_transport_proto(it, target); return to_val_for_transport_proto(it, target);
} }
@ -984,4 +994,328 @@ inline ValPtr to_val(const T& t, const TypePtr& target) {
return target->AsEnumType()->GetEnumVal(bt); 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<uint64_t> 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 "<module>::<enum>", 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<int64_t> 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<uint16_t>(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<const char*>(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<uint64_t> 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<int64_t> 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<typename T>
::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<typename T>
::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<typename T>
::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<typename T>
::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<typename T>
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<typename T>
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<uint64_t>& 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<uint64_t>& index) {
return vector_index(get_vector(name), index);
}
/** Returns the size of a Zeek vector. Throws on errors. */
inline hilti::rt::integer::safe<uint64_t> 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<uint64_t> vector_size(const std::string& name) { return vector_size(get_vector(name)); }
} // namespace zeek::spicy::rt } // 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 "<Zeek value>"; }
inline std::string to_string(const zeek::spicy::rt::ValRecordPtr& v, detail::adl::tag /* unused */) {
return "<Zeek record>";
}
inline std::string to_string(const zeek::spicy::rt::ValTablePtr& v, detail::adl::tag /* unused */) {
return "<Zeek set/table>";
}
inline std::string to_string(const zeek::spicy::rt::ValVectorPtr& v, detail::adl::tag /* unused */) {
return "<Zeek vector>";
}
} // namespace hilti::rt::detail::adl

View file

@ -0,0 +1,6 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
<Zeek value>
<Zeek record>
<Zeek set/table>
<Zeek set/table>
<Zeek vector>

View file

@ -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