diff --git a/scripts/spicy/zeek.spicy b/scripts/spicy/zeek.spicy index 5f93b74480..a6f3b1f344 100644 --- a/scripts/spicy/zeek.spicy +++ b/scripts/spicy/zeek.spicy @@ -2,6 +2,8 @@ module zeek; +import spicy; + # Note: Retain the formatting here, doc/scripts/autogen-spicy-lib is picking up on that. %cxx-include = "zeek/spicy/runtime-support.h"; @@ -48,46 +50,82 @@ public type ProtocolHandle = __library_type("zeek::spicy::rt::ProtocolHandle"); ## Adds a Zeek-side child protocol analyzer to the current connection. ## -## If the same analyzer was added previously with protocol_handle_get_or_create or -## protocol_begin with same argument, and not closed with protocol_handle_close -## or protocol_end, no new analyzer will be added. +## If the same analyzer was added previously with `protocol_handle_get_or_create` or +## `protocol_begin` with same argument, and not closed with `protocol_handle_close` +## or `protocol_end`, no new analyzer will be added. ## ## See `protocol_handle_get_or_create` for the error semantics of this function. ## ## analyzer: type of analyzer to instantiate, specified through its Zeek-side -## name (similar to what Zeek's signature action `enable` takes); if not -## specified, Zeek will perform its usual dynamic protocol detection to figure -## out how to parse the data (the latter will work only for TCP protocols, though.) -public function protocol_begin(analyzer: optional = Null) : void &cxxname="zeek::spicy::rt::protocol_begin"; +## name (similar to what Zeek's signature action `enable` takes) +## +## protocol: the transport-layer protocol that the analyzer uses; only TCP is +## currently supported here +## +## 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 +## `protocol_begin` for that instead. +public function protocol_begin(analyzer: optional, 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 +## on subsequently provided data. +## +## If the same DPD analyzer was added previously with `protocol_handle_get_or_create` or +## `protocol_begin` with same argument, and not closed with `protocol_handle_close` +## or `protocol_end`, no new analyzer will be added. +## +## See `protocol_handle_get_or_create` for the error semantics of this function. +## +## protocol: the transport-layer protocol on which to perform protocol detection; +## only TCP is currently supported here +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. ## -## If no such child exists it will be added; otherwise a handle to the +## If no such child exists yet it will be added; otherwise a handle to the ## existing child protocol analyzer will be returned. ## -## This function will return an error +## This function will return an error if: ## -## - if not called from a protocol analyzer, or -## - the requested child protocol analyzer is unknown, or +## - not called from a protocol analyzer, or +## - the requested child protocol analyzer is of unknown type or not support by the requested transport protocol, or ## - creation of a child analyzer of the requested type was prevented by a ## previous call of `disable_analyzer` with `prevent=T` ## -## analyzer: type of analyzer to instantiate, specified through its Zeek-side +## analyzer: type of analyzer to get or instantiate, specified through its Zeek-side ## name (similar to what Zeek's signature action `enable` takes). -public function protocol_handle_get_or_create(analyzer: string) : ProtocolHandle &cxxname="zeek::spicy::rt::protocol_handle_get_or_create"; +## +## protocol: the transport-layer protocol that the analyser uses; only TCP is +## 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"; -## Forwards protocol data to all previously instantiated Zeek-side child protocol analyzers. +## Forwards protocol data to all previously instantiated Zeek-side child protocol analyzers of a given transport-layer. ## ## is_orig: true to feed the data to the child's originator side, false for the responder +## ## data: chunk of data to forward to child analyzer -## h: optional handle to the child analyzer to forward data into, else forward to all child analyzers -public function protocol_data_in(is_orig: bool, data: bytes, h: optional = Null) : void &cxxname="zeek::spicy::rt::protocol_data_in"; +## +## 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"; + +## Forwards protocol data to a specific previously instantiated Zeek-side child analyzer. +## +## is_orig: true to feed the data to the child's originator side, false for the responder +## +## data: chunk of data to forward to child analyzer +## +## 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"; ## Signals a gap in input data to all previously instantiated Zeek-side child protocol analyzers. ## ## is_orig: true to signal gap to the child's originator side, false for the responder +## ## offset: start offset of gap in input stream +## ## len: size of gap +## ## 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 = Null) : void &cxxname="zeek::spicy::rt::protocol_gap"; diff --git a/src/spicy/runtime-support.cc b/src/spicy/runtime-support.cc index e6b340e0be..a95d3a9947 100644 --- a/src/spicy/runtime-support.cc +++ b/src/spicy/runtime-support.cc @@ -485,15 +485,16 @@ void rt::weird(const std::string& id, const std::string& addl) { throw ValueUnavailable("none of $conn, $file, or $packet available for weird reporting"); } -void rt::protocol_begin(const std::optional& analyzer) { +void rt::protocol_begin(const std::optional& analyzer, const ::hilti::rt::Protocol& proto) { auto _ = hilti::rt::profiler::start("zeek/rt/protocol_begin"); if ( analyzer ) { - protocol_handle_get_or_create(*analyzer); + protocol_handle_get_or_create(*analyzer, proto); return; } // Instantiate a DPD analyzer. + auto cookie = static_cast(hilti::rt::context::cookie()); assert(cookie); @@ -501,36 +502,46 @@ void rt::protocol_begin(const std::optional& analyzer) { if ( ! c ) throw ValueUnavailable("no current connection available"); - // Use a Zeek PIA stream analyzer performing DPD. - auto pia_tcp = std::make_unique(c->analyzer->Conn()); + switch ( proto.value() ) { + case ::hilti::rt::Protocol::TCP: { + auto pia_tcp = std::make_unique(c->analyzer->Conn()); + pia_tcp->FirstPacket(true, nullptr); + pia_tcp->FirstPacket(false, nullptr); - // Forward empty payload to trigger lifecycle management in this analyzer tree. - c->analyzer->ForwardStream(0, reinterpret_cast(c->analyzer), true); - c->analyzer->ForwardStream(0, reinterpret_cast(c->analyzer), false); + // Forward empty payload to trigger lifecycle management in this analyzer tree. + c->analyzer->ForwardStream(0, reinterpret_cast(c->analyzer), true); + c->analyzer->ForwardStream(0, reinterpret_cast(c->analyzer), false); - // Direct child of this type already exists. We ignore this silently - // because that makes usage nicer if either side of the connection - // might end up creating the analyzer; this way the user doesn't - // need to track what the other side already did. - // - // We inspect the children directly to work around zeek/zeek#2899. - const auto& children = c->analyzer->GetChildren(); - if ( auto it = std::find_if(children.begin(), children.end(), - [&](const auto& it) { - return ! it->Removing() && ! it->IsFinished() && - it->GetAnalyzerTag() == pia_tcp->GetAnalyzerTag(); - }); - it != children.end() ) - return; + // If the child already exists, do not add it again so this function is idempotent. + // + // We inspect the children directly to work around zeek/zeek#2899. + const auto& children = c->analyzer->GetChildren(); + if ( auto it = std::find_if(children.begin(), children.end(), + [&](const auto& it) { + return ! it->Removing() && ! it->IsFinished() && + it->GetAnalyzerName() == pia_tcp->GetAnalyzerTag(); + }); + it != children.end() ) + return; - auto child = pia_tcp.release(); - c->analyzer->AddChildAnalyzer(child); + auto child = pia_tcp.release(); + c->analyzer->AddChildAnalyzer(child); + break; + } - child->FirstPacket(true, nullptr); - child->FirstPacket(false, nullptr); + case ::hilti::rt::Protocol::UDP: throw Unsupported("protocol_begin: UDPnot supported for DPD"); + + case ::hilti::rt::Protocol::ICMP: throw Unsupported("protocol_begin: ICMP not supported for DPD"); + + case ::hilti::rt::Protocol::Undef: throw InvalidValue("protocol_begin: no protocol specified for DPD"); + + default: throw InvalidValue("protocol_begin: unknown protocol for DPD"); + } } -rt::ProtocolHandle rt::protocol_handle_get_or_create(const std::string& analyzer) { +void rt::protocol_begin(const ::hilti::rt::Protocol& proto) { return protocol_begin(std::nullopt, proto); } + +rt::ProtocolHandle rt::protocol_handle_get_or_create(const std::string& analyzer, const ::hilti::rt::Protocol& proto) { auto _ = hilti::rt::profiler::start("zeek/rt/protocol_handle_get_or_create"); auto cookie = static_cast(hilti::rt::context::cookie()); assert(cookie); @@ -539,56 +550,73 @@ rt::ProtocolHandle rt::protocol_handle_get_or_create(const std::string& analyzer if ( ! c ) throw ValueUnavailable("no current connection available"); - // Forward empty payload to trigger lifecycle management in this analyzer tree. - c->analyzer->ForwardStream(0, reinterpret_cast(c->analyzer), true); - c->analyzer->ForwardStream(0, reinterpret_cast(c->analyzer), false); + switch ( proto.value() ) { + case ::hilti::rt::Protocol::TCP: { + // Forward empty payload to trigger lifecycle management in this analyzer tree. + c->analyzer->ForwardStream(0, reinterpret_cast(c->analyzer), true); + c->analyzer->ForwardStream(0, reinterpret_cast(c->analyzer), false); - // If the child already exists, do not add it again so this function is idempotent. - // - // We inspect the children directly to work around zeek/zeek#2899. - const auto& children = c->analyzer->GetChildren(); - if ( auto it = std::find_if(children.begin(), children.end(), - [&](const auto& it) { - return ! it->Removing() && ! it->IsFinished() && it->GetAnalyzerName() == analyzer; - }); - it != children.end() ) - return rt::ProtocolHandle((*it)->GetID()); + // If the child already exists, do not add it again so this function is idempotent. + // + // We inspect the children directly to work around zeek/zeek#2899. + const auto& children = c->analyzer->GetChildren(); + if ( auto it = std::find_if(children.begin(), children.end(), + [&](const auto& it) { + return ! it->Removing() && ! it->IsFinished() && + it->GetAnalyzerName() == analyzer; + }); + it != children.end() ) + return rt::ProtocolHandle((*it)->GetID(), proto); - auto child = analyzer_mgr->InstantiateAnalyzer(analyzer.c_str(), c->analyzer->Conn()); - if ( ! child ) - throw ZeekError(::hilti::rt::fmt("unknown analyzer '%s' requested", analyzer)); + auto child = analyzer_mgr->InstantiateAnalyzer(analyzer.c_str(), c->analyzer->Conn()); + if ( ! child ) + throw ZeekError(::hilti::rt::fmt("unknown analyzer '%s' requested", analyzer)); - // If we had no such child before but cannot add it the analyzer was prevented. - // - // NOTE: We make this a hard error since returning e.g., an empty optional - // here would make it easy to incorrectly use the return value with e.g., - // `protocol_data_in` or `protocol_gap`. - if ( ! c->analyzer->AddChildAnalyzer(child) ) - throw ZeekError(::hilti::rt::fmt("creation of child analyzer %s was prevented", analyzer)); + // If we had no such child before but cannot add it the analyzer was prevented. + // + // NOTE: We make this a hard error since returning e.g., an empty optional + // here would make it easy to incorrectly use the return value with e.g., + // `protocol_data_in` or `protocol_gap`. + if ( ! c->analyzer->AddChildAnalyzer(child) ) + throw ZeekError(::hilti::rt::fmt("creation of child analyzer %s was prevented", analyzer)); - if ( c->analyzer->Conn()->ConnTransport() != TRANSPORT_TCP ) { - // Some TCP application analyzer may expect to have access to a TCP - // analyzer. To make that work, we'll create a fake TCP analyzer, - // just so that they have something to access. It won't - // semantically have any "TCP" to analyze obviously. - c->fake_tcp = std::make_shared(c->analyzer->Conn()); - static_cast(c->fake_tcp.get()) - ->Done(); // will never see packets; cast to get around protected inheritance + if ( c->analyzer->Conn()->ConnTransport() != TRANSPORT_TCP ) { + // Some TCP application analyzer may expect to have access to a TCP + // analyzer. To make that work, we'll create a fake TCP analyzer, + // just so that they have something to access. It won't + // semantically have any "TCP" to analyze obviously. + c->fake_tcp = std::make_shared(c->analyzer->Conn()); + static_cast(c->fake_tcp.get()) + ->Done(); // will never see packets; cast to get around protected inheritance + } + + auto* child_as_tcp = dynamic_cast(child); + if ( ! child_as_tcp ) + throw ZeekError( + ::hilti::rt::fmt("could not add analyzer '%s' to connection; not a TCP-based analyzer", analyzer)); + + if ( c->fake_tcp ) + child_as_tcp->SetTCP(c->fake_tcp.get()); + + return rt::ProtocolHandle(child->GetID(), proto); + } + + case ::hilti::rt::Protocol::UDP: { + throw Unsupported("protocol_handle_get_or_create: UDP not supported"); + } + + case ::hilti::rt::Protocol::ICMP: throw Unsupported("protocol_handle_get_or_create: ICMP not supported"); + + case ::hilti::rt::Protocol::Undef: throw InvalidValue("protocol_handle_get_or_create: no protocol specified"); + + default: throw InvalidValue("protocol_handle_get_or_create: unknown protocol"); } - - auto* child_as_tcp = dynamic_cast(child); - if ( ! child_as_tcp ) - throw ZeekError( - ::hilti::rt::fmt("could not add analyzer '%s' to connection; not a TCP-based analyzer", analyzer)); - - if ( c->fake_tcp ) - child_as_tcp->SetTCP(c->fake_tcp.get()); - - return rt::ProtocolHandle(child->GetID()); } -void rt::protocol_data_in(const hilti::rt::Bool& is_orig, const hilti::rt::Bytes& data, - const std::optional& h) { +namespace zeek::spicy::rt { +static void protocol_data_in(const hilti::rt::Bool& is_orig, const hilti::rt::Bytes& data, + const std::optional<::hilti::rt::Protocol>& proto, + const std::optional& h) { auto _ = hilti::rt::profiler::start("zeek/rt/protocol_data_in"); auto cookie = static_cast(hilti::rt::context::cookie()); assert(cookie); @@ -597,25 +625,65 @@ void rt::protocol_data_in(const hilti::rt::Bool& is_orig, const hilti::rt::Bytes if ( ! c ) throw ValueUnavailable("no current connection available"); - auto len = data.size(); - auto* data_ = reinterpret_cast(data.data()); + ::hilti::rt::Protocol protocol_to_use = ::hilti::rt::Protocol::Undef; - if ( h ) { - if ( auto* output_handler = c->analyzer->GetOutputHandler() ) - output_handler->DeliverStream(len, data_, is_orig); + if ( proto ) { + if ( h && h->protocol() != *proto ) + throw InvalidValue("protocol_data_in: protocol mismatches with analyzer handle"); - auto* child = c->analyzer->FindChild(h->id()); - if ( ! child ) - throw ValueUnavailable(hilti::rt::fmt("unknown child analyzer %s", *h)); - - if ( child->IsFinished() || child->Removing() ) - throw ValueUnavailable(hilti::rt::fmt("child analyzer %s no longer exist", *h)); - - child->NextStream(len, data_, is_orig); + protocol_to_use = *proto; } + else if ( h ) + protocol_to_use = h->protocol(); - else - c->analyzer->ForwardStream(len, data_, is_orig); + if ( protocol_to_use == ::hilti::rt::Protocol::Undef ) + throw InvalidValue("protocol_data_in: cannot determine protocol to use"); + + switch ( protocol_to_use.value() ) { + case ::hilti::rt::Protocol::TCP: { + auto len = data.size(); + auto* data_ = reinterpret_cast(data.data()); + + if ( h ) { + if ( auto* output_handler = c->analyzer->GetOutputHandler() ) + output_handler->DeliverStream(len, data_, is_orig); + + auto* child = c->analyzer->FindChild(h->id()); + if ( ! child ) + throw ValueUnavailable(hilti::rt::fmt("unknown child analyzer %s", *h)); + + if ( child->IsFinished() || child->Removing() ) + throw ValueUnavailable(hilti::rt::fmt("child analyzer %s no longer exist", *h)); + + child->NextStream(len, data_, is_orig); + } + + else + c->analyzer->ForwardStream(len, data_, is_orig); + + break; + } + + case ::hilti::rt::Protocol::UDP: { + throw Unsupported("protocol_data_in: UDP not supported"); + } + + case ::hilti::rt::Protocol::ICMP: throw Unsupported("protocol_data_in: ICMP not supported"); + + case ::hilti::rt::Protocol::Undef: hilti::rt::cannot_be_reached(); + + default: throw InvalidValue("protocol_data_in: unknown protocol"); + } +} +} // namespace zeek::spicy::rt + +void rt::protocol_data_in(const hilti::rt::Bool& is_orig, const hilti::rt::Bytes& data, + const ::hilti::rt::Protocol& proto) { + protocol_data_in(is_orig, data, proto, {}); +} + +void rt::protocol_data_in(const hilti::rt::Bool& is_orig, const hilti::rt::Bytes& data, const rt::ProtocolHandle& h) { + protocol_data_in(is_orig, data, {}, h); } void rt::protocol_gap(const hilti::rt::Bool& is_orig, const hilti::rt::integer::safe& offset, @@ -628,22 +696,38 @@ void rt::protocol_gap(const hilti::rt::Bool& is_orig, const hilti::rt::integer:: if ( ! c ) throw ValueUnavailable("no current connection available"); - if ( h ) { - if ( auto* output_handler = c->analyzer->GetOutputHandler() ) - output_handler->Undelivered(offset, len, is_orig); + switch ( h->protocol().value() ) { + case ::hilti::rt::Protocol::TCP: { + if ( h ) { + if ( auto* output_handler = c->analyzer->GetOutputHandler() ) + output_handler->Undelivered(offset, len, is_orig); - auto* child = c->analyzer->FindChild(h->id()); - if ( ! child ) - throw ValueUnavailable(hilti::rt::fmt("unknown child analyzer %s", *h)); + auto* child = c->analyzer->FindChild(h->id()); + if ( ! child ) + throw ValueUnavailable(hilti::rt::fmt("unknown child analyzer %s", *h)); - if ( child->IsFinished() || child->Removing() ) - throw ValueUnavailable(hilti::rt::fmt("child analyzer %s no longer exist", *h)); + if ( child->IsFinished() || child->Removing() ) + throw ValueUnavailable(hilti::rt::fmt("child analyzer %s no longer exist", *h)); - child->NextUndelivered(offset, len, is_orig); + child->NextUndelivered(offset, len, is_orig); + } + + else + c->analyzer->ForwardUndelivered(offset, len, is_orig); + + break; + } + + case ::hilti::rt::Protocol::UDP: { + throw Unsupported("protocol_gap: UDP not supported"); + } + + case ::hilti::rt::Protocol::ICMP: throw Unsupported("protocol_gap: ICMP not supported"); + + case ::hilti::rt::Protocol::Undef: throw InvalidValue("protocol_gap: no protocol specified"); + + default: throw InvalidValue("protocol_gap: unknown protocol"); } - - else - c->analyzer->ForwardUndelivered(offset, len, is_orig); } void rt::protocol_end() { @@ -668,17 +752,32 @@ void rt::protocol_handle_close(const ProtocolHandle& handle) { if ( ! c ) throw ValueUnavailable("no current connection available"); - auto child = c->analyzer->FindChild(handle.id()); - if ( ! child ) - throw ValueUnavailable(hilti::rt::fmt("unknown child analyzer %s", handle)); + switch ( handle.protocol().value() ) { + case ::hilti::rt::Protocol::TCP: { + auto child = c->analyzer->FindChild(handle.id()); + if ( ! child ) + throw ValueUnavailable(hilti::rt::fmt("unknown child analyzer %s", handle)); - if ( child->IsFinished() || child->Removing() ) - throw ValueUnavailable(hilti::rt::fmt("child analyzer %s no longer exist", handle)); + if ( child->IsFinished() || child->Removing() ) + throw ValueUnavailable(hilti::rt::fmt("child analyzer %s no longer exist", handle)); - child->NextEndOfData(true); - child->NextEndOfData(false); + child->NextEndOfData(true); + child->NextEndOfData(false); - c->analyzer->RemoveChildAnalyzer(handle.id()); + c->analyzer->RemoveChildAnalyzer(handle.id()); + break; + } + + case ::hilti::rt::Protocol::UDP: { + throw Unsupported("protocol_handle_close: UDP not supported"); + } + + case ::hilti::rt::Protocol::ICMP: throw Unsupported("protocol_handle_close: ICMP not supported"); + + case ::hilti::rt::Protocol::Undef: throw InvalidValue("protocol_handle_close: no protocol specified"); + + default: throw InvalidValue("protocol_handle_close: unknown protocol"); + } } rt::cookie::FileState* rt::cookie::FileStateStack::push(std::optional fid_provided) { diff --git a/src/spicy/runtime-support.h b/src/spicy/runtime-support.h index 95d08f219f..9b2a5d448e 100644 --- a/src/spicy/runtime-support.h +++ b/src/spicy/runtime-support.h @@ -294,7 +294,7 @@ void reject_protocol(const std::string& reason = "protocol rejected"); class ProtocolHandle { public: ProtocolHandle() {} - explicit ProtocolHandle(uint64_t id) : _id(id) {} + explicit ProtocolHandle(uint64_t id, ::hilti::rt::Protocol proto) : _id(id), _proto(proto) {} uint64_t id() const { if ( ! _id ) @@ -303,6 +303,8 @@ public: return *_id; } + const auto& protocol() const { return _proto; } + friend std::string to_string(const ProtocolHandle& h, ::hilti::rt::detail::adl::tag) { if ( ! h._id ) return "(uninitialized protocol handle)"; @@ -316,38 +318,56 @@ public: private: std::optional _id; + ::hilti::rt::Protocol _proto = ::hilti::rt::Protocol::Undef; }; /** * Adds a Zeek-side child protocol analyzer to the current connection. * - * @param analyzer if given, the Zeek-side name of the analyzer to instantiate; - * if not given, DPD will be used + * @param analyzer the Zeek-side name of the analyzer to instantiate; can be left unset to add a DPD analyzer */ -void protocol_begin(const std::optional& analyzer); +void protocol_begin(const std::optional& analyzer, const ::hilti::rt::Protocol& proto); + +/** + * Adds a Zeek-side DPD child analyzer to the current connection. + * + * @param proto the transport-layer protocol of the desired DPD analyzer; must be TCP or UDP + */ +void protocol_begin(const ::hilti::rt::Protocol& proto); /** * Gets a handle to a child analyzer of a given type. If a child of that type * does not yet exist it will be created. * * @param analyzer the Zeek-side name of the analyzer to get (e.g., `HTTP`) + * @param proto the transport-layer protocol of the analyzer, which must match + * the type of the child analyzer that *analyzer* refers to * * @return a handle to the child analyzer. When done, the handle should be * closed, either explicitly with protocol_handle_close or implicitly with * protocol_end. */ -ProtocolHandle protocol_handle_get_or_create(const std::string& analyzer); +rt::ProtocolHandle protocol_handle_get_or_create(const std::string& analyzer, const ::hilti::rt::Protocol& proto); /** * Forwards data to all previously instantiated Zeek-side child protocol - * analyzers. + * analyzers of a given transport-layer protocol. * * @param is_orig true to feed data to originator side, false for responder * @param data next chunk of stream data for child analyzer to process - * @param h optional handle to the child analyzer to stream data into + * @param h optional handle to pass data to a specific child analyzer only */ -void protocol_data_in(const hilti::rt::Bool& is_orig, const hilti::rt::Bytes& data, - const std::optional& h = {}); +void protocol_data_in(const hilti::rt::Bool& is_orig, const hilti::rt::Bytes& data, const ::hilti::rt::Protocol& proto); + +/** + * Forwards data to a specific previously instantiated Zeek-side child protocol + * analyzer. + * + * @param is_orig true to feed data to originator side, false for responder + * @param data next chunk of stream data for child analyzer to process + * @param h handle identifying the specific child analyzer only + */ +void protocol_data_in(const hilti::rt::Bool& is_orig, const hilti::rt::Bytes& data, const ProtocolHandle& h); /** * Signals a gap in input data to all previously instantiated Zeek-side child