diff --git a/CHANGES b/CHANGES index f13c1e0fe3..c47654f94a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,15 @@ +2.5-350 | 2017-11-21 12:19:28 -0600 + + * Add HookReporter plugin hook function. + + This hook gives access to basically all information that is available in + the function in Reporter.cc that performs the logging. The hook is + called each time when anything passes through the reporter in the cases + in which an event usually would be called. This includes weirds. The + hook can return false to prevent the normal reporter events from being + raised. (Corelight) + 2.5-348 | 2017-11-21 11:30:55 -0600 * Fix a nb_dns.c compile error (older OSs) due to C90 vs C99. (Corelight) diff --git a/VERSION b/VERSION index ad68c02345..6fb178837c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.5-348 +2.5-350 diff --git a/src/Reporter.cc b/src/Reporter.cc index 4823b33ef3..eb89a29d30 100644 --- a/src/Reporter.cc +++ b/src/Reporter.cc @@ -10,6 +10,8 @@ #include "NetVar.h" #include "Net.h" #include "Conn.h" +#include "plugin/Plugin.h" +#include "plugin/Manager.h" #ifdef SYSLOG_INT extern "C" { @@ -323,7 +325,24 @@ void Reporter::DoLog(const char* prefix, EventHandlerPtr event, FILE* out, // buffer size above. safe_snprintf(buffer + strlen(buffer), size - strlen(buffer), " [%s]", postfix); - if ( event && via_events && ! in_error_handler ) + bool raise_event = true; + + if ( via_events && ! in_error_handler ) + { + if ( locations.size() ) + { + auto locs = locations.back(); + raise_event = PLUGIN_HOOK_WITH_RESULT(HOOK_REPORTER, + HookReporter(prefix, event, conn, addl, location, + locs.first, locs.second, time, buffer), true); + } + else + raise_event = PLUGIN_HOOK_WITH_RESULT(HOOK_REPORTER, + HookReporter(prefix, event, conn, addl, location, + nullptr, nullptr, time, buffer), true); + } + + if ( raise_event && event && via_events && ! in_error_handler ) { val_list* vl = new val_list; diff --git a/src/plugin/Manager.cc b/src/plugin/Manager.cc index 2f04354b76..836520d03a 100644 --- a/src/plugin/Manager.cc +++ b/src/plugin/Manager.cc @@ -841,6 +841,52 @@ bool Manager::HookLogWrite(const std::string& writer, return result; } +bool Manager::HookReporter(const std::string& prefix, const EventHandlerPtr event, + const Connection* conn, const val_list* addl, bool location, + const Location* location1, const Location* location2, + bool time, const std::string& message) + + { + HookArgumentList args; + + if ( HavePluginForHook(META_HOOK_PRE) ) + { + args.push_back(HookArgument(prefix)); + args.push_back(HookArgument(conn)); + args.push_back(HookArgument(addl)); + args.push_back(HookArgument(location1)); + args.push_back(HookArgument(location2)); + args.push_back(HookArgument(location)); + args.push_back(HookArgument(time)); + args.push_back(HookArgument(message)); + MetaHookPre(HOOK_REPORTER, args); + } + + hook_list* l = hooks[HOOK_REPORTER]; + + bool result = true; + + if ( l ) + { + for ( hook_list::iterator i = l->begin(); i != l->end(); ++i ) + { + Plugin* p = (*i).second; + + if ( ! p->HookReporter(prefix, event, conn, addl, location, location1, location2, time, message) ) + { + result = false; + break; + } + } + } + + if ( HavePluginForHook(META_HOOK_POST) ) + MetaHookPost(HOOK_REPORTER, args, HookArgument(result)); + + return result; + } + + void Manager::MetaHookPre(HookType hook, const HookArgumentList& args) const { hook_list* l = hooks[HOOK_CALL_FUNCTION]; diff --git a/src/plugin/Manager.h b/src/plugin/Manager.h index 7139121b97..61b8dc1047 100644 --- a/src/plugin/Manager.h +++ b/src/plugin/Manager.h @@ -355,6 +355,39 @@ public: int num_fields, const threading::Field* const* fields, threading::Value** vals) const; + /** + * Hook into reporting. This method will be called for each reporter call + * made; this includes weirds. The method cannot manipulate the data at + * the current time; however it is possible to prevent script-side events + * from being called by returning false. + * + * @param prefix The prefix passed by the reporter framework + * + * @param event The event to be called + * + * @param conn The associated connection + * + * @param addl Additional Bro values; typically will be passed to the event + * by the reporter framework. + * + * @param location True if event expects location information + * + * @param location1 First location + * + * @param location2 Second location + * + * @param time True if event expects time information + * + * @param message Message supplied by the reporter framework + * + * @return true if event should be called by the reporter framework, false + * if the event call should be skipped + */ + bool HookReporter(const std::string& prefix, const EventHandlerPtr event, + const Connection* conn, const val_list* addl, bool location, + const Location* location1, const Location* location2, + bool time, const std::string& message); + /** * Internal method that registers a freshly instantiated plugin with * the manager. diff --git a/src/plugin/Plugin.cc b/src/plugin/Plugin.cc index 502dc0b9e7..1264759d02 100644 --- a/src/plugin/Plugin.cc +++ b/src/plugin/Plugin.cc @@ -208,6 +208,16 @@ void HookArgument::Describe(ODesc* d) const d->Add("}"); } break; + + case LOCATION: + if ( arg.loc ) + { + arg.loc->Describe(d); + } + else + { + d->Add(""); + } } } @@ -393,6 +403,14 @@ bool Plugin::HookLogWrite(const std::string& writer, const std::string& filter, return true; } +bool Plugin::HookReporter(const std::string& prefix, const EventHandlerPtr event, + const Connection* conn, const val_list* addl, bool location, + const Location* location1, const Location* location2, + bool time, const std::string& message) + { + return true; + } + void Plugin::MetaHookPre(HookType hook, const HookArgumentList& args) { } diff --git a/src/plugin/Plugin.h b/src/plugin/Plugin.h index 9f5ccb592c..2cec7cf453 100644 --- a/src/plugin/Plugin.h +++ b/src/plugin/Plugin.h @@ -48,6 +48,7 @@ enum HookType { HOOK_SETUP_ANALYZER_TREE, //< Activates Plugin::HookAddToAnalyzerTree HOOK_LOG_INIT, //< Activates Plugin::HookLogInit HOOK_LOG_WRITE, //< Activates Plugin::HookLogWrite + HOOK_REPORTER, //< Activates Plugin::HookReporter // Meta hooks. META_HOOK_PRE, //< Activates Plugin::MetaHookPre(). @@ -172,7 +173,7 @@ public: */ enum Type { BOOL, DOUBLE, EVENT, FRAME, FUNC, FUNC_RESULT, INT, STRING, VAL, - VAL_LIST, VOID, VOIDP, WRITER_INFO, CONN, THREAD_FIELDS + VAL_LIST, VOID, VOIDP, WRITER_INFO, CONN, THREAD_FIELDS, LOCATION }; /** @@ -250,6 +251,11 @@ public: */ explicit HookArgument(const std::pair fpair) { type = THREAD_FIELDS; tfields = fpair; } + /** + * Constructor with a location argument. + */ + explicit HookArgument(const Location* location) { type = LOCATION; arg.loc = location; } + /** * Returns the value for a boolen argument. The argument's type must * match accordingly. @@ -360,6 +366,7 @@ private: const val_list* vals; const void* voidp; const logging::WriterBackend::WriterInfo* winfo; + const Location* loc; } arg; // Outside union because these have dtors. @@ -781,6 +788,39 @@ protected: const threading::Field* const* fields, threading::Value** vals); + /** + * Hook into reporting. This method will be called for each reporter call + * made; this includes weirds. The method cannot manipulate the data at + * the current time; however it is possible to prevent script-side events + * from being called by returning false. + * + * @param prefix The prefix passed by the reporter framework + * + * @param event The event to be called + * + * @param conn The associated connection + * + * @param addl Additional Bro values; typically will be passed to the event + * by the reporter framework. + * + * @param location True if event expects location information + * + * @param location1 First location + * + * @param location2 Second location + * + * @param time True if event expects time information + * + * @param message Message supplied by the reporter framework + * + * @return true if event should be called by the reporter framework, false + * if the event call should be skipped + */ + virtual bool HookReporter(const std::string& prefix, const EventHandlerPtr event, + const Connection* conn, const val_list* addl, bool location, + const Location* location1, const Location* location2, + bool time, const std::string& message); + // Meta hooks. /** diff --git a/testing/btest/Baseline/plugins.reporter-hook/output b/testing/btest/Baseline/plugins.reporter-hook/output new file mode 100644 index 0000000000..e5ed573e67 --- /dev/null +++ b/testing/btest/Baseline/plugins.reporter-hook/output @@ -0,0 +1,10 @@ + | Hook Some Info <...>/reporter-hook.bro, line 16 + | Hook error An Error <...>/reporter-hook.bro, line 18 + | Hook error An Error that does not show up in the log <...>/reporter-hook.bro, line 19 + | Hook expression error field value missing [b$a] <...>/reporter-hook.bro, line 23 + | Hook warning A warning <...>/reporter-hook.bro, line 17 +<...>/reporter-hook.bro, line 16: Some Info +error in <...>/reporter-hook.bro, line 18: An Error +error in <...>/reporter-hook.bro, line 19: An Error that does not show up in the log +expression error in <...>/reporter-hook.bro, line 23: field value missing [b$a] +warning in <...>/reporter-hook.bro, line 17: A warning diff --git a/testing/btest/Baseline/plugins.reporter-hook/reporter.log b/testing/btest/Baseline/plugins.reporter-hook/reporter.log new file mode 100644 index 0000000000..ab70b0c17a --- /dev/null +++ b/testing/btest/Baseline/plugins.reporter-hook/reporter.log @@ -0,0 +1,13 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path reporter +#open 2017-07-26-17-58-52 +#fields ts level message location +#types time enum string string +0.000000 Reporter::INFO Some Info /Users/johanna/corelight/bro/testing/btest/.tmp/plugins.reporter-hook/reporter-hook.bro, line 16 +0.000000 Reporter::WARNING A warning /Users/johanna/corelight/bro/testing/btest/.tmp/plugins.reporter-hook/reporter-hook.bro, line 17 +0.000000 Reporter::ERROR An Error /Users/johanna/corelight/bro/testing/btest/.tmp/plugins.reporter-hook/reporter-hook.bro, line 18 +0.000000 Reporter::ERROR field value missing [b$a] /Users/johanna/corelight/bro/testing/btest/.tmp/plugins.reporter-hook/reporter-hook.bro, line 23 +#close 2017-07-26-17-58-52 diff --git a/testing/btest/plugins/reporter-hook-plugin/.btest-ignore b/testing/btest/plugins/reporter-hook-plugin/.btest-ignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testing/btest/plugins/reporter-hook-plugin/src/Plugin.cc b/testing/btest/plugins/reporter-hook-plugin/src/Plugin.cc new file mode 100644 index 0000000000..9c8eee6ca8 --- /dev/null +++ b/testing/btest/plugins/reporter-hook-plugin/src/Plugin.cc @@ -0,0 +1,43 @@ + +#include "Plugin.h" + +#include +#include +#include +#include + +namespace plugin { namespace Reporter_Hook { Plugin plugin; } } + +using namespace plugin::Reporter_Hook; + +plugin::Configuration Plugin::Configure() + { + EnableHook(HOOK_REPORTER); + + plugin::Configuration config; + config.name = "Reporter::Hook"; + config.description = "Exercise Reporter Hook"; + config.version.major = 1; + config.version.minor = 0; + return config; + } + +bool Plugin::HookReporter(const std::string& prefix, const EventHandlerPtr event, + const Connection* conn, const val_list* addl, bool location, + const Location* location1, const Location* location2, + bool time, const std::string& message) + { + ODesc d; + if ( location1 ) + location1->Describe(&d); + if ( location2 ) + location2->Describe(&d); + + fprintf(stderr, " | Hook %s %s %s\n", prefix.c_str(), message.c_str(), d.Description()); + + if ( message == "An Error that does not show up in the log" ) + return false; + + return true; + } + diff --git a/testing/btest/plugins/reporter-hook-plugin/src/Plugin.h b/testing/btest/plugins/reporter-hook-plugin/src/Plugin.h new file mode 100644 index 0000000000..2e793aba08 --- /dev/null +++ b/testing/btest/plugins/reporter-hook-plugin/src/Plugin.h @@ -0,0 +1,27 @@ + +#ifndef BRO_PLUGIN_Reporter_Hook +#define BRO_PLUGIN_Reporter_Hook + +#include + +namespace plugin { +namespace Reporter_Hook { + +class Plugin : public ::plugin::Plugin +{ +protected: + bool HookReporter(const std::string& prefix, const EventHandlerPtr event, + const Connection* conn, const val_list* addl, bool location, + const Location* location1, const Location* location2, + bool time, const std::string& buffer) override; + + // Overridden from plugin::Plugin. + plugin::Configuration Configure() override; +}; + +extern Plugin plugin; + +} +} + +#endif diff --git a/testing/btest/plugins/reporter-hook.bro b/testing/btest/plugins/reporter-hook.bro new file mode 100644 index 0000000000..13e98fc76e --- /dev/null +++ b/testing/btest/plugins/reporter-hook.bro @@ -0,0 +1,24 @@ +# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin -u . Reporter Hook +# @TEST-EXEC: cp -r %DIR/reporter-hook-plugin/* . +# @TEST-EXEC: ./configure --bro-dist=${DIST} && make +# @TEST-EXEC: BRO_PLUGIN_ACTIVATE="Reporter::Hook" BRO_PLUGIN_PATH=`pwd` bro -b %INPUT 2>&1 | $SCRIPTS/diff-remove-abspath | sort | uniq >output +# @TEST-EXEC: btest-diff output +# @TEST-EXEC: TEST_DIFF_CANONIFIER="$SCRIPTS/diff-remove-abspath | $SCRIPTS/diff-remove-timestamps" btest-diff reporter.log + +@load base/frameworks/reporter + +type TestType: record { + a: bool &optional; +}; + +event bro_init() + { + Reporter::info("Some Info"); + Reporter::warning("A warning"); + Reporter::error("An Error"); + Reporter::error("An Error that does not show up in the log"); + + # And just trigger a runtime problem. + local b = TestType(); + print b$a; + }