mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Merge remote-tracking branch 'origin/topic/robin/gh-3561-forward-to-udp'
* origin/topic/robin/gh-3561-forward-to-udp: Update docs. Add explicit children life-cycle management method to analyzers. Spicy: Support UDP in Spicy's `protocol_*` runtime functions. Add method to analyzer to retrieve direct child by name. Extend PIA's `FirstPacket` API. Spicy: Prepare for supporting forwarding to protocols other than TCP.
This commit is contained in:
commit
82be6425e6
18 changed files with 678 additions and 190 deletions
54
CHANGES
54
CHANGES
|
@ -1,3 +1,57 @@
|
||||||
|
7.0.0-dev.244 | 2024-05-10 11:08:47 +0200
|
||||||
|
|
||||||
|
* GH-3725: Spicy: Fix service reporting for replaced analyzers. (Robin Sommer, Corelight)
|
||||||
|
|
||||||
|
We accidentally applied analyzer mappings when looking up an
|
||||||
|
analyzer's name from scriptland.
|
||||||
|
|
||||||
|
* GH-3561: Spicy: Support UDP in Spicy's `protocol_*` runtime functions. (Robin Sommer, Corelight)
|
||||||
|
|
||||||
|
This extends the ability to feed new payload back into Zeek's analyzer
|
||||||
|
pipeline from TCP to now also UDP.
|
||||||
|
|
||||||
|
Note: We don't extend this further to ICMP because the ICMP analyzer
|
||||||
|
cannot be dynamically instantiated (Zeek aborts when trying so). As
|
||||||
|
ICMP isn't very interesting from use case perspective anyways, that
|
||||||
|
seems fine.
|
||||||
|
|
||||||
|
* Add method to analyzer to retrieve direct child by name. (Robin Sommer, Corelight)
|
||||||
|
|
||||||
|
* Add explicit children life-cycle management method to analyzers. (Robin Sommer, Corelight)
|
||||||
|
|
||||||
|
* Extend PIA's `FirstPacket` API. (Robin Sommer, Corelight)
|
||||||
|
|
||||||
|
`FirstPacket()` so far supported only TCP. To extend this to UDP, we
|
||||||
|
move the method into the PIA base class; give it a protocol parameter
|
||||||
|
for the case that there's no actual packet is available; and add the
|
||||||
|
ability to create fake UDP packets as well, not just TCP.
|
||||||
|
|
||||||
|
This whole thing is pretty ugly to begin with, and this doesn't make
|
||||||
|
it nicer, but we need this extension that so we can feed UDP data into
|
||||||
|
the signature engine that's tunneled over other protocols. Without the
|
||||||
|
fake packets, DPD signatures in particular wouldn't have anything to
|
||||||
|
match on.
|
||||||
|
|
||||||
|
* Spicy: Prepare for supporting forwarding to protocols other than TCP. (Robin Sommer, Corelight)
|
||||||
|
|
||||||
|
So far the Spicy runtime supported forwarding data into other
|
||||||
|
analyzers only for TCP analyzers. This puts branching logic in place
|
||||||
|
that let the relevant runtime functions dispatch differently based on
|
||||||
|
the target transport-layer protocol. We don't implement anything else
|
||||||
|
than TCP yet; that will come next.
|
||||||
|
|
||||||
|
Along with the internal changes, this also updates the user-visible
|
||||||
|
runtime function to pass protocol information in. For now, this
|
||||||
|
likewise remains limited to TCP. The function signatures are chosen so
|
||||||
|
that they stay backwards-compatible to previous Spicy version. In
|
||||||
|
particular, they default to TCP where not otherwise specified.
|
||||||
|
|
||||||
|
* Fix include of private file in public header (Benjamin Bannier, Corelight)
|
||||||
|
|
||||||
|
* Update package-manager submodule [nomail] (Tim Wojtulewicz, Corelight)
|
||||||
|
|
||||||
|
* Update zeekctl submodule [nomail] (Tim Wojtulewicz, Corelight)
|
||||||
|
|
||||||
7.0.0-dev.231 | 2024-05-08 13:09:25 -0700
|
7.0.0-dev.231 | 2024-05-08 13:09:25 -0700
|
||||||
|
|
||||||
* enable ZAM operation specifications to reside in multiple files (not yet used) (Vern Paxson, Corelight)
|
* enable ZAM operation specifications to reside in multiple files (not yet used) (Vern Paxson, Corelight)
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
7.0.0-dev.234
|
7.0.0-dev.244
|
||||||
|
|
2
doc
2
doc
|
@ -1 +1 @@
|
||||||
Subproject commit 3ecd2d62d0c369810eef5c6ffaedc17efbff6095
|
Subproject commit 0253bce437cde767b4fe66988defc8fa26935a30
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
module zeek;
|
module zeek;
|
||||||
|
|
||||||
|
import spicy;
|
||||||
|
|
||||||
# Note: Retain the formatting here, doc/scripts/autogen-spicy-lib is picking up on that.
|
# Note: Retain the formatting here, doc/scripts/autogen-spicy-lib is picking up on that.
|
||||||
|
|
||||||
%cxx-include = "zeek/spicy/runtime-support.h";
|
%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.
|
## 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
|
## 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
|
## `protocol_begin` with same argument, and not closed with `protocol_handle_close`
|
||||||
## or protocol_end, no new analyzer will be added.
|
## or `protocol_end`, no new analyzer will be added.
|
||||||
##
|
##
|
||||||
## See `protocol_handle_get_or_create` for the error semantics of this function.
|
## See `protocol_handle_get_or_create` for the error semantics of this function.
|
||||||
##
|
##
|
||||||
## analyzer: type of analyzer to instantiate, specified through its Zeek-side
|
## analyzer: type of analyzer to instantiate, specified through its Zeek-side
|
||||||
## name (similar to what Zeek's signature action `enable` takes); if not
|
## name (similar to what Zeek's signature action `enable` takes)
|
||||||
## 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.)
|
## protocol: the transport-layer protocol that the analyzer uses; only TCP is
|
||||||
public function protocol_begin(analyzer: optional<string> = Null) : void &cxxname="zeek::spicy::rt::protocol_begin";
|
## 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<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
|
||||||
|
## 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.
|
## 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.
|
## 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
|
## - not called from a protocol analyzer, or
|
||||||
## - the requested child protocol analyzer is unknown, 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
|
## - creation of a child analyzer of the requested type was prevented by a
|
||||||
## previous call of `disable_analyzer` with `prevent=T`
|
## 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).
|
## 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
|
## 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
|
## 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<ProtocolHandle> = 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.
|
## 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
|
## is_orig: true to signal gap to the child's originator side, false for the responder
|
||||||
|
##
|
||||||
## offset: start offset of gap in input stream
|
## offset: start offset of gap in input stream
|
||||||
|
##
|
||||||
## 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";
|
||||||
|
|
||||||
|
|
|
@ -422,6 +422,18 @@ Analyzer* Analyzer::GetChildAnalyzer(const zeek::Tag& tag) const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Analyzer* Analyzer::GetChildAnalyzer(const std::string& name) const {
|
||||||
|
LOOP_OVER_CHILDREN(i)
|
||||||
|
if ( (*i)->GetAnalyzerName() == name && ! ((*i)->removing || (*i)->finished) )
|
||||||
|
return *i;
|
||||||
|
|
||||||
|
LOOP_OVER_GIVEN_CHILDREN(i, new_children)
|
||||||
|
if ( (*i)->GetAnalyzerName() == name && ! ((*i)->removing || (*i)->finished) )
|
||||||
|
return *i;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Analyzer* Analyzer::FindChild(ID arg_id) {
|
Analyzer* Analyzer::FindChild(ID arg_id) {
|
||||||
if ( id == arg_id && ! (removing || finished) )
|
if ( id == arg_id && ! (removing || finished) )
|
||||||
return this;
|
return this;
|
||||||
|
@ -465,6 +477,17 @@ Analyzer* Analyzer::FindChild(const char* name) {
|
||||||
return tag ? FindChild(tag) : nullptr;
|
return tag ? FindChild(tag) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Analyzer::CleanupChildren() {
|
||||||
|
AppendNewChildren();
|
||||||
|
|
||||||
|
for ( auto i = children.begin(); i != children.end(); ) {
|
||||||
|
if ( ! ((*i)->finished || (*i)->removing) )
|
||||||
|
++i;
|
||||||
|
else
|
||||||
|
i = DeleteChild(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
analyzer_list::iterator Analyzer::DeleteChild(analyzer_list::iterator i) {
|
analyzer_list::iterator Analyzer::DeleteChild(analyzer_list::iterator i) {
|
||||||
Analyzer* child = *i;
|
Analyzer* child = *i;
|
||||||
|
|
||||||
|
|
|
@ -451,6 +451,19 @@ public:
|
||||||
*/
|
*/
|
||||||
Analyzer* GetChildAnalyzer(const zeek::Tag& tag) const;
|
Analyzer* GetChildAnalyzer(const zeek::Tag& tag) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a pointer to a direct child analyzer of the given name.
|
||||||
|
*
|
||||||
|
* Note that the returned pointer is owned by the analyzer and may
|
||||||
|
* be deleted without notification. Do not hold on to it.
|
||||||
|
*
|
||||||
|
* @param name The name of the analyzers, as returned by its
|
||||||
|
* GetAnalyzereName() method.
|
||||||
|
*
|
||||||
|
* @return The analyzer, or null if not found.
|
||||||
|
*/
|
||||||
|
Analyzer* GetChildAnalyzer(const std::string& name) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively searches all (direct or indirect) children of the
|
* Recursively searches all (direct or indirect) children of the
|
||||||
* analyzer for an analyzer with a specific ID.
|
* analyzer for an analyzer with a specific ID.
|
||||||
|
@ -494,6 +507,13 @@ public:
|
||||||
*/
|
*/
|
||||||
const analyzer_list& GetChildren() { return children; }
|
const analyzer_list& GetChildren() { return children; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes any child analyzers that are finished or marked for deletion.
|
||||||
|
* This normally happens automatically are various times when feeding data
|
||||||
|
* to children, can be triggered explicitly through this method as needed.
|
||||||
|
*/
|
||||||
|
void CleanupChildren();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a pointer to the parent analyzer, or null if this instance
|
* Returns a pointer to the parent analyzer, or null if this instance
|
||||||
* has not yet been added to an analyzer tree.
|
* has not yet been added to an analyzer tree.
|
||||||
|
|
|
@ -946,8 +946,8 @@ void HTTP_Analyzer::DeliverStream(int len, const u_char* data, bool is_orig) {
|
||||||
pia = new analyzer::pia::PIA_TCP(Conn());
|
pia = new analyzer::pia::PIA_TCP(Conn());
|
||||||
|
|
||||||
if ( AddChildAnalyzer(pia) ) {
|
if ( AddChildAnalyzer(pia) ) {
|
||||||
pia->FirstPacket(true, nullptr);
|
pia->FirstPacket(true, TransportProto::TRANSPORT_TCP);
|
||||||
pia->FirstPacket(false, nullptr);
|
pia->FirstPacket(false, TransportProto::TRANSPORT_TCP);
|
||||||
|
|
||||||
int remaining_in_content_line = content_line_resp->GetDeliverStreamRemainingLength();
|
int remaining_in_content_line = content_line_resp->GetDeliverStreamRemainingLength();
|
||||||
if ( remaining_in_content_line > 0 ) {
|
if ( remaining_in_content_line > 0 ) {
|
||||||
|
@ -1396,8 +1396,8 @@ void HTTP_Analyzer::HTTP_Upgrade() {
|
||||||
upgrade_protocol.c_str());
|
upgrade_protocol.c_str());
|
||||||
pia = new analyzer::pia::PIA_TCP(Conn());
|
pia = new analyzer::pia::PIA_TCP(Conn());
|
||||||
if ( AddChildAnalyzer(pia) ) {
|
if ( AddChildAnalyzer(pia) ) {
|
||||||
pia->FirstPacket(true, nullptr);
|
pia->FirstPacket(true, TransportProto::TRANSPORT_TCP);
|
||||||
pia->FirstPacket(false, nullptr);
|
pia->FirstPacket(false, TransportProto::TRANSPORT_TCP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -169,26 +169,37 @@ void PIA_TCP::Init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PIA_TCP::FirstPacket(bool is_orig, const IP_Hdr* ip) {
|
void PIA::FirstPacket(bool is_orig, TransportProto proto) { FirstPacket(is_orig, proto, nullptr); }
|
||||||
|
|
||||||
|
void PIA::FirstPacket(bool is_orig, const IP_Hdr* ip) {
|
||||||
|
assert(ip);
|
||||||
|
FirstPacket(is_orig, {}, ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PIA::FirstPacket(bool is_orig, const std::optional<TransportProto>& proto, const IP_Hdr* ip) {
|
||||||
static char dummy_packet[sizeof(struct ip) + sizeof(struct tcphdr)];
|
static char dummy_packet[sizeof(struct ip) + sizeof(struct tcphdr)];
|
||||||
static struct ip* ip4 = nullptr;
|
static struct ip* ip4_tcp = nullptr;
|
||||||
|
static struct ip* ip4_udp = nullptr;
|
||||||
static struct tcphdr* tcp4 = nullptr;
|
static struct tcphdr* tcp4 = nullptr;
|
||||||
static IP_Hdr* ip4_hdr = nullptr;
|
static struct udphdr* udp4 = nullptr;
|
||||||
|
static IP_Hdr* ip4_tcp_hdr = nullptr;
|
||||||
|
static IP_Hdr* ip4_udp_hdr = nullptr;
|
||||||
|
|
||||||
DBG_LOG(DBG_ANALYZER, "PIA_TCP[%d] FirstPacket(%s)", GetID(), (is_orig ? "T" : "F"));
|
if ( ! ip && proto ) { // proto needed here to avoid GCC warning that it may be used uninitialized
|
||||||
|
|
||||||
if ( ! ip ) {
|
|
||||||
// Create a dummy packet. Not very elegant, but everything
|
// Create a dummy packet. Not very elegant, but everything
|
||||||
// else would be *really* ugly ...
|
// else would be *really* ugly ...
|
||||||
if ( ! ip4_hdr ) {
|
switch ( *proto ) {
|
||||||
ip4 = (struct ip*)dummy_packet;
|
case TransportProto::TRANSPORT_TCP: {
|
||||||
|
DBG_LOG(DBG_ANALYZER, "PIA/TCP FirstPacket(%s)", (is_orig ? "T" : "F"));
|
||||||
|
|
||||||
|
if ( ! ip4_tcp_hdr ) {
|
||||||
|
ip4_tcp = (struct ip*)dummy_packet;
|
||||||
tcp4 = (struct tcphdr*)(dummy_packet + sizeof(struct ip));
|
tcp4 = (struct tcphdr*)(dummy_packet + sizeof(struct ip));
|
||||||
ip4->ip_len = sizeof(struct ip) + sizeof(struct tcphdr);
|
ip4_tcp->ip_len = sizeof(struct ip) + sizeof(struct tcphdr);
|
||||||
ip4->ip_hl = sizeof(struct ip) >> 2;
|
ip4_tcp->ip_hl = sizeof(struct ip) >> 2;
|
||||||
ip4->ip_p = IPPROTO_TCP;
|
|
||||||
|
|
||||||
// Cast to const so that it doesn't delete it.
|
// Cast to const so that it doesn't delete it.
|
||||||
ip4_hdr = new IP_Hdr(ip4, false);
|
ip4_tcp_hdr = new IP_Hdr(ip4_tcp, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locals used to avoid potential alignment problems
|
// Locals used to avoid potential alignment problems
|
||||||
|
@ -210,11 +221,60 @@ void PIA_TCP::FirstPacket(bool is_orig, const IP_Hdr* ip) {
|
||||||
tcp4->th_dport = htons(Conn()->OrigPort());
|
tcp4->th_dport = htons(Conn()->OrigPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
ip4->ip_src = tmp_src;
|
ip4_tcp->ip_src = tmp_src;
|
||||||
ip4->ip_dst = tmp_dst;
|
ip4_tcp->ip_dst = tmp_dst;
|
||||||
ip = ip4_hdr;
|
ip4_tcp->ip_p = IPPROTO_TCP;
|
||||||
|
ip = ip4_tcp_hdr;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case TransportProto::TRANSPORT_UDP: {
|
||||||
|
DBG_LOG(DBG_ANALYZER, "PIA/UDP FirstPacket(%s)", (is_orig ? "T" : "F"));
|
||||||
|
|
||||||
|
if ( ! ip4_udp_hdr ) {
|
||||||
|
ip4_udp = (struct ip*)dummy_packet;
|
||||||
|
udp4 = (struct udphdr*)(dummy_packet + sizeof(struct ip));
|
||||||
|
ip4_udp->ip_len = sizeof(struct ip) + sizeof(struct udphdr);
|
||||||
|
ip4_udp->ip_hl = sizeof(struct ip) >> 2;
|
||||||
|
|
||||||
|
// Cast to const so that it doesn't delete it.
|
||||||
|
ip4_udp_hdr = new IP_Hdr(ip4_udp, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locals used to avoid potential alignment problems
|
||||||
|
// with some archs/compilers when grabbing the address
|
||||||
|
// of the struct member directly in the following.
|
||||||
|
in_addr tmp_src;
|
||||||
|
in_addr tmp_dst;
|
||||||
|
|
||||||
|
if ( is_orig ) {
|
||||||
|
Conn()->OrigAddr().CopyIPv4(&tmp_src);
|
||||||
|
Conn()->RespAddr().CopyIPv4(&tmp_dst);
|
||||||
|
udp4->uh_sport = htons(Conn()->OrigPort());
|
||||||
|
udp4->uh_dport = htons(Conn()->RespPort());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Conn()->RespAddr().CopyIPv4(&tmp_src);
|
||||||
|
Conn()->OrigAddr().CopyIPv4(&tmp_dst);
|
||||||
|
udp4->uh_sport = htons(Conn()->RespPort());
|
||||||
|
udp4->uh_dport = htons(Conn()->OrigPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
ip4_udp->ip_src = tmp_src;
|
||||||
|
ip4_udp->ip_dst = tmp_dst;
|
||||||
|
ip4_udp->ip_p = IPPROTO_UDP;
|
||||||
|
ip = ip4_udp_hdr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TransportProto::TRANSPORT_ICMP: reporter->InternalError("ICMP not supported in PIA::FirstPacket");
|
||||||
|
|
||||||
|
default: reporter->InternalError("unknown transport proto in PIA::FirstPacket");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(ip);
|
||||||
|
|
||||||
if ( ! MatcherInitialized(is_orig) )
|
if ( ! MatcherInitialized(is_orig) )
|
||||||
DoMatch((const u_char*)"", 0, is_orig, true, false, false, ip);
|
DoMatch((const u_char*)"", 0, is_orig, true, false, false, ip);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,32 @@ public:
|
||||||
|
|
||||||
void ReplayPacketBuffer(analyzer::Analyzer* analyzer);
|
void ReplayPacketBuffer(analyzer::Analyzer* analyzer);
|
||||||
|
|
||||||
|
// The first packet for each direction of a connection is passed
|
||||||
|
// in here. This initializes the signature engine state for DPD.
|
||||||
|
//
|
||||||
|
// This version of the method should be used preferably, assuming an IP
|
||||||
|
// header is available.
|
||||||
|
//
|
||||||
|
// (This API is a bit crude as it doesn't really fit nicely into the
|
||||||
|
// analyzer interface. Yet we need it for initializing the packet matcher
|
||||||
|
// in the case that we already get reassembled input; and making it part of
|
||||||
|
// the general analyzer interface seems to be unnecessary overhead.)
|
||||||
|
void FirstPacket(bool is_orig, const IP_Hdr* ip);
|
||||||
|
|
||||||
|
// The first packet for each direction of a connection is passed
|
||||||
|
// in here. This initializes the signature engine state for DPD.
|
||||||
|
//
|
||||||
|
// This version of the method should be used if no actual IP header is
|
||||||
|
// available. In that case a fake one will be created internally just for
|
||||||
|
// initializing the signature engine. The fake header's transport-layer
|
||||||
|
// protocol will be `proto`. Only TCP or UDP are supported.
|
||||||
|
//
|
||||||
|
// (Similar to the other variant of this method, this API is a bit crude as
|
||||||
|
// it doesn't really fit nicely into the analyzer interface. This version
|
||||||
|
// we need for feeding data into the matcher that's not directly top-level
|
||||||
|
// IP payload, but decapsulated out of other layers.)
|
||||||
|
void FirstPacket(bool is_orig, TransportProto proto);
|
||||||
|
|
||||||
// Children are also derived from Analyzer. Return this object
|
// Children are also derived from Analyzer. Return this object
|
||||||
// as pointer to an Analyzer.
|
// as pointer to an Analyzer.
|
||||||
analyzer::Analyzer* AsAnalyzer() { return as_analyzer; }
|
analyzer::Analyzer* AsAnalyzer() { return as_analyzer; }
|
||||||
|
@ -77,11 +103,15 @@ protected:
|
||||||
void DoMatch(const u_char* data, int len, bool is_orig, bool bol, bool eol, bool clear_state,
|
void DoMatch(const u_char* data, int len, bool is_orig, bool bol, bool eol, bool clear_state,
|
||||||
const IP_Hdr* ip = nullptr);
|
const IP_Hdr* ip = nullptr);
|
||||||
|
|
||||||
|
auto Conn() const { return conn; }
|
||||||
void SetConn(Connection* c) { conn = c; }
|
void SetConn(Connection* c) { conn = c; }
|
||||||
|
|
||||||
Buffer pkt_buffer;
|
Buffer pkt_buffer;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Joint backend for the two public FirstPacket() methods.
|
||||||
|
void FirstPacket(bool is_orig, const std::optional<TransportProto>& proto, const IP_Hdr* ip);
|
||||||
|
|
||||||
analyzer::Analyzer* as_analyzer;
|
analyzer::Analyzer* as_analyzer;
|
||||||
Connection* conn;
|
Connection* conn;
|
||||||
DataBlock current_packet;
|
DataBlock current_packet;
|
||||||
|
@ -123,16 +153,6 @@ public:
|
||||||
|
|
||||||
void Init() override;
|
void Init() override;
|
||||||
|
|
||||||
// The first packet for each direction of a connection is passed
|
|
||||||
// in here.
|
|
||||||
//
|
|
||||||
// (This is a bit crude as it doesn't really fit nicely into the
|
|
||||||
// analyzer interface. Yet we need it for initializing the packet
|
|
||||||
// matcher in the case that we already get reassembled input,
|
|
||||||
// and making it part of the general analyzer interface seems
|
|
||||||
// to be unnecessary overhead.)
|
|
||||||
void FirstPacket(bool is_orig, const IP_Hdr* ip);
|
|
||||||
|
|
||||||
void ReplayStreamBuffer(analyzer::Analyzer* analyzer);
|
void ReplayStreamBuffer(analyzer::Analyzer* analyzer);
|
||||||
|
|
||||||
static analyzer::Analyzer* Instantiate(Connection* conn) { return new PIA_TCP(conn); }
|
static analyzer::Analyzer* Instantiate(Connection* conn) { return new PIA_TCP(conn); }
|
||||||
|
|
|
@ -48,8 +48,8 @@ void SOCKS_Analyzer::DeliverStream(int len, const u_char* data, bool orig) {
|
||||||
if ( ! pia ) {
|
if ( ! pia ) {
|
||||||
pia = new analyzer::pia::PIA_TCP(Conn());
|
pia = new analyzer::pia::PIA_TCP(Conn());
|
||||||
if ( AddChildAnalyzer(pia) ) {
|
if ( AddChildAnalyzer(pia) ) {
|
||||||
pia->FirstPacket(true, nullptr);
|
pia->FirstPacket(true, TransportProto::TRANSPORT_TCP);
|
||||||
pia->FirstPacket(false, nullptr);
|
pia->FirstPacket(false, TransportProto::TRANSPORT_TCP);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
pia = nullptr;
|
pia = nullptr;
|
||||||
|
|
|
@ -357,8 +357,8 @@ void SSL_Analyzer::ForwardDecryptedData(const std::vector<u_char>& data, bool is
|
||||||
if ( ! pia ) {
|
if ( ! pia ) {
|
||||||
pia = new analyzer::pia::PIA_TCP(Conn());
|
pia = new analyzer::pia::PIA_TCP(Conn());
|
||||||
if ( AddChildAnalyzer(pia) ) {
|
if ( AddChildAnalyzer(pia) ) {
|
||||||
pia->FirstPacket(true, nullptr);
|
pia->FirstPacket(true, TransportProto::TRANSPORT_TCP);
|
||||||
pia->FirstPacket(false, nullptr);
|
pia->FirstPacket(false, TransportProto::TRANSPORT_TCP);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
reporter->Error("Could not initialize PIA");
|
reporter->Error("Could not initialize PIA");
|
||||||
|
|
|
@ -79,8 +79,8 @@ bool WebSocket_Analyzer::Configure(zeek::RecordValPtr config) {
|
||||||
|
|
||||||
auto* pia = new analyzer::pia::PIA_TCP(Conn());
|
auto* pia = new analyzer::pia::PIA_TCP(Conn());
|
||||||
if ( effective_analyzer->AddChildAnalyzer(pia) ) {
|
if ( effective_analyzer->AddChildAnalyzer(pia) ) {
|
||||||
pia->FirstPacket(true, nullptr);
|
pia->FirstPacket(true, TransportProto::TRANSPORT_TCP);
|
||||||
pia->FirstPacket(false, nullptr);
|
pia->FirstPacket(false, TransportProto::TRANSPORT_TCP);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include "zeek/spicy/runtime-support.h"
|
#include "zeek/spicy/runtime-support.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <hilti/rt/exception.h>
|
#include <hilti/rt/exception.h>
|
||||||
|
@ -10,6 +9,7 @@
|
||||||
#include <hilti/rt/types/port.h>
|
#include <hilti/rt/types/port.h>
|
||||||
#include <hilti/rt/util.h>
|
#include <hilti/rt/util.h>
|
||||||
|
|
||||||
|
#include "net_util.h"
|
||||||
#include "zeek/Event.h"
|
#include "zeek/Event.h"
|
||||||
#include "zeek/analyzer/Manager.h"
|
#include "zeek/analyzer/Manager.h"
|
||||||
#include "zeek/analyzer/protocol/pia/PIA.h"
|
#include "zeek/analyzer/protocol/pia/PIA.h"
|
||||||
|
@ -485,15 +485,19 @@ void rt::weird(const std::string& id, const std::string& addl) {
|
||||||
throw ValueUnavailable("none of $conn, $file, or $packet available for weird reporting");
|
throw ValueUnavailable("none of $conn, $file, or $packet available for weird reporting");
|
||||||
}
|
}
|
||||||
|
|
||||||
void rt::protocol_begin(const std::optional<std::string>& analyzer) {
|
void rt::protocol_begin(const std::optional<std::string>& analyzer, const ::hilti::rt::Protocol& proto) {
|
||||||
auto _ = hilti::rt::profiler::start("zeek/rt/protocol_begin");
|
auto _ = hilti::rt::profiler::start("zeek/rt/protocol_begin");
|
||||||
|
|
||||||
if ( analyzer ) {
|
if ( analyzer ) {
|
||||||
protocol_handle_get_or_create(*analyzer);
|
protocol_handle_get_or_create(*analyzer, proto);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instantiate a DPD analyzer.
|
// Instantiate a DPD analyzer. If a direct child of this type already
|
||||||
|
// exists, we abort 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.
|
||||||
|
|
||||||
auto cookie = static_cast<Cookie*>(hilti::rt::context::cookie());
|
auto cookie = static_cast<Cookie*>(hilti::rt::context::cookie());
|
||||||
assert(cookie);
|
assert(cookie);
|
||||||
|
|
||||||
|
@ -501,36 +505,47 @@ void rt::protocol_begin(const std::optional<std::string>& analyzer) {
|
||||||
if ( ! c )
|
if ( ! c )
|
||||||
throw ValueUnavailable("no current connection available");
|
throw ValueUnavailable("no current connection available");
|
||||||
|
|
||||||
// Use a Zeek PIA stream analyzer performing DPD.
|
switch ( proto.value() ) {
|
||||||
|
case ::hilti::rt::Protocol::TCP: {
|
||||||
|
// Use a Zeek PIA stream (TCP) analyzer performing DPD.
|
||||||
auto pia_tcp = std::make_unique<analyzer::pia::PIA_TCP>(c->analyzer->Conn());
|
auto pia_tcp = std::make_unique<analyzer::pia::PIA_TCP>(c->analyzer->Conn());
|
||||||
|
pia_tcp->FirstPacket(true, TransportProto::TRANSPORT_TCP);
|
||||||
|
pia_tcp->FirstPacket(false, TransportProto::TRANSPORT_TCP);
|
||||||
|
|
||||||
// Forward empty payload to trigger lifecycle management in this analyzer tree.
|
c->analyzer->CleanupChildren();
|
||||||
c->analyzer->ForwardStream(0, reinterpret_cast<const u_char*>(c->analyzer), true);
|
|
||||||
c->analyzer->ForwardStream(0, reinterpret_cast<const u_char*>(c->analyzer), false);
|
|
||||||
|
|
||||||
// Direct child of this type already exists. We ignore this silently
|
// If the child already exists, do not add it again so this function is idempotent.
|
||||||
// because that makes usage nicer if either side of the connection
|
if ( auto child = c->analyzer->GetChildAnalyzer(pia_tcp->GetAnalyzerName()) )
|
||||||
// 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;
|
return;
|
||||||
|
|
||||||
auto child = pia_tcp.release();
|
auto child = pia_tcp.release();
|
||||||
c->analyzer->AddChildAnalyzer(child);
|
c->analyzer->AddChildAnalyzer(child);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
child->FirstPacket(true, nullptr);
|
case ::hilti::rt::Protocol::UDP: {
|
||||||
child->FirstPacket(false, nullptr);
|
// Use a Zeek PIA packet (UDP) analyzer performing DPD.
|
||||||
|
auto pia_udp = std::make_unique<analyzer::pia::PIA_UDP>(c->analyzer->Conn());
|
||||||
|
pia_udp->FirstPacket(true, TransportProto::TRANSPORT_UDP);
|
||||||
|
pia_udp->FirstPacket(false, TransportProto::TRANSPORT_UDP);
|
||||||
|
|
||||||
|
c->analyzer->CleanupChildren();
|
||||||
|
auto child = pia_udp.release();
|
||||||
|
c->analyzer->AddChildAnalyzer(child);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 _ = hilti::rt::profiler::start("zeek/rt/protocol_handle_get_or_create");
|
||||||
auto cookie = static_cast<Cookie*>(hilti::rt::context::cookie());
|
auto cookie = static_cast<Cookie*>(hilti::rt::context::cookie());
|
||||||
assert(cookie);
|
assert(cookie);
|
||||||
|
@ -539,20 +554,13 @@ rt::ProtocolHandle rt::protocol_handle_get_or_create(const std::string& analyzer
|
||||||
if ( ! c )
|
if ( ! c )
|
||||||
throw ValueUnavailable("no current connection available");
|
throw ValueUnavailable("no current connection available");
|
||||||
|
|
||||||
// Forward empty payload to trigger lifecycle management in this analyzer tree.
|
switch ( proto.value() ) {
|
||||||
c->analyzer->ForwardStream(0, reinterpret_cast<const u_char*>(c->analyzer), true);
|
case ::hilti::rt::Protocol::TCP: {
|
||||||
c->analyzer->ForwardStream(0, reinterpret_cast<const u_char*>(c->analyzer), false);
|
c->analyzer->CleanupChildren();
|
||||||
|
|
||||||
// If the child already exists, do not add it again so this function is idempotent.
|
// If the child already exists, do not add it again so this function is idempotent.
|
||||||
//
|
if ( auto child = c->analyzer->GetChildAnalyzer(analyzer) )
|
||||||
// We inspect the children directly to work around zeek/zeek#2899.
|
return rt::ProtocolHandle(child->GetID(), proto);
|
||||||
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());
|
|
||||||
|
|
||||||
auto child = analyzer_mgr->InstantiateAnalyzer(analyzer.c_str(), c->analyzer->Conn());
|
auto child = analyzer_mgr->InstantiateAnalyzer(analyzer.c_str(), c->analyzer->Conn());
|
||||||
if ( ! child )
|
if ( ! child )
|
||||||
|
@ -576,18 +584,42 @@ rt::ProtocolHandle rt::protocol_handle_get_or_create(const std::string& analyzer
|
||||||
->Done(); // will never see packets; cast to get around protected inheritance
|
->Done(); // will never see packets; cast to get around protected inheritance
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* child_as_tcp = dynamic_cast<analyzer::tcp::TCP_ApplicationAnalyzer*>(child);
|
return rt::ProtocolHandle(child->GetID(), proto);
|
||||||
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 )
|
case ::hilti::rt::Protocol::UDP: {
|
||||||
child_as_tcp->SetTCP(c->fake_tcp.get());
|
c->analyzer->CleanupChildren();
|
||||||
|
|
||||||
return rt::ProtocolHandle(child->GetID());
|
// If the child already exists, do not add it again so this function is idempotent.
|
||||||
|
if ( auto child = c->analyzer->GetChildAnalyzer(analyzer) )
|
||||||
|
return rt::ProtocolHandle(child->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));
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
return rt::ProtocolHandle(child->GetID(), proto);
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rt::protocol_data_in(const hilti::rt::Bool& is_orig, const hilti::rt::Bytes& data,
|
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<rt::ProtocolHandle>& h) {
|
const std::optional<rt::ProtocolHandle>& h) {
|
||||||
auto _ = hilti::rt::profiler::start("zeek/rt/protocol_data_in");
|
auto _ = hilti::rt::profiler::start("zeek/rt/protocol_data_in");
|
||||||
auto cookie = static_cast<Cookie*>(hilti::rt::context::cookie());
|
auto cookie = static_cast<Cookie*>(hilti::rt::context::cookie());
|
||||||
|
@ -597,6 +629,22 @@ void rt::protocol_data_in(const hilti::rt::Bool& is_orig, const hilti::rt::Bytes
|
||||||
if ( ! c )
|
if ( ! c )
|
||||||
throw ValueUnavailable("no current connection available");
|
throw ValueUnavailable("no current connection available");
|
||||||
|
|
||||||
|
::hilti::rt::Protocol protocol_to_use = ::hilti::rt::Protocol::Undef;
|
||||||
|
|
||||||
|
if ( proto ) {
|
||||||
|
if ( h && h->protocol() != *proto )
|
||||||
|
throw InvalidValue("protocol_data_in: protocol mismatches with analyzer handle");
|
||||||
|
|
||||||
|
protocol_to_use = *proto;
|
||||||
|
}
|
||||||
|
else if ( h )
|
||||||
|
protocol_to_use = h->protocol();
|
||||||
|
|
||||||
|
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 len = data.size();
|
||||||
auto* data_ = reinterpret_cast<const u_char*>(data.data());
|
auto* data_ = reinterpret_cast<const u_char*>(data.data());
|
||||||
|
|
||||||
|
@ -616,6 +664,50 @@ void rt::protocol_data_in(const hilti::rt::Bool& is_orig, const hilti::rt::Bytes
|
||||||
|
|
||||||
else
|
else
|
||||||
c->analyzer->ForwardStream(len, data_, is_orig);
|
c->analyzer->ForwardStream(len, data_, is_orig);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ::hilti::rt::Protocol::UDP: {
|
||||||
|
auto len = data.size();
|
||||||
|
auto* data_ = reinterpret_cast<const u_char*>(data.data());
|
||||||
|
|
||||||
|
if ( h ) {
|
||||||
|
if ( auto* output_handler = c->analyzer->GetOutputHandler() )
|
||||||
|
output_handler->DeliverPacket(len, data_, is_orig, 0, nullptr, 0);
|
||||||
|
|
||||||
|
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->NextPacket(len, data_, is_orig);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
c->analyzer->ForwardPacket(len, data_, is_orig, 0, nullptr, 0);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<uint64_t>& offset,
|
void rt::protocol_gap(const hilti::rt::Bool& is_orig, const hilti::rt::integer::safe<uint64_t>& offset,
|
||||||
|
@ -628,6 +720,8 @@ void rt::protocol_gap(const hilti::rt::Bool& is_orig, const hilti::rt::integer::
|
||||||
if ( ! c )
|
if ( ! c )
|
||||||
throw ValueUnavailable("no current connection available");
|
throw ValueUnavailable("no current connection available");
|
||||||
|
|
||||||
|
switch ( h->protocol().value() ) {
|
||||||
|
case ::hilti::rt::Protocol::TCP: {
|
||||||
if ( h ) {
|
if ( h ) {
|
||||||
if ( auto* output_handler = c->analyzer->GetOutputHandler() )
|
if ( auto* output_handler = c->analyzer->GetOutputHandler() )
|
||||||
output_handler->Undelivered(offset, len, is_orig);
|
output_handler->Undelivered(offset, len, is_orig);
|
||||||
|
@ -644,6 +738,20 @@ void rt::protocol_gap(const hilti::rt::Bool& is_orig, const hilti::rt::integer::
|
||||||
|
|
||||||
else
|
else
|
||||||
c->analyzer->ForwardUndelivered(offset, len, is_orig);
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rt::protocol_end() {
|
void rt::protocol_end() {
|
||||||
|
@ -668,6 +776,8 @@ void rt::protocol_handle_close(const ProtocolHandle& handle) {
|
||||||
if ( ! c )
|
if ( ! c )
|
||||||
throw ValueUnavailable("no current connection available");
|
throw ValueUnavailable("no current connection available");
|
||||||
|
|
||||||
|
switch ( handle.protocol().value() ) {
|
||||||
|
case ::hilti::rt::Protocol::TCP: {
|
||||||
auto child = c->analyzer->FindChild(handle.id());
|
auto child = c->analyzer->FindChild(handle.id());
|
||||||
if ( ! child )
|
if ( ! child )
|
||||||
throw ValueUnavailable(hilti::rt::fmt("unknown child analyzer %s", handle));
|
throw ValueUnavailable(hilti::rt::fmt("unknown child analyzer %s", handle));
|
||||||
|
@ -679,6 +789,27 @@ void rt::protocol_handle_close(const ProtocolHandle& handle) {
|
||||||
child->NextEndOfData(false);
|
child->NextEndOfData(false);
|
||||||
|
|
||||||
c->analyzer->RemoveChildAnalyzer(handle.id());
|
c->analyzer->RemoveChildAnalyzer(handle.id());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ::hilti::rt::Protocol::UDP: {
|
||||||
|
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));
|
||||||
|
|
||||||
|
c->analyzer->RemoveChildAnalyzer(handle.id());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<std::string> fid_provided) {
|
rt::cookie::FileState* rt::cookie::FileStateStack::push(std::optional<std::string> fid_provided) {
|
||||||
|
|
|
@ -294,7 +294,7 @@ void reject_protocol(const std::string& reason = "protocol rejected");
|
||||||
class ProtocolHandle {
|
class ProtocolHandle {
|
||||||
public:
|
public:
|
||||||
ProtocolHandle() {}
|
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 {
|
uint64_t id() const {
|
||||||
if ( ! _id )
|
if ( ! _id )
|
||||||
|
@ -303,6 +303,8 @@ public:
|
||||||
return *_id;
|
return *_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto& protocol() const { return _proto; }
|
||||||
|
|
||||||
friend std::string to_string(const ProtocolHandle& h, ::hilti::rt::detail::adl::tag) {
|
friend std::string to_string(const ProtocolHandle& h, ::hilti::rt::detail::adl::tag) {
|
||||||
if ( ! h._id )
|
if ( ! h._id )
|
||||||
return "(uninitialized protocol handle)";
|
return "(uninitialized protocol handle)";
|
||||||
|
@ -316,38 +318,56 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::optional<uint64_t> _id;
|
std::optional<uint64_t> _id;
|
||||||
|
::hilti::rt::Protocol _proto = ::hilti::rt::Protocol::Undef;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a Zeek-side child protocol analyzer to the current connection.
|
* Adds a Zeek-side child protocol analyzer to the current connection.
|
||||||
*
|
*
|
||||||
* @param analyzer if given, the Zeek-side name of the analyzer to instantiate;
|
* @param analyzer the Zeek-side name of the analyzer to instantiate; can be left unset to add a DPD analyzer
|
||||||
* if not given, DPD will be used
|
|
||||||
*/
|
*/
|
||||||
void protocol_begin(const std::optional<std::string>& analyzer);
|
void protocol_begin(const std::optional<std::string>& 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
|
* 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.
|
* does not yet exist it will be created.
|
||||||
*
|
*
|
||||||
* @param analyzer the Zeek-side name of the analyzer to get (e.g., `HTTP`)
|
* @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
|
* @return a handle to the child analyzer. When done, the handle should be
|
||||||
* closed, either explicitly with protocol_handle_close or implicitly with
|
* closed, either explicitly with protocol_handle_close or implicitly with
|
||||||
* protocol_end.
|
* 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
|
* 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 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 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,
|
void protocol_data_in(const hilti::rt::Bool& is_orig, const hilti::rt::Bytes& data, const ::hilti::rt::Protocol& proto);
|
||||||
const std::optional<ProtocolHandle>& h = {});
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Signals a gap in input data to all previously instantiated Zeek-side child
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
#separator \x09
|
||||||
|
#set_separator ,
|
||||||
|
#empty_field (empty)
|
||||||
|
#unset_field -
|
||||||
|
#path http
|
||||||
|
#open XXXX-XX-XX-XX-XX-XX
|
||||||
|
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p trans_depth method host uri referrer version user_agent origin request_body_len response_body_len status_code status_msg info_code info_msg tags username password proxied orig_fuids orig_filenames orig_mime_types resp_fuids resp_filenames resp_mime_types
|
||||||
|
#types time string addr port addr port count string string string string string string string count count count string count string set[enum] string string set[string] vector[string] vector[string] vector[string] vector[string] vector[string] vector[string]
|
||||||
|
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 207.158.192.40 53 10.20.1.31 53 1 GET - /etc/passwd1 - 1.0 - - 0 0 200 OK - - (empty) - - - - - - - - -
|
||||||
|
#close XXXX-XX-XX-XX-XX-XX
|
|
@ -0,0 +1,18 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
#separator \x09
|
||||||
|
#set_separator ,
|
||||||
|
#empty_field (empty)
|
||||||
|
#unset_field -
|
||||||
|
#path syslog
|
||||||
|
#open XXXX-XX-XX-XX-XX-XX
|
||||||
|
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto facility severity message
|
||||||
|
#types time string addr port addr port enum string string string
|
||||||
|
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.150.186.169 49244 131.159.14.23 22 tcp UNSPECIFIED UNSPECIFIED A1 orig
|
||||||
|
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.150.186.169 49244 131.159.14.23 22 tcp UNSPECIFIED UNSPECIFIED A1 resp
|
||||||
|
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.150.186.169 49244 131.159.14.23 22 tcp UNSPECIFIED UNSPECIFIED A2 orig
|
||||||
|
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.150.186.169 49244 131.159.14.23 22 tcp UNSPECIFIED UNSPECIFIED A2 resp
|
||||||
|
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.150.186.169 49244 131.159.14.23 22 tcp UNSPECIFIED UNSPECIFIED B1 orig
|
||||||
|
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.150.186.169 49244 131.159.14.23 22 tcp UNSPECIFIED UNSPECIFIED B1 resp
|
||||||
|
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.150.186.169 49244 131.159.14.23 22 tcp UNSPECIFIED UNSPECIFIED C1 orig
|
||||||
|
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.150.186.169 49244 131.159.14.23 22 tcp UNSPECIFIED UNSPECIFIED C1 resp
|
||||||
|
#close XXXX-XX-XX-XX-XX-XX
|
|
@ -0,0 +1,34 @@
|
||||||
|
# @TEST-REQUIRES: have-spicy
|
||||||
|
#
|
||||||
|
# @TEST-EXEC: spicyz -d -o test.hlto dns.spicy ./dns.evt
|
||||||
|
# @TEST-EXEC: zeek -r ${TRACES}/dns53.pcap test.hlto %INPUT
|
||||||
|
# @TEST-EXEC: btest-diff http.log
|
||||||
|
|
||||||
|
# @TEST-START-FILE dns.spicy
|
||||||
|
module DNS;
|
||||||
|
|
||||||
|
import spicy;
|
||||||
|
import zeek;
|
||||||
|
|
||||||
|
public type Packet = unit {
|
||||||
|
data: bytes &eod;
|
||||||
|
};
|
||||||
|
|
||||||
|
on Packet::%done {
|
||||||
|
zeek::protocol_begin("HTTP", spicy::Protocol::TCP);
|
||||||
|
zeek::protocol_data_in(True, b"GET /etc/passwd1 ");
|
||||||
|
zeek::protocol_data_in(True, b"HTTP/1.0\r\n\r\n");
|
||||||
|
zeek::protocol_data_in(False, b"HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n");
|
||||||
|
zeek::protocol_end();
|
||||||
|
}
|
||||||
|
# @TEST-END-FILE
|
||||||
|
|
||||||
|
# @TEST-START-FILE dns.evt
|
||||||
|
|
||||||
|
import zeek;
|
||||||
|
|
||||||
|
protocol analyzer spicy::DNS over UDP:
|
||||||
|
parse originator with DNS::Packet,
|
||||||
|
replaces DNS;
|
||||||
|
|
||||||
|
# @TEST-END-FILE
|
59
testing/btest/spicy/protocol-analyzer-data-in-udp.zeek
Normal file
59
testing/btest/spicy/protocol-analyzer-data-in-udp.zeek
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
# @TEST-REQUIRES: have-spicy
|
||||||
|
#
|
||||||
|
# @TEST-EXEC: spicyz -d -o test.hlto test.evt test.spicy
|
||||||
|
# @TEST-EXEC: zeek -B dpd -s test.sig -r ${TRACES}/ssh/single-conn.trace test.hlto %INPUT Spicy::enable_print=T >&2
|
||||||
|
# @TEST-EXEC: btest-diff syslog.log
|
||||||
|
|
||||||
|
event zeek_init() {
|
||||||
|
Analyzer::register_for_port(Analyzer::ANALYZER_SPICY_SSH, 22/tcp);
|
||||||
|
}
|
||||||
|
|
||||||
|
# @TEST-START-FILE test.sig
|
||||||
|
signature dpd_syslog {
|
||||||
|
payload /.*C1/
|
||||||
|
enable "Syslog"
|
||||||
|
}
|
||||||
|
# @TEST-END-FILE
|
||||||
|
|
||||||
|
# @TEST-START-FILE test.spicy
|
||||||
|
module Test;
|
||||||
|
|
||||||
|
import spicy;
|
||||||
|
import zeek;
|
||||||
|
|
||||||
|
public type Foo = unit {
|
||||||
|
};
|
||||||
|
|
||||||
|
on Foo::%init {
|
||||||
|
# Specify analyzer.
|
||||||
|
zeek::protocol_begin("Syslog", spicy::Protocol::UDP);
|
||||||
|
zeek::protocol_data_in(True, b"A1 orig", spicy::Protocol::UDP);
|
||||||
|
zeek::protocol_data_in(False, b"A1 resp", spicy::Protocol::UDP);
|
||||||
|
zeek::protocol_data_in(True, b"A2 orig", spicy::Protocol::UDP);
|
||||||
|
zeek::protocol_data_in(False, b"A2 resp", spicy::Protocol::UDP);
|
||||||
|
zeek::protocol_end();
|
||||||
|
|
||||||
|
# Use explicit handle.
|
||||||
|
local syslog = zeek::protocol_handle_get_or_create("syslog", spicy::Protocol::UDP);
|
||||||
|
zeek::protocol_data_in(True, b"B1 orig", syslog);
|
||||||
|
zeek::protocol_data_in(False, b"B1 resp", syslog);
|
||||||
|
zeek::protocol_handle_close(syslog);
|
||||||
|
|
||||||
|
# DPD.
|
||||||
|
zeek::protocol_begin(spicy::Protocol::UDP);
|
||||||
|
zeek::protocol_data_in(True, b"C1 orig", spicy::Protocol::UDP);
|
||||||
|
zeek::protocol_data_in(False, b"C1 resp", spicy::Protocol::UDP);
|
||||||
|
zeek::protocol_end();
|
||||||
|
|
||||||
|
}
|
||||||
|
# @TEST-END-FILE
|
||||||
|
|
||||||
|
# @TEST-START-FILE test.evt
|
||||||
|
|
||||||
|
import zeek;
|
||||||
|
|
||||||
|
protocol analyzer spicy::SSH over TCP:
|
||||||
|
parse originator with Test::Foo,
|
||||||
|
replaces SSH;
|
||||||
|
|
||||||
|
# @TEST-END-FILE
|
Loading…
Add table
Add a link
Reference in a new issue