Merge remote-tracking branch 'origin/topic/bbannier/protocol-handle-close-finish'

* origin/topic/bbannier/protocol-handle-close-finish:
  [Spicy] Let `zeek::protocol_handle_close()` send a TCP EOF.
This commit is contained in:
Robin Sommer 2025-07-10 09:38:11 +02:00
commit ce6c7a6cd1
No known key found for this signature in database
GPG key ID: D8187293B3FFE5D0
3 changed files with 62 additions and 1 deletions

View file

@ -818,7 +818,14 @@ void rt::protocol_handle_close(const ProtocolHandle& handle) {
if ( child->IsFinished() || child->Removing() ) if ( child->IsFinished() || child->Removing() )
throw ValueUnavailable(hilti::rt::fmt("child analyzer %s no longer exist", handle)); throw ValueUnavailable(hilti::rt::fmt("child analyzer %s no longer exist", handle));
child->NextEndOfData(true); auto* tcp_child = dynamic_cast<analyzer::tcp::TCP_ApplicationAnalyzer*>(child);
if ( ! tcp_child )
throw ValueUnavailable(hilti::rt::fmt("child analyzer %s is not a TCP application analyzer", handle));
tcp_child->EndpointEOF(true); // For Spicy analyzers, this will trigger Finish() ...
child->NextEndOfData(true); // ... whereas this won't.
tcp_child->EndpointEOF(false);
child->NextEndOfData(false); child->NextEndOfData(false);
c->analyzer->RemoveChildAnalyzer(handle.id()); c->analyzer->RemoveChildAnalyzer(handle.id());

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
F, S, 1
T, S, 1

View file

@ -0,0 +1,51 @@
# @TEST-REQUIRES: have-spicy
#
# @TEST-EXEC: spicyz -d -o x.hlto x.spicy x.evt
# @TEST-EXEC: zeek -r ${TRACES}/ssh/single-conn.trace Zeek::Spicy x.hlto x.zeek >output 2>&1
# @TEST-EXEC: btest-diff output
#
# @TEST-DOC: Checks that a analyzer is properly finished when a protocol handle is closed.
# We use a child analyzer since this particular issue does not trigger for the root analyzer.
# @TEST-START-FILE x.spicy
module Foo;
import zeek;
public type X = unit {
data: bytes &size=2 {
local h = zeek::protocol_handle_get_or_create("Y");
zeek::protocol_data_in(zeek::is_orig(), $$, h);
zeek::protocol_handle_close(h);
}
};
public type Y = unit {
a: bytes &size=1;
b: bytes &eod;
};
# @TEST-END-FILE
# @TEST-START-FILE x.evt
import Foo;
protocol analyzer X over TCP:
parse with Foo::X;
protocol analyzer Y over TCP:
parse with Foo::Y;
export Foo::Y;
on Foo::Y -> event foo($is_orig, self);
# @TEST-END-FILE
# @TEST-START-FILE x.zeek
event zeek_init() {
Analyzer::register_for_port(Analyzer::ANALYZER_X, 22/tcp);
}
event foo(is_orig: bool, y: Foo::Y) {
print is_orig, y$a, |y$b|;
}
# @TEST-END-FILE