mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +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
|
||||
|
||||
* 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;
|
||||
|
||||
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<string> = 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<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.
|
||||
##
|
||||
## 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<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.
|
||||
##
|
||||
## 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<ProtocolHandle> = Null) : void &cxxname="zeek::spicy::rt::protocol_gap";
|
||||
|
||||
|
|
|
@ -422,6 +422,18 @@ Analyzer* Analyzer::GetChildAnalyzer(const zeek::Tag& tag) const {
|
|||
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) {
|
||||
if ( id == arg_id && ! (removing || finished) )
|
||||
return this;
|
||||
|
@ -465,6 +477,17 @@ Analyzer* Analyzer::FindChild(const char* name) {
|
|||
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* child = *i;
|
||||
|
||||
|
|
|
@ -451,6 +451,19 @@ public:
|
|||
*/
|
||||
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
|
||||
* analyzer for an analyzer with a specific ID.
|
||||
|
@ -494,6 +507,13 @@ public:
|
|||
*/
|
||||
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
|
||||
* 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());
|
||||
|
||||
if ( AddChildAnalyzer(pia) ) {
|
||||
pia->FirstPacket(true, nullptr);
|
||||
pia->FirstPacket(false, nullptr);
|
||||
pia->FirstPacket(true, TransportProto::TRANSPORT_TCP);
|
||||
pia->FirstPacket(false, TransportProto::TRANSPORT_TCP);
|
||||
|
||||
int remaining_in_content_line = content_line_resp->GetDeliverStreamRemainingLength();
|
||||
if ( remaining_in_content_line > 0 ) {
|
||||
|
@ -1396,8 +1396,8 @@ void HTTP_Analyzer::HTTP_Upgrade() {
|
|||
upgrade_protocol.c_str());
|
||||
pia = new analyzer::pia::PIA_TCP(Conn());
|
||||
if ( AddChildAnalyzer(pia) ) {
|
||||
pia->FirstPacket(true, nullptr);
|
||||
pia->FirstPacket(false, nullptr);
|
||||
pia->FirstPacket(true, TransportProto::TRANSPORT_TCP);
|
||||
pia->FirstPacket(false, TransportProto::TRANSPORT_TCP);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -169,52 +169,112 @@ 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 struct ip* ip4 = nullptr;
|
||||
static struct ip* ip4_tcp = nullptr;
|
||||
static struct ip* ip4_udp = 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 ) {
|
||||
if ( ! ip && proto ) { // proto needed here to avoid GCC warning that it may be used uninitialized
|
||||
// Create a dummy packet. Not very elegant, but everything
|
||||
// else would be *really* ugly ...
|
||||
if ( ! ip4_hdr ) {
|
||||
ip4 = (struct ip*)dummy_packet;
|
||||
tcp4 = (struct tcphdr*)(dummy_packet + sizeof(struct ip));
|
||||
ip4->ip_len = sizeof(struct ip) + sizeof(struct tcphdr);
|
||||
ip4->ip_hl = sizeof(struct ip) >> 2;
|
||||
ip4->ip_p = IPPROTO_TCP;
|
||||
switch ( *proto ) {
|
||||
case TransportProto::TRANSPORT_TCP: {
|
||||
DBG_LOG(DBG_ANALYZER, "PIA/TCP FirstPacket(%s)", (is_orig ? "T" : "F"));
|
||||
|
||||
// Cast to const so that it doesn't delete it.
|
||||
ip4_hdr = new IP_Hdr(ip4, false);
|
||||
if ( ! ip4_tcp_hdr ) {
|
||||
ip4_tcp = (struct ip*)dummy_packet;
|
||||
tcp4 = (struct tcphdr*)(dummy_packet + sizeof(struct ip));
|
||||
ip4_tcp->ip_len = sizeof(struct ip) + sizeof(struct tcphdr);
|
||||
ip4_tcp->ip_hl = sizeof(struct ip) >> 2;
|
||||
|
||||
// Cast to const so that it doesn't delete it.
|
||||
ip4_tcp_hdr = new IP_Hdr(ip4_tcp, 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);
|
||||
tcp4->th_sport = htons(Conn()->OrigPort());
|
||||
tcp4->th_dport = htons(Conn()->RespPort());
|
||||
}
|
||||
else {
|
||||
Conn()->RespAddr().CopyIPv4(&tmp_src);
|
||||
Conn()->OrigAddr().CopyIPv4(&tmp_dst);
|
||||
tcp4->th_sport = htons(Conn()->RespPort());
|
||||
tcp4->th_dport = htons(Conn()->OrigPort());
|
||||
}
|
||||
|
||||
ip4_tcp->ip_src = tmp_src;
|
||||
ip4_tcp->ip_dst = tmp_dst;
|
||||
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");
|
||||
}
|
||||
|
||||
// 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);
|
||||
tcp4->th_sport = htons(Conn()->OrigPort());
|
||||
tcp4->th_dport = htons(Conn()->RespPort());
|
||||
}
|
||||
else {
|
||||
Conn()->RespAddr().CopyIPv4(&tmp_src);
|
||||
Conn()->OrigAddr().CopyIPv4(&tmp_dst);
|
||||
tcp4->th_sport = htons(Conn()->RespPort());
|
||||
tcp4->th_dport = htons(Conn()->OrigPort());
|
||||
}
|
||||
|
||||
ip4->ip_src = tmp_src;
|
||||
ip4->ip_dst = tmp_dst;
|
||||
ip = ip4_hdr;
|
||||
}
|
||||
|
||||
assert(ip);
|
||||
|
||||
if ( ! MatcherInitialized(is_orig) )
|
||||
DoMatch((const u_char*)"", 0, is_orig, true, false, false, ip);
|
||||
}
|
||||
|
|
|
@ -36,6 +36,32 @@ public:
|
|||
|
||||
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
|
||||
// as pointer to an 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,
|
||||
const IP_Hdr* ip = nullptr);
|
||||
|
||||
auto Conn() const { return conn; }
|
||||
void SetConn(Connection* c) { conn = c; }
|
||||
|
||||
Buffer pkt_buffer;
|
||||
|
||||
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;
|
||||
Connection* conn;
|
||||
DataBlock current_packet;
|
||||
|
@ -123,16 +153,6 @@ public:
|
|||
|
||||
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);
|
||||
|
||||
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 ) {
|
||||
pia = new analyzer::pia::PIA_TCP(Conn());
|
||||
if ( AddChildAnalyzer(pia) ) {
|
||||
pia->FirstPacket(true, nullptr);
|
||||
pia->FirstPacket(false, nullptr);
|
||||
pia->FirstPacket(true, TransportProto::TRANSPORT_TCP);
|
||||
pia->FirstPacket(false, TransportProto::TRANSPORT_TCP);
|
||||
}
|
||||
else
|
||||
pia = nullptr;
|
||||
|
|
|
@ -357,8 +357,8 @@ void SSL_Analyzer::ForwardDecryptedData(const std::vector<u_char>& data, bool is
|
|||
if ( ! pia ) {
|
||||
pia = new analyzer::pia::PIA_TCP(Conn());
|
||||
if ( AddChildAnalyzer(pia) ) {
|
||||
pia->FirstPacket(true, nullptr);
|
||||
pia->FirstPacket(false, nullptr);
|
||||
pia->FirstPacket(true, TransportProto::TRANSPORT_TCP);
|
||||
pia->FirstPacket(false, TransportProto::TRANSPORT_TCP);
|
||||
}
|
||||
else
|
||||
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());
|
||||
if ( effective_analyzer->AddChildAnalyzer(pia) ) {
|
||||
pia->FirstPacket(true, nullptr);
|
||||
pia->FirstPacket(false, nullptr);
|
||||
pia->FirstPacket(true, TransportProto::TRANSPORT_TCP);
|
||||
pia->FirstPacket(false, TransportProto::TRANSPORT_TCP);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include "zeek/spicy/runtime-support.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <hilti/rt/exception.h>
|
||||
|
@ -10,6 +9,7 @@
|
|||
#include <hilti/rt/types/port.h>
|
||||
#include <hilti/rt/util.h>
|
||||
|
||||
#include "net_util.h"
|
||||
#include "zeek/Event.h"
|
||||
#include "zeek/analyzer/Manager.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");
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
if ( analyzer ) {
|
||||
protocol_handle_get_or_create(*analyzer);
|
||||
protocol_handle_get_or_create(*analyzer, proto);
|
||||
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());
|
||||
assert(cookie);
|
||||
|
||||
|
@ -501,36 +505,47 @@ void rt::protocol_begin(const std::optional<std::string>& analyzer) {
|
|||
if ( ! c )
|
||||
throw ValueUnavailable("no current connection available");
|
||||
|
||||
// Use a Zeek PIA stream analyzer performing DPD.
|
||||
auto pia_tcp = std::make_unique<analyzer::pia::PIA_TCP>(c->analyzer->Conn());
|
||||
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());
|
||||
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->ForwardStream(0, reinterpret_cast<const u_char*>(c->analyzer), true);
|
||||
c->analyzer->ForwardStream(0, reinterpret_cast<const u_char*>(c->analyzer), false);
|
||||
c->analyzer->CleanupChildren();
|
||||
|
||||
// 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.
|
||||
if ( auto child = c->analyzer->GetChildAnalyzer(pia_tcp->GetAnalyzerName()) )
|
||||
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: {
|
||||
// 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 cookie = static_cast<Cookie*>(hilti::rt::context::cookie());
|
||||
assert(cookie);
|
||||
|
@ -539,56 +554,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<const u_char*>(c->analyzer), true);
|
||||
c->analyzer->ForwardStream(0, reinterpret_cast<const u_char*>(c->analyzer), false);
|
||||
switch ( proto.value() ) {
|
||||
case ::hilti::rt::Protocol::TCP: {
|
||||
c->analyzer->CleanupChildren();
|
||||
|
||||
// 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.
|
||||
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));
|
||||
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<packet_analysis::TCP::TCPSessionAdapter>(c->analyzer->Conn());
|
||||
static_cast<analyzer::Analyzer*>(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<packet_analysis::TCP::TCPSessionAdapter>(c->analyzer->Conn());
|
||||
static_cast<analyzer::Analyzer*>(c->fake_tcp.get())
|
||||
->Done(); // will never see packets; cast to get around protected inheritance
|
||||
}
|
||||
|
||||
return rt::ProtocolHandle(child->GetID(), proto);
|
||||
}
|
||||
|
||||
case ::hilti::rt::Protocol::UDP: {
|
||||
c->analyzer->CleanupChildren();
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
||||
auto* child_as_tcp = dynamic_cast<analyzer::tcp::TCP_ApplicationAnalyzer*>(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<rt::ProtocolHandle>& 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<rt::ProtocolHandle>& h) {
|
||||
auto _ = hilti::rt::profiler::start("zeek/rt/protocol_data_in");
|
||||
auto cookie = static_cast<Cookie*>(hilti::rt::context::cookie());
|
||||
assert(cookie);
|
||||
|
@ -597,25 +629,85 @@ 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<const u_char*>(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<const u_char*>(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: {
|
||||
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,
|
||||
|
@ -628,22 +720,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 +776,40 @@ 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: {
|
||||
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) {
|
||||
|
|
|
@ -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<uint64_t> _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<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
|
||||
* 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<ProtocolHandle>& 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
|
||||
|
|
|
@ -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