Add component API to transparently remap one component to another one.

When a specific component is requested through its tag or name, one
can now have the component manager transparently return a different
one that has been registered to replace the original one. We limit
this to disabled components to avoid unnecessary confusion. That also
means that remappings are currently only supported for analyzers
(because other types of components cannot be disabled for now, per the
previous change).
This commit is contained in:
Robin Sommer 2024-04-30 08:29:54 +02:00
parent ac1a7508ee
commit 5d0c61e68b
No known key found for this signature in database
GPG key ID: D8187293B3FFE5D0
7 changed files with 123 additions and 32 deletions

View file

@ -801,4 +801,30 @@ TEST_SUITE("Analyzer management") {
CHECK(conn->FindAnalyzer("IMAP"));
conn->Done();
}
TEST_CASE("Analyzer mapping") {
REQUIRE(zeek::analyzer_mgr);
zeek::Packet p;
zeek::ConnTuple t;
auto conn = std::make_unique<zeek::Connection>(zeek::detail::ConnKey(t), 0, &t, 0, &p);
auto ssh = zeek::analyzer_mgr->InstantiateAnalyzer("SSH", conn.get());
REQUIRE(ssh);
auto imap = zeek::analyzer_mgr->InstantiateAnalyzer("IMAP", conn.get());
REQUIRE(imap);
zeek::analyzer_mgr->AddComponentMapping(ssh->GetAnalyzerTag(), imap->GetAnalyzerTag());
zeek::analyzer_mgr->DisableAnalyzer(ssh->GetAnalyzerTag()); // needs to be disabled for mapping to take effect
auto ssh_is_imap = zeek::analyzer_mgr->InstantiateAnalyzer("SSH", conn.get());
CHECK_EQ(ssh_is_imap->GetAnalyzerTag(), imap->GetAnalyzerTag()); // SSH is now IMAP
// orderly cleanup through connection
auto* tcp = new zeek::packet_analysis::TCP::TCPSessionAdapter(conn.get());
conn->SetSessionAdapter(tcp, nullptr);
tcp->AddChildAnalyzer(ssh);
tcp->AddChildAnalyzer(imap);
tcp->AddChildAnalyzer(ssh_is_imap);
conn->Done();
}
}

View file

@ -110,7 +110,7 @@ void Manager::DumpDebug() {
void Manager::Done() {}
bool Manager::EnableAnalyzer(const zeek::Tag& tag) {
Component* p = Lookup(tag);
Component* p = Lookup(tag, false);
if ( ! p )
return false;
@ -122,7 +122,7 @@ bool Manager::EnableAnalyzer(const zeek::Tag& tag) {
}
bool Manager::EnableAnalyzer(EnumVal* val) {
Component* p = Lookup(val);
Component* p = Lookup(val, false);
if ( ! p )
return false;
@ -134,7 +134,7 @@ bool Manager::EnableAnalyzer(EnumVal* val) {
}
bool Manager::DisableAnalyzer(const zeek::Tag& tag) {
Component* p = Lookup(tag);
Component* p = Lookup(tag, false);
if ( ! p )
return false;

View file

@ -41,7 +41,7 @@ function Files::__set_reassembly_buffer%(file_id: string, max: count%): bool
## :zeek:see:`Files::enable_analyzer`.
function Files::__enable_analyzer%(tag: Files::Tag%) : bool
%{
auto c = zeek::file_mgr->Lookup(tag->AsEnumVal());
auto c = zeek::file_mgr->Lookup(tag->AsEnumVal(), false);
if ( ! c )
return zeek::val_mgr->False();
@ -53,7 +53,7 @@ function Files::__enable_analyzer%(tag: Files::Tag%) : bool
## :zeek:see:`Files::disable_analyzer`.
function Files::__disable_analyzer%(tag: Files::Tag%) : bool
%{
auto c = zeek::file_mgr->Lookup(tag->AsEnumVal());
auto c = zeek::file_mgr->Lookup(tag->AsEnumVal(), false);
if ( ! c )
return zeek::val_mgr->False();

View file

@ -21,7 +21,7 @@ void Component::SetEnabled(bool arg_enabled) {
plugin::Component::SetEnabled(arg_enabled);
// If we already have instantiated an analyzer, update its state.
if ( auto analyzer = packet_mgr->Lookup(Tag().AsVal().get()) )
if ( auto analyzer = packet_mgr->Lookup(Tag().AsVal().get(), false) )
analyzer->SetEnabled(arg_enabled);
}

View file

@ -82,14 +82,14 @@ AnalyzerPtr Manager::GetAnalyzer(const std::string& name) {
}
bool Manager::EnableAnalyzer(EnumVal* tag) {
Component* c = Lookup(tag);
Component* c = Lookup(tag, false);
c->SetEnabled(true);
return true;
}
bool Manager::DisableAnalyzer(EnumVal* tag) {
Component* c = Lookup(tag);
Component* c = Lookup(tag, false);
c->SetEnabled(false);
return true;

View file

@ -119,24 +119,59 @@ public:
/**
* @param name The canonical name of a component.
* @param consider_remappings If true, component mappings will be honored
* if the original component is disabled.
* @return The component associated with the name or a null pointer if no
* such component exists.
*/
C* Lookup(const std::string& name) const;
C* Lookup(const std::string& name, bool consider_remappings = true) const;
/**
* @param name A component tag.
* @param consider_remappings If true, component mappings will be honored
* if the original component is disabled.
* @return The component associated with the tag or a null pointer if no
* such component exists.
*/
C* Lookup(const zeek::Tag& tag) const;
C* Lookup(const zeek::Tag& tag, bool consider_remappings = true) const;
/**
* @param name A component's enum value.
* @param consider_remappings If true, component mappings will be honored
* if the original component is disabled.
* @return The component associated with the value or a null pointer if no
* such component exists.
*/
C* Lookup(EnumVal* val) const;
C* Lookup(EnumVal* val, bool consider_remappings = true) const;
/**
* Registers a mapping of a component to another one that will be honored
* by the `Lookup()` methods if (and only if) the original is currently
* disabled.
*
* @param old The original component tag.
* @param new_ The new component tag.
*/
void AddComponentMapping(const zeek::Tag& old, const zeek::Tag& new_) {
if ( old != new_ ) {
component_mapping_by_src[old] = new_;
component_mapping_by_dst[new_] = old;
}
}
/**
* Returns true if a given component has a mapping to different one in place.
*
* @param tag The component tag to check.
*/
auto HasComponentMapping(const zeek::Tag& tag) const { return component_mapping_by_src.count(tag); }
/**
* Returns true if a given component is mapped to from a different one.
*
* @param tag The component tag to check.
*/
bool ProvidesComponentMapping(const zeek::Tag& tag) const { return component_mapping_by_dst.count(tag); }
private:
/** Script layer module in which component tags live. */
@ -150,6 +185,8 @@ private:
std::map<std::string, C*> components_by_name;
std::map<zeek::Tag, C*> components_by_tag;
std::map<zeek_int_t, C*> components_by_val;
std::map<zeek::Tag, zeek::Tag> component_mapping_by_src;
std::map<zeek::Tag, zeek::Tag> component_mapping_by_dst;
};
template<class C>
@ -204,7 +241,7 @@ const std::string& ComponentManager<C>::GetComponentName(zeek::Tag tag) const {
if ( ! tag )
return error;
if ( C* c = Lookup(tag) )
if ( C* c = Lookup(tag, false) ) // use actual, not remapped name
return c->CanonicalName();
reporter->InternalWarning("requested name of unknown component tag %s", tag.AsString().c_str());
@ -266,21 +303,48 @@ zeek::Tag ComponentManager<C>::GetComponentTag(Val* v) const {
}
template<class C>
C* ComponentManager<C>::Lookup(const std::string& name) const {
typename std::map<std::string, C*>::const_iterator i = components_by_name.find(util::to_upper(name));
return i != components_by_name.end() ? i->second : nullptr;
C* ComponentManager<C>::Lookup(const std::string& name, bool consider_remappings) const {
if ( auto i = components_by_name.find(util::to_upper(name)); i != components_by_name.end() ) {
auto c = (*i).second;
if ( consider_remappings && ! c->Enabled() ) {
if ( auto j = component_mapping_by_src.find(c->Tag()); j != component_mapping_by_src.end() )
return Lookup(j->second, false);
}
return c;
}
else
return nullptr;
}
template<class C>
C* ComponentManager<C>::Lookup(const zeek::Tag& tag) const {
typename std::map<zeek::Tag, C*>::const_iterator i = components_by_tag.find(tag);
return i != components_by_tag.end() ? i->second : nullptr;
C* ComponentManager<C>::Lookup(const zeek::Tag& tag, bool consider_remappings) const {
if ( auto i = components_by_tag.find(tag); i != components_by_tag.end() ) {
auto c = (*i).second;
if ( consider_remappings && ! c->Enabled() ) {
if ( auto j = component_mapping_by_src.find(c->Tag()); j != component_mapping_by_src.end() )
return Lookup(j->second, false);
}
return c;
}
else
return nullptr;
}
template<class C>
C* ComponentManager<C>::Lookup(EnumVal* val) const {
typename std::map<zeek_int_t, C*>::const_iterator i = components_by_val.find(val->InternalInt());
return i != components_by_val.end() ? i->second : nullptr;
C* ComponentManager<C>::Lookup(EnumVal* val, bool consider_remappings) const {
if ( auto i = components_by_val.find(val->InternalInt()); i != components_by_val.end() ) {
auto c = (*i).second;
if ( consider_remappings && ! c->Enabled() ) {
if ( auto j = component_mapping_by_src.find(c->Tag()); j != component_mapping_by_src.end() )
return Lookup(j->second, false);
}
return c;
}
else
return nullptr;
}
template<class C>

View file

@ -375,8 +375,9 @@ bool Manager::toggleFileAnalyzer(const Tag& tag, bool enable) {
// not set -> not ours
return false;
file_analysis::Component* component = file_mgr->Lookup(tag);
file_analysis::Component* component_replaces = analyzer.replaces ? file_mgr->Lookup(analyzer.replaces) : nullptr;
file_analysis::Component* component = file_mgr->Lookup(tag, false);
file_analysis::Component* component_replaces =
analyzer.replaces ? file_mgr->Lookup(analyzer.replaces, false) : nullptr;
if ( ! component ) {
// Shouldn't really happen.
@ -418,9 +419,9 @@ bool Manager::togglePacketAnalyzer(const Tag& tag, bool enable) {
// not set -> not ours
return false;
packet_analysis::Component* component = packet_mgr->Lookup(tag);
packet_analysis::Component* component = packet_mgr->Lookup(tag, false);
packet_analysis::Component* component_replaces =
analyzer.replaces ? packet_mgr->Lookup(analyzer.replaces) : nullptr;
analyzer.replaces ? packet_mgr->Lookup(analyzer.replaces, false) : nullptr;
if ( ! component ) {
// Shouldn't really happen.
@ -452,21 +453,21 @@ bool Manager::togglePacketAnalyzer(const Tag& tag, bool enable) {
bool Manager::toggleAnalyzer(EnumVal* tag, bool enable) {
if ( tag->GetType() == analyzer_mgr->GetTagType() ) {
if ( auto analyzer = analyzer_mgr->Lookup(tag) )
if ( auto analyzer = analyzer_mgr->Lookup(tag, false) )
return toggleProtocolAnalyzer(analyzer->Tag(), enable);
else
return false;
}
if ( tag->GetType() == file_mgr->GetTagType() ) {
if ( auto analyzer = file_mgr->Lookup(tag) )
if ( auto analyzer = file_mgr->Lookup(tag, false) )
return toggleFileAnalyzer(analyzer->Tag(), enable);
else
return false;
}
if ( tag->GetType() == packet_mgr->GetTagType() ) {
if ( auto analyzer = packet_mgr->Lookup(tag) )
if ( auto analyzer = packet_mgr->Lookup(tag, false) )
return togglePacketAnalyzer(analyzer->Tag(), enable);
else
return false;
@ -893,7 +894,7 @@ void Manager::disableReplacedAnalyzers() {
auto replaces = info.name_replaces.c_str();
if ( file_mgr->Lookup(replaces) || packet_mgr->Lookup(replaces) )
if ( file_mgr->Lookup(replaces, false) || packet_mgr->Lookup(replaces, false) )
reporter->FatalError("cannot replace '%s' analyzer with a protocol analyzer", replaces);
auto tag = analyzer_mgr->GetAnalyzerTag(replaces);
@ -915,10 +916,10 @@ void Manager::disableReplacedAnalyzers() {
auto replaces = info.name_replaces.c_str();
if ( analyzer_mgr->Lookup(replaces) || packet_mgr->Lookup(replaces) )
if ( analyzer_mgr->Lookup(replaces, false) || packet_mgr->Lookup(replaces, false) )
reporter->FatalError("cannot replace '%s' analyzer with a file analyzer", replaces);
auto component = file_mgr->Lookup(replaces);
auto component = file_mgr->Lookup(replaces, false);
if ( ! component ) {
SPICY_DEBUG(hilti::rt::fmt("%s is supposed to replace file analyzer %s, but that does not exist",
info.name_analyzer, replaces));
@ -937,7 +938,7 @@ void Manager::disableReplacedAnalyzers() {
auto replaces = info.name_replaces.c_str();
auto component = packet_mgr->Lookup(replaces);
auto component = packet_mgr->Lookup(replaces, false);
if ( ! component ) {
SPICY_DEBUG(hilti::rt::fmt("%s is supposed to replace packet analyzer %s, but that does not exist",
info.name_analyzer, replaces));