diff --git a/doc b/doc index 278f7b7d0c..7d39602b09 160000 --- a/doc +++ b/doc @@ -1 +1 @@ -Subproject commit 278f7b7d0cda1aab5789122d808f411cd05a20fc +Subproject commit 7d39602b09f16e1687408b63af3fd6a3edb33d07 diff --git a/scripts/spicy/zeek.spicy b/scripts/spicy/zeek.spicy index 4f4467008d..0afde17e8b 100644 --- a/scripts/spicy/zeek.spicy +++ b/scripts/spicy/zeek.spicy @@ -118,6 +118,10 @@ public function fuid() : string &cxxname="zeek::spicy::rt::fuid"; ## called from inside a protocol analyzer. public function terminate_session() : void &cxxname="zeek::spicy::rt::terminate_session"; +## Tells Zeek to skip sending any further input data to the current analyzer. +## This is supported for protocol and file analyzers. +public function skip_input() : void &cxxname="zeek::spicy::rt::skip_input"; + ## Signals the expected size of a file to Zeek's file analysis. ## ## size: expected size of file diff --git a/src/spicy/runtime-support.cc b/src/spicy/runtime-support.cc index 497abb91ea..c59465aa4b 100644 --- a/src/spicy/runtime-support.cc +++ b/src/spicy/runtime-support.cc @@ -748,6 +748,19 @@ void rt::terminate_session() { throw spicy::rt::ValueUnavailable("terminate_session() not available in the current context"); } +void rt::skip_input() { + auto _ = hilti::rt::profiler::start("zeek/rt/skip_input"); + auto cookie = static_cast(hilti::rt::context::cookie()); + assert(cookie); + + if ( auto p = cookie->protocol ) + p->analyzer->SetSkip(true); + else if ( auto f = cookie->file ) + f->analyzer->SetSkip(true); + else + throw spicy::rt::ValueUnavailable("skip() not available in the current context"); +} + std::string rt::fuid() { auto _ = hilti::rt::profiler::start("zeek/rt/fuid"); auto cookie = static_cast(hilti::rt::context::cookie()); diff --git a/src/spicy/runtime-support.h b/src/spicy/runtime-support.h index c9291b7460..0ba1962d0f 100644 --- a/src/spicy/runtime-support.h +++ b/src/spicy/runtime-support.h @@ -381,7 +381,7 @@ void protocol_handle_close(const ProtocolHandle& handle); * Signals the beginning of a file to Zeek's file analysis, associating it * with the current connection. * - * param mime_type optional mime type passed to Zeek + * @param mime_type optional mime type passed to Zeek * @returns Zeek-side file ID of the new file */ std::string file_begin(const std::optional& mime_type); @@ -397,6 +397,12 @@ std::string fuid(); */ void terminate_session(); +/** + * Tells Zeek to skip sending any further input data to the current protocol + * or file analyzer. + */ +void skip_input(); + /** * Signals the expected size of a file to Zeek's file analysis. * diff --git a/testing/btest/Baseline/spicy.skip-input-file/output b/testing/btest/Baseline/spicy.skip-input-file/output new file mode 100644 index 0000000000..13b1db7813 --- /dev/null +++ b/testing/btest/Baseline/spicy.skip-input-file/output @@ -0,0 +1,4 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +1, 12 +2, 34 +3, 56 diff --git a/testing/btest/Baseline/spicy.skip-input-protocol/output b/testing/btest/Baseline/spicy.skip-input-protocol/output new file mode 100644 index 0000000000..2a759f6957 --- /dev/null +++ b/testing/btest/Baseline/spicy.skip-input-protocol/output @@ -0,0 +1,7 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +1, True, 28 +event +2, False, 256 +event +3, True, 28 +event diff --git a/testing/btest/spicy/skip-input-file.zeek b/testing/btest/spicy/skip-input-file.zeek new file mode 100644 index 0000000000..af30a2ff39 --- /dev/null +++ b/testing/btest/spicy/skip-input-file.zeek @@ -0,0 +1,60 @@ +# @TEST-REQUIRES: have-spicy +# +# @TEST-EXEC: spicyz -d -o test.hlto ssh.spicy ./ssh-cond.evt +# @TEST-EXEC: zeek -r ${TRACES}/ssh/single-conn.trace test.hlto %INPUT Spicy::enable_print=T >output +# @TEST-EXEC: btest-diff output +# +# @TEST-DOC: Validate that `skip_input` works for file analyzers. + +# @TEST-START-FILE ssh.spicy +module SSH; + +import spicy; +import zeek; + +public type Banner = unit { + magic : /SSH-/; + version : /[^-]*/; + dash : /-/; + software: /[^\r\n]*/; +}; + +type Context = tuple; + +public type Data = unit { + %context = Context; + + : (bytes &size=2)[] foreach { + self.context().counter = self.context().counter + 1; + + print self.context().counter, $$; + + if ( self.context().counter == 3 ) + zeek::skip_input(); + } +}; + +on Banner::%done { + local fid1 = zeek::file_begin("foo/bar"); + zeek::file_data_in(b"12", fid1); + zeek::file_data_in(b"34", fid1); + zeek::file_data_in(b"56", fid1); + zeek::file_data_in(b"78", fid1); + zeek::file_data_in(b"90", fid1); + zeek::file_end(fid1); +} +# @TEST-END-FILE + +# @TEST-START-FILE ssh-cond.evt + +import zeek; + +protocol analyzer spicy::SSH over TCP: + parse originator with SSH::Banner, + port 22/tcp, + replaces SSH; + +file analyzer spicy::Text: + parse with SSH::Data, + mime-type foo/bar; +# @TEST-END-FILE diff --git a/testing/btest/spicy/skip-input-protocol.zeek b/testing/btest/spicy/skip-input-protocol.zeek new file mode 100644 index 0000000000..5acc9d299a --- /dev/null +++ b/testing/btest/spicy/skip-input-protocol.zeek @@ -0,0 +1,44 @@ +# @TEST-REQUIRES: have-spicy +# +# @TEST-EXEC: spicyz -d -o test.hlto test.spicy test.evt +# @TEST-EXEC: zeek -b -r ${TRACES}/dns/long-connection.pcap Zeek::Spicy test.hlto %INPUT "Spicy::enable_print = T;" >output +# @TEST-EXEC: btest-diff output +# +# @TEST-DOC: Validate that `skip_input` works for protocol analyzers. + +redef likely_server_ports += { 53/udp }; # avoid flipping direction after termination +redef udp_inactivity_timeout = 24hrs; # avoid long gaps to trigger removal + +event Test::foo() { print "event"; } + +# @TEST-START-FILE test.spicy +module Test; + +import zeek; + +type Counter = tuple; + +public type Foo = unit { + %context = Counter; + + data: bytes &eod; + + on %done { + self.context().counter = self.context().counter + 1; + + print self.context().counter, zeek::is_orig(), |self.data|; + + if ( self.context().counter == 3 ) + zeek::skip_input(); + } +}; + +# @TEST-END-FILE + +# @TEST-START-FILE test.evt +protocol analyzer spicy::Test over UDP: + port 53/udp, + parse with Test::Foo; + +on Test::Foo -> event Test::foo(); +# @TEST-END-FILE