From a7d3cb48efdb517a3227e7398ced7da31ef56e81 Mon Sep 17 00:00:00 2001 From: Tim Wojtulewicz Date: Fri, 1 Oct 2021 12:54:27 -0700 Subject: [PATCH] Add concept of "parent" tag namespaces This allows us to create an EnumType that groups all of the analyzer tag values into a single type, while still having the existing types that split them up. We can then use this for certain events that benefit from taking all of the tag types at once. --- scripts/base/frameworks/analyzer/main.zeek | 11 +++- src/analyzer/Manager.cc | 5 +- src/analyzer/analyzer.bif | 27 ++++++++-- src/file_analysis/Manager.cc | 4 +- src/packet_analysis/Manager.cc | 3 +- src/plugin/Component.cc | 2 +- src/plugin/Component.h | 2 +- src/plugin/ComponentManager.h | 52 ++++++++++++++++--- src/util.cc | 16 ++++++ src/util.h | 3 ++ .../output | 7 +++ .../base/frameworks/analyzer/tags.zeek | 36 +++++++++++++ 12 files changed, 147 insertions(+), 21 deletions(-) create mode 100644 testing/btest/Baseline/scripts.base.frameworks.analyzer.tags/output create mode 100644 testing/btest/scripts/base/frameworks/analyzer/tags.zeek diff --git a/scripts/base/frameworks/analyzer/main.zeek b/scripts/base/frameworks/analyzer/main.zeek index 54ba82178d..e4d740979a 100644 --- a/scripts/base/frameworks/analyzer/main.zeek +++ b/scripts/base/frameworks/analyzer/main.zeek @@ -9,6 +9,13 @@ ##! These tags are defined internally by ##! the analyzers themselves, and documented in their analyzer-specific ##! description along with the events that they generate. +##! +##! Analyzer tags are also inserted into a global :zeek:type:`AllAnalyzers` enum +##! type. This type contains duplicates of all of the :zeek:type:`Analyzer::Tag`, +##! :zeek:type:`PacketAnalyzer::Tag` and :zeek:type:`Files::Tag` enum values +##! and can be used for arguments to function/hook/event definitions where they +##! need to handle any analyzer type. See :zeek:id:`Analyzer::register_for_ports` +##! for an example. @load base/frameworks/packet-filter/utils @@ -186,12 +193,12 @@ function all_registered_ports(): table[Analyzer::Tag] of set[port] return ports; } -function name(atype: Analyzer::Tag) : string +function name(atype: AllAnalyzers::Tag) : string { return __name(atype); } -function get_tag(name: string): Analyzer::Tag +function get_tag(name: string): AllAnalyzers::Tag { return __tag(name); } diff --git a/src/analyzer/Manager.cc b/src/analyzer/Manager.cc index 70d4847931..ba21821a84 100644 --- a/src/analyzer/Manager.cc +++ b/src/analyzer/Manager.cc @@ -56,7 +56,10 @@ bool Manager::ConnIndex::operator<(const ConnIndex& other) const return false; } -Manager::Manager() : plugin::ComponentManager("Analyzer", "Tag") { } +Manager::Manager() + : plugin::ComponentManager("Analyzer", "Tag", "AllAnalyzers") + { + } Manager::~Manager() { diff --git a/src/analyzer/analyzer.bif b/src/analyzer/analyzer.bif index 9e8018342f..0bd666fac6 100644 --- a/src/analyzer/analyzer.bif +++ b/src/analyzer/analyzer.bif @@ -38,14 +38,31 @@ function Analyzer::__schedule_analyzer%(orig: addr, resp: addr, resp_p: port, return zeek::val_mgr->True(); %} -function __name%(atype: Analyzer::Tag%) : string +function __name%(atype: AllAnalyzers::Tag%) : string %{ - const auto& n = zeek::analyzer_mgr->GetComponentName(zeek::IntrusivePtr{zeek::NewRef{}, atype->AsEnumVal()}); - return zeek::make_intrusive(n); + auto val = atype->AsEnumVal(); + + plugin::Component* component = zeek::analyzer_mgr->Lookup(val); + if ( ! component ) + component = zeek::packet_mgr->Lookup(val); + if ( ! component ) + component = zeek::file_mgr->Lookup(val); + if ( ! component ) + return zeek::make_intrusive(""); + + return zeek::make_intrusive(component->CanonicalName()); %} -function __tag%(name: string%) : Analyzer::Tag +function __tag%(name: string%) : AllAnalyzers::Tag %{ - zeek::Tag t = zeek::analyzer_mgr->GetComponentTag(name->CheckString()); + auto val = name->CheckString(); + + plugin::Component* component = zeek::analyzer_mgr->Lookup(val); + if ( ! component ) + component = zeek::packet_mgr->Lookup(val); + if ( ! component ) + component = zeek::file_mgr->Lookup(val); + + zeek::Tag t = component ? component->Tag() : zeek::Tag(); return t.AsVal(); %} diff --git a/src/file_analysis/Manager.cc b/src/file_analysis/Manager.cc index c8a48c5802..b05545dd23 100644 --- a/src/file_analysis/Manager.cc +++ b/src/file_analysis/Manager.cc @@ -19,8 +19,8 @@ namespace zeek::file_analysis { Manager::Manager() - : plugin::ComponentManager("Files", "Tag"), current_file_id(), - magic_state(), cumulative_files(0), max_files(0) + : plugin::ComponentManager("Files", "Tag", "AllAnalyzers"), + current_file_id(), magic_state(), cumulative_files(0), max_files(0) { } diff --git a/src/packet_analysis/Manager.cc b/src/packet_analysis/Manager.cc index dfc2ac8dd9..74cb92010a 100644 --- a/src/packet_analysis/Manager.cc +++ b/src/packet_analysis/Manager.cc @@ -13,7 +13,8 @@ using namespace zeek::packet_analysis; -Manager::Manager() : plugin::ComponentManager("PacketAnalyzer", "Tag") +Manager::Manager() + : plugin::ComponentManager("PacketAnalyzer", "Tag", "AllAnalyzers") { } diff --git a/src/plugin/Component.cc b/src/plugin/Component.cc index 9a1d6d2bbd..b2fafbad0c 100644 --- a/src/plugin/Component.cc +++ b/src/plugin/Component.cc @@ -11,7 +11,7 @@ namespace zeek::plugin Tag::type_t Component::type_counter(0); Component::Component(component::Type arg_type, const std::string& arg_name, - Tag::subtype_t tag_subtype, zeek::EnumTypePtr etype) + Tag::subtype_t tag_subtype, EnumTypePtr etype) : type(arg_type), name(arg_name), tag(etype, 1, 0), etype(std::move(etype)), tag_subtype(tag_subtype) { diff --git a/src/plugin/Component.h b/src/plugin/Component.h index ba7b5d3267..c62ad88cb5 100644 --- a/src/plugin/Component.h +++ b/src/plugin/Component.h @@ -63,7 +63,7 @@ public: * script-land. */ Component(component::Type type, const std::string& name, Tag::subtype_t tag_subtype = 0, - zeek::EnumTypePtr etype = nullptr); + EnumTypePtr etype = nullptr); /** * Destructor. diff --git a/src/plugin/ComponentManager.h b/src/plugin/ComponentManager.h index c8a5b6e492..f2485ecad8 100644 --- a/src/plugin/ComponentManager.h +++ b/src/plugin/ComponentManager.h @@ -4,13 +4,16 @@ #include #include +#include "zeek/Attr.h" #include "zeek/DebugLogger.h" +#include "zeek/Expr.h" #include "zeek/Reporter.h" #include "zeek/Scope.h" #include "zeek/Tag.h" #include "zeek/Type.h" #include "zeek/Val.h" #include "zeek/Var.h" // for add_type() +#include "zeek/module_util.h" #include "zeek/zeekygen/Manager.h" namespace zeek::plugin @@ -36,7 +39,8 @@ public: * @param local_id The local part of the ID of the new enum type * (e.g., "Tag"). */ - ComponentManager(const std::string& module, const std::string& local_id); + ComponentManager(const std::string& module, const std::string& local_id, + const std::string& parent_module = ""); /** * @return The script-layer module in which the component's "Tag" ID lives. @@ -120,20 +124,45 @@ public: C* Lookup(EnumVal* val) const; private: - std::string module; /**< Script layer module in which component tags live. */ - EnumTypePtr tag_enum_type; /**< Enum type of component tags. */ + /** Script layer module in which component tags live. */ + std::string module; + std::string parent_module; + + /** Module-local type of component tags. */ + EnumTypePtr tag_enum_type; + EnumTypePtr parent_tag_enum_type; + std::map components_by_name; std::map components_by_tag; std::map components_by_val; }; template -ComponentManager::ComponentManager(const std::string& arg_module, const std::string& local_id) - : module(arg_module), tag_enum_type(make_intrusive(module + "::" + local_id)) +ComponentManager::ComponentManager(const std::string& module, const std::string& local_id, + const std::string& parent_module) + : module(module), parent_module(parent_module) { + tag_enum_type = make_intrusive(module + "::" + local_id); auto id = zeek::detail::install_ID(local_id.c_str(), module.c_str(), true, true); zeek::detail::add_type(id.get(), tag_enum_type, nullptr); zeek::detail::zeekygen_mgr->Identifier(std::move(id)); + + if ( ! parent_module.empty() ) + { + // check to see if the parent module's type has been created already + id = zeek::detail::lookup_ID(local_id.c_str(), parent_module.c_str(), false, true, false); + if ( id != zeek::detail::ID::nil ) + { + parent_tag_enum_type = id->GetType(); + } + else + { + parent_tag_enum_type = make_intrusive(parent_module + "::" + local_id); + id = zeek::detail::install_ID(local_id.c_str(), parent_module.c_str(), true, true); + zeek::detail::add_type(id.get(), parent_tag_enum_type, nullptr); + zeek::detail::zeekygen_mgr->Identifier(std::move(id)); + } + } } template const std::string& ComponentManager::GetModule() const @@ -194,19 +223,19 @@ template C* ComponentManager::Lookup(const std::string& name) const { typename std::map::const_iterator i = components_by_name.find( util::to_upper(name)); - return i != components_by_name.end() ? i->second : 0; + return i != components_by_name.end() ? i->second : nullptr; } template C* ComponentManager::Lookup(const zeek::Tag& tag) const { typename std::map::const_iterator i = components_by_tag.find(tag); - return i != components_by_tag.end() ? i->second : 0; + return i != components_by_tag.end() ? i->second : nullptr; } template C* ComponentManager::Lookup(EnumVal* val) const { typename std::map::const_iterator i = components_by_val.find(val->InternalInt()); - return i != components_by_val.end() ? i->second : 0; + return i != components_by_val.end() ? i->second : nullptr; } template @@ -229,6 +258,13 @@ void ComponentManager::RegisterComponent(C* component, const std::string& pre std::string id = util::fmt("%s%s", prefix.c_str(), cname.c_str()); tag_enum_type->AddName(module, id.c_str(), component->Tag().AsVal()->InternalInt(), true, nullptr); + + if ( parent_tag_enum_type ) + { + std::string parent_id = util::fmt("%s_%s", util::strtoupper(module).c_str(), id.c_str()); + parent_tag_enum_type->AddName(parent_module, parent_id.c_str(), + component->Tag().AsVal()->InternalInt(), true, nullptr); + } } } // namespace zeek::plugin diff --git a/src/util.cc b/src/util.cc index f63dfdbc83..ce8026ca2e 100644 --- a/src/util.cc +++ b/src/util.cc @@ -1537,6 +1537,22 @@ std::string strtolower(const std::string& s) return t; } +TEST_CASE("util strtoupper") + { + const char* a = "aBcD"; + CHECK(strtoupper(a) == "ABCD"); + + std::string b = "aBcD"; + CHECK(strtoupper(b) == "ABCD"); + } + +std::string strtoupper(const std::string& s) + { + std::string t = s; + std::transform(t.begin(), t.end(), t.begin(), ::toupper); + return t; + } + TEST_CASE("util fmt_bytes") { const char* a = "abcd"; diff --git a/src/util.h b/src/util.h index c35da260eb..019162a7bb 100644 --- a/src/util.h +++ b/src/util.h @@ -342,6 +342,9 @@ extern std::string strstrip(std::string s); // Return a lower-cased version of the string. extern std::string strtolower(const std::string& s); +// Return a upper-cased version of the string. +extern std::string strtoupper(const std::string& s); + extern int fputs(int len, const char* s, FILE* fp); extern bool is_printable(const char* s, int len); diff --git a/testing/btest/Baseline/scripts.base.frameworks.analyzer.tags/output b/testing/btest/Baseline/scripts.base.frameworks.analyzer.tags/output new file mode 100644 index 0000000000..184cf06fa8 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.analyzer.tags/output @@ -0,0 +1,7 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +all, Analyzer::ANALYZER_DNS +analyzer, Analyzer::ANALYZER_DNS +all, PacketAnalyzer::ANALYZER_UDP +packet analyzer, PacketAnalyzer::ANALYZER_UDP +all, Files::ANALYZER_X509 +file analyzer, Files::ANALYZER_X509 diff --git a/testing/btest/scripts/base/frameworks/analyzer/tags.zeek b/testing/btest/scripts/base/frameworks/analyzer/tags.zeek new file mode 100644 index 0000000000..66a6acfc48 --- /dev/null +++ b/testing/btest/scripts/base/frameworks/analyzer/tags.zeek @@ -0,0 +1,36 @@ +# @TEST-EXEC: zeek %INPUT > output +# @TEST-EXEC: btest-diff output + +# Validate that we can pass the individual Tag types into functions that +# take both their own Tag type as well the AllAnalyzers type. + +global test2: function(a: Analyzer::Tag); +global test3: function(a: PacketAnalyzer::Tag); +global test4: function(a: Files::Tag); + +function test1(a: AllAnalyzers::Tag) { + print "all", a; +} + +function test2(a: Analyzer::Tag) { + print "analyzer", a; +} + +function test3(a: PacketAnalyzer::Tag) { + print "packet analyzer", a; +} + +function test4(a: Files::Tag) { + print "file analyzer", a; +} + +event zeek_init() { + test1(Analyzer::ANALYZER_DNS); + test2(Analyzer::ANALYZER_DNS); + + test1(PacketAnalyzer::ANALYZER_UDP); + test3(PacketAnalyzer::ANALYZER_UDP); + + test1(Files::ANALYZER_X509); + test4(Files::ANALYZER_X509); +} \ No newline at end of file