Checkpointing the dynamic plugin code.

This is essentially the code from the dynamic-plugin branch except for
some pieces that I have split out into separate, earlier commits.

I'm going to updatre things in this branch going forward.
This commit is contained in:
Robin Sommer 2013-11-26 11:23:25 -08:00
parent 7412470d66
commit 555df1e7ea
43 changed files with 1306 additions and 110 deletions

View file

@ -1,5 +1,10 @@
project(Bro C CXX) project(Bro C CXX)
# When changing the minimum version here, also adapt
# cmake/BroPluginDynamic and
# aux/bro-aux/plugin-support/skeleton/CMakeLists.txt
cmake_minimum_required(VERSION 2.6.3 FATAL_ERROR) cmake_minimum_required(VERSION 2.6.3 FATAL_ERROR)
include(cmake/CommonCMakeConfig.cmake) include(cmake/CommonCMakeConfig.cmake)
######################################################################## ########################################################################
@ -17,6 +22,7 @@ set(BRO_SCRIPT_SOURCE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/scripts)
get_filename_component(BRO_SCRIPT_INSTALL_PATH ${BRO_SCRIPT_INSTALL_PATH} get_filename_component(BRO_SCRIPT_INSTALL_PATH ${BRO_SCRIPT_INSTALL_PATH}
ABSOLUTE) ABSOLUTE)
set(BRO_PLUGIN_INSTALL_PATH ${BRO_ROOT_DIR}/share/bro/plugins)
set(BRO_MAGIC_INSTALL_PATH ${BRO_ROOT_DIR}/share/bro/magic) set(BRO_MAGIC_INSTALL_PATH ${BRO_ROOT_DIR}/share/bro/magic)
set(BRO_MAGIC_SOURCE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/magic/database) set(BRO_MAGIC_SOURCE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/magic/database)
@ -183,6 +189,10 @@ include(MiscTests)
include(PCAPTests) include(PCAPTests)
include(OpenSSLTests) include(OpenSSLTests)
include(CheckNameserCompat) include(CheckNameserCompat)
include(GetArchitecture)
# Tell the plugin code that we're building as part of the main tree.
set(BRO_PLUGIN_INTERNAL_BUILD true CACHE INTERNAL "" FORCE)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
${CMAKE_CURRENT_BINARY_DIR}/config.h) ${CMAKE_CURRENT_BINARY_DIR}/config.h)

2
cmake

@ -1 +1 @@
Subproject commit 0187b33a29d5ec824f940feff60dc5d8c2fe314f Subproject commit fc53d57770c86fbf9bd2d9694f06a1d539ebe35f

View file

@ -209,3 +209,14 @@
/* Common IPv6 extension structure */ /* Common IPv6 extension structure */
#cmakedefine HAVE_IP6_EXT #cmakedefine HAVE_IP6_EXT
/* String with host architecture (e.g., "linux-x86_64") */
#define HOST_ARCHITECTURE "@HOST_ARCHITECTURE@"
/* String with extension of dynamic libraries (e.g., ".so") */
#define SHARED_LIBRARY_SUFFIX "@CMAKE_SHARED_LIBRARY_SUFFIX@"
/* True if we're building outside of the main Bro source code tree. */
#ifndef BRO_PLUGIN_INTERNAL_BUILD
#define BRO_PLUGIN_INTERNAL_BUILD @BRO_PLUGIN_INTERNAL_BUILD@
#endif

View file

@ -414,6 +414,17 @@ add_dependencies(bro bif_loader_plugins)
# Install *.bif.bro. # Install *.bif.bro.
install(DIRECTORY ${CMAKE_BINARY_DIR}/scripts/base/bif DESTINATION ${BRO_SCRIPT_INSTALL_PATH}/base) install(DIRECTORY ${CMAKE_BINARY_DIR}/scripts/base/bif DESTINATION ${BRO_SCRIPT_INSTALL_PATH}/base)
# Make clean removes the bif directory. # Install the plugin directory with a short README.
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_BINARY_DIR}/scripts/base/bif) set(plugin_binary_dir "${CMAKE_BINARY_DIR}/plugins")
add_custom_command(OUTPUT ${plugin_binary_dir}/README
COMMAND mkdir ARGS -p ${plugin_binary_dir}
COMMAND echo ARGS "This directory holds dynamic Bro plugins." >${plugin_binary_dir}/README)
add_custom_target(plugin-dir DEPENDS ${plugin_binary_dir}/README)
add_dependencies(bro plugin-dir)
# Make clean removes the bif and plugin directories.
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_BINARY_DIR}/scripts/base/bif)
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${plugin_binary_dir})

View file

@ -5,6 +5,7 @@
#include "DebugLogger.h" #include "DebugLogger.h"
#include "Net.h" #include "Net.h"
#include "plugin/Plugin.h"
DebugLogger debug_logger("debug"); DebugLogger debug_logger("debug");
@ -73,10 +74,12 @@ void DebugLogger::EnableStreams(const char* s)
{ {
if ( strcasecmp("verbose", tok) == 0 ) if ( strcasecmp("verbose", tok) == 0 )
verbose = true; verbose = true;
else else if ( strncmp(tok, "plugin-", 7) != 0 )
reporter->FatalError("unknown debug stream %s\n", tok); reporter->FatalError("unknown debug stream %s\n", tok);
} }
enabled_streams.insert(tok);
tok = strtok(0, ","); tok = strtok(0, ",");
} }
@ -105,4 +108,24 @@ void DebugLogger::Log(DebugStream stream, const char* fmt, ...)
fflush(file); fflush(file);
} }
void DebugLogger::Log(const plugin::Plugin& plugin, const char* fmt, ...)
{
string tok = string("plugin-") + plugin.Name();
tok = strreplace(tok, "::", "-");
if ( enabled_streams.find(tok) == enabled_streams.end() )
return;
fprintf(file, "%17.06f/%17.06f [plugin %s] ",
network_time, current_time(true), plugin.Name());
va_list ap;
va_start(ap, fmt);
vfprintf(file, fmt, ap);
va_end(ap);
fputc('\n', file);
fflush(file);
}
#endif #endif

View file

@ -7,6 +7,8 @@
#ifdef DEBUG #ifdef DEBUG
#include <stdio.h> #include <stdio.h>
#include <string>
#include <set>
// To add a new debugging stream, add a constant here as well as // To add a new debugging stream, add a constant here as well as
// an entry to DebugLogger::streams in DebugLogger.cc. // an entry to DebugLogger::streams in DebugLogger.cc.
@ -27,7 +29,7 @@ enum DebugStream {
DBG_INPUT, // Input streams DBG_INPUT, // Input streams
DBG_THREADING, // Threading system DBG_THREADING, // Threading system
DBG_FILE_ANALYSIS, // File analysis DBG_FILE_ANALYSIS, // File analysis
DBG_PLUGINS, DBG_PLUGINS, // Plugin support
NUM_DBGS // Has to be last NUM_DBGS // Has to be last
}; };
@ -39,6 +41,10 @@ enum DebugStream {
#define DBG_PUSH(stream) debug_logger.PushIndent(stream) #define DBG_PUSH(stream) debug_logger.PushIndent(stream)
#define DBG_POP(stream) debug_logger.PopIndent(stream) #define DBG_POP(stream) debug_logger.PopIndent(stream)
#define PLUGIN_DBG_LOG(plugin, args...) debug_logger.Log(plugin, args)
namespace plugin { class Plugin; }
class DebugLogger { class DebugLogger {
public: public:
// Output goes to stderr per default. // Output goes to stderr per default.
@ -46,6 +52,7 @@ public:
~DebugLogger(); ~DebugLogger();
void Log(DebugStream stream, const char* fmt, ...); void Log(DebugStream stream, const char* fmt, ...);
void Log(const plugin::Plugin& plugin, const char* fmt, ...);
void PushIndent(DebugStream stream) void PushIndent(DebugStream stream)
{ ++streams[int(stream)].indent; } { ++streams[int(stream)].indent; }
@ -76,6 +83,8 @@ private:
bool enabled; bool enabled;
}; };
std::set<std::string> enabled_streams;
static Stream streams[NUM_DBGS]; static Stream streams[NUM_DBGS];
}; };
@ -86,6 +95,7 @@ extern DebugLogger debug_logger;
#define DBG_LOG_VERBOSE(args...) #define DBG_LOG_VERBOSE(args...)
#define DBG_PUSH(stream) #define DBG_PUSH(stream)
#define DBG_POP(stream) #define DBG_POP(stream)
#define PLUGIN_DBG_LOG(plugin, args...)
#endif #endif
#endif #endif

View file

@ -6,6 +6,7 @@
#include "Func.h" #include "Func.h"
#include "NetVar.h" #include "NetVar.h"
#include "Trigger.h" #include "Trigger.h"
#include "plugin/Manager.h"
EventMgr mgr; EventMgr mgr;
@ -77,6 +78,9 @@ EventMgr::~EventMgr()
void EventMgr::QueueEvent(Event* event) void EventMgr::QueueEvent(Event* event)
{ {
if ( plugin_mgr->QueueEvent(event) )
return;
if ( ! head ) if ( ! head )
head = tail = event; head = tail = event;
else else
@ -115,6 +119,8 @@ void EventMgr::Drain()
SegmentProfiler(segment_logger, "draining-events"); SegmentProfiler(segment_logger, "draining-events");
plugin_mgr->DrainEvents();
draining = true; draining = true;
while ( head ) while ( head )
Dispatch(); Dispatch();

View file

@ -49,7 +49,7 @@ void FlowSrc::Process()
// This is normally done by calling net_packet_dispatch(), // This is normally done by calling net_packet_dispatch(),
// but as we don't have a packet to dispatch ... // but as we don't have a packet to dispatch ...
network_time = next_timestamp; net_update_time(next_timestamp);
expire_timers(); expire_timers();
netflow_analyzer->downflow()->set_exporter_ip(exporter_ip); netflow_analyzer->downflow()->set_exporter_ip(exporter_ip);

View file

@ -46,6 +46,7 @@
#include "Event.h" #include "Event.h"
#include "Traverse.h" #include "Traverse.h"
#include "Reporter.h" #include "Reporter.h"
#include "plugin/Manager.h"
extern RETSIGTYPE sig_handler(int signo); extern RETSIGTYPE sig_handler(int signo);
@ -226,7 +227,7 @@ TraversalCode Func::Traverse(TraversalCallback* cb) const
HANDLE_TC_STMT_PRE(tc); HANDLE_TC_STMT_PRE(tc);
// FIXME: Traverse arguments to builtin functions, too. // FIXME: Traverse arguments to builtin functions, too.
if ( kind == BRO_FUNC ) if ( kind == BRO_FUNC && scope )
{ {
tc = scope->Traverse(cb); tc = scope->Traverse(cb);
HANDLE_TC_STMT_PRE(tc); HANDLE_TC_STMT_PRE(tc);
@ -281,6 +282,50 @@ Val* BroFunc::Call(val_list* args, Frame* parent) const
#ifdef PROFILE_BRO_FUNCTIONS #ifdef PROFILE_BRO_FUNCTIONS
DEBUG_MSG("Function: %s\n", id->Name()); DEBUG_MSG("Function: %s\n", id->Name());
#endif #endif
Val* plugin_result = plugin_mgr->CallFunction(this, args);
if ( plugin_result )
{
// TODO: We should factor this out into its own method.
switch ( Flavor() ) {
case FUNC_FLAVOR_EVENT:
Unref(plugin_result);
plugin_result = 0;
break;
case FUNC_FLAVOR_HOOK:
if ( plugin_result->Type()->Tag() != TYPE_BOOL )
reporter->InternalError("plugin returned non-bool for hook");
break;
case FUNC_FLAVOR_FUNCTION:
{
BroType* yt = FType()->YieldType();
if ( (! yt) || yt->Tag() == TYPE_VOID )
{
Unref(plugin_result);
plugin_result = 0;
}
else
{
if ( plugin_result->Type()->Tag() != yt->Tag() )
reporter->InternalError("plugin returned wrong type for function call");
}
break;
}
}
loop_over_list(*args, i)
Unref((*args)[i]);
return plugin_result;
}
if ( bodies.empty() ) if ( bodies.empty() )
{ {
// Can only happen for events and hooks. // Can only happen for events and hooks.

View file

@ -30,6 +30,7 @@
#include "PacketSort.h" #include "PacketSort.h"
#include "Serializer.h" #include "Serializer.h"
#include "PacketDumper.h" #include "PacketDumper.h"
#include "plugin/Manager.h"
extern "C" { extern "C" {
#include "setsignal.h" #include "setsignal.h"
@ -144,13 +145,17 @@ RETSIGTYPE watchdog(int /* signo */)
return RETSIGVAL; return RETSIGVAL;
} }
void net_update_time(double new_network_time)
{
network_time = new_network_time;
plugin_mgr->UpdateNetworkTime(network_time);
}
void net_init(name_list& interfaces, name_list& readfiles, void net_init(name_list& interfaces, name_list& readfiles,
name_list& netflows, name_list& flowfiles, name_list& netflows, name_list& flowfiles,
const char* writefile, const char* filter, const char* writefile, const char* filter,
const char* secondary_filter, int do_watchdog) const char* secondary_filter, int do_watchdog)
{ {
init_net_var();
if ( readfiles.length() > 0 || flowfiles.length() > 0 ) if ( readfiles.length() > 0 || flowfiles.length() > 0 )
{ {
reading_live = pseudo_realtime > 0.0; reading_live = pseudo_realtime > 0.0;
@ -323,7 +328,7 @@ void net_packet_dispatch(double t, const struct pcap_pkthdr* hdr,
: timer_mgr; : timer_mgr;
// network_time never goes back. // network_time never goes back.
network_time = tmgr->Time() < t ? t : tmgr->Time(); net_update_time(tmgr->Time() < t ? t : tmgr->Time());
current_pktsrc = src_ps; current_pktsrc = src_ps;
current_iosrc = src_ps; current_iosrc = src_ps;
@ -456,7 +461,7 @@ void net_run()
{ {
// Take advantage of the lull to get up to // Take advantage of the lull to get up to
// date on timers and events. // date on timers and events.
network_time = ct; net_update_time(ct);
expire_timers(); expire_timers();
usleep(1); // Just yield. usleep(1); // Just yield.
} }
@ -478,7 +483,7 @@ void net_run()
// date on timers and events. Because we only // date on timers and events. Because we only
// have timers as sources, going to sleep here // have timers as sources, going to sleep here
// doesn't risk blocking on other inputs. // doesn't risk blocking on other inputs.
network_time = current_time(); net_update_time(current_time());
expire_timers(); expire_timers();
// Avoid busy-waiting - pause for 100 ms. // Avoid busy-waiting - pause for 100 ms.

View file

@ -19,6 +19,7 @@ extern void net_run();
extern void net_get_final_stats(); extern void net_get_final_stats();
extern void net_finish(int drain_events); extern void net_finish(int drain_events);
extern void net_delete(); // Reclaim all memory, etc. extern void net_delete(); // Reclaim all memory, etc.
extern void net_update_time(double new_network_time);
extern void net_packet_arrival(double t, const struct pcap_pkthdr* hdr, extern void net_packet_arrival(double t, const struct pcap_pkthdr* hdr,
const u_char* pkt, int hdr_size, const u_char* pkt, int hdr_size,
PktSrc* src_ps); PktSrc* src_ps);

View file

@ -1455,7 +1455,7 @@ void RemoteSerializer::Process()
// FIXME: The following chunk of code is copied from // FIXME: The following chunk of code is copied from
// net_packet_dispatch(). We should change that function // net_packet_dispatch(). We should change that function
// to accept an IOSource instead of the PktSrc. // to accept an IOSource instead of the PktSrc.
network_time = p->time; net_update_time(p->time);
SegmentProfiler(segment_logger, "expiring-timers"); SegmentProfiler(segment_logger, "expiring-timers");
TimerMgr* tmgr = sessions->LookupTimerMgr(GetCurrentTag()); TimerMgr* tmgr = sessions->LookupTimerMgr(GetCurrentTag());

View file

@ -247,14 +247,13 @@ void init_alternative_mode()
fprintf(fp_func_init, "\n"); fprintf(fp_func_init, "\n");
fprintf(fp_func_init, "#include <list>\n"); fprintf(fp_func_init, "#include <list>\n");
fprintf(fp_func_init, "#include <string>\n"); fprintf(fp_func_init, "#include <string>\n");
fprintf(fp_func_init, "#include \"plugin/Plugin.h\"\n");
fprintf(fp_func_init, "#include \"%s.h\"\n", input_filename); fprintf(fp_func_init, "#include \"%s.h\"\n", input_filename);
fprintf(fp_func_init, "\n"); fprintf(fp_func_init, "\n");
fprintf(fp_func_init, "namespace plugin { namespace %s {\n", plugin); fprintf(fp_func_init, "namespace plugin { namespace %s {\n", plugin);
fprintf(fp_func_init, "\n"); fprintf(fp_func_init, "\n");
fprintf(fp_func_init, "std::list<std::pair<const char*, int> > __bif_%s_init()\n", name); fprintf(fp_func_init, "void __bif_%s_init(plugin::Plugin* plugin)\n", name);
fprintf(fp_func_init, "\t{\n"); fprintf(fp_func_init, "\t{\n");
fprintf(fp_func_init, "\tstd::list<std::pair<const char*, int> > bifs;\n");
fprintf(fp_func_init, "\n");
} }
} }
@ -266,7 +265,6 @@ void finish_alternative_mode()
if ( plugin ) if ( plugin )
{ {
fprintf(fp_func_init, "\n"); fprintf(fp_func_init, "\n");
fprintf(fp_func_init, "\treturn bifs;\n");
fprintf(fp_func_init, "\t}\n"); fprintf(fp_func_init, "\t}\n");
fprintf(fp_func_init, "} }\n"); fprintf(fp_func_init, "} }\n");
fprintf(fp_func_init, "\n"); fprintf(fp_func_init, "\n");

View file

@ -267,12 +267,12 @@ void print_event_c_body(FILE *fp)
//fprintf(fp, "%s // end namespace\n", decl.generate_c_namespace_end.c_str()); //fprintf(fp, "%s // end namespace\n", decl.generate_c_namespace_end.c_str());
} }
void record_bif_item(const char* id, int type) void record_bif_item(const char* id, const char* type)
{ {
if ( ! plugin ) if ( ! plugin )
return; return;
fprintf(fp_func_init, "\tbifs.push_back(std::make_pair(\"%s\", %d));\n", id, type); fprintf(fp_func_init, "\tplugin->AddBifItem(\"%s\", plugin::BifItem::%s);\n", id, type);
} }
%} %}
@ -358,7 +358,7 @@ type_def: TOK_TYPE opt_ws TOK_ID opt_ws ':' opt_ws type_def_types opt_ws ';'
decl.c_fullname.c_str(), decl.bro_fullname.c_str(), decl.c_fullname.c_str(), decl.bro_fullname.c_str(),
type_name.c_str()); type_name.c_str());
record_bif_item(decl.bro_fullname.c_str(), 5); record_bif_item(decl.bro_fullname.c_str(), "TYPE");
} }
; ;
@ -401,7 +401,7 @@ enum_def: enum_def_1 enum_list TOK_RPB
"\t%s = internal_type(\"%s\")->AsEnumType();\n", "\t%s = internal_type(\"%s\")->AsEnumType();\n",
decl.c_fullname.c_str(), decl.bro_fullname.c_str()); decl.c_fullname.c_str(), decl.bro_fullname.c_str());
record_bif_item(decl.bro_fullname.c_str(), 5); record_bif_item(decl.bro_fullname.c_str(), "TYPE");
} }
; ;
@ -457,7 +457,7 @@ const_def: TOK_CONST opt_ws TOK_ID opt_ws ':' opt_ws TOK_ID opt_ws ';'
decl.c_fullname.c_str(), decl.bro_fullname.c_str(), decl.c_fullname.c_str(), decl.bro_fullname.c_str(),
accessor); accessor);
record_bif_item(decl.bro_fullname.c_str(), 3); record_bif_item(decl.bro_fullname.c_str(), "CONSTANT");
} }
attr_list: attr_list:
@ -545,7 +545,7 @@ head_1: TOK_ID opt_ws arg_begin
"Val* %s(Frame* frame, val_list* %s)", "Val* %s(Frame* frame, val_list* %s)",
decl.c_fullname.c_str(), arg_list_name); decl.c_fullname.c_str(), arg_list_name);
record_bif_item(decl.bro_fullname.c_str(), 1); record_bif_item(decl.bro_fullname.c_str(), "FUNCTION");
} }
else if ( definition_type == EVENT_DEF ) else if ( definition_type == EVENT_DEF )
{ {
@ -562,7 +562,7 @@ head_1: TOK_ID opt_ws arg_begin
"\t%s = internal_handler(\"%s\");\n", "\t%s = internal_handler(\"%s\");\n",
decl.c_fullname.c_str(), decl.bro_fullname.c_str()); decl.c_fullname.c_str(), decl.bro_fullname.c_str());
record_bif_item(decl.bro_fullname.c_str(), 2); record_bif_item(decl.bro_fullname.c_str(), "EVENT");
// C++ prototypes of bro_event_* functions will // C++ prototypes of bro_event_* functions will
// be generated later. // be generated later.

View file

@ -198,6 +198,7 @@ void usage()
fprintf(stderr, " -N|--print-plugins | print available plugins and exit (-NN for verbose)\n"); fprintf(stderr, " -N|--print-plugins | print available plugins and exit (-NN for verbose)\n");
fprintf(stderr, " -O|--optimize | optimize policy script\n"); fprintf(stderr, " -O|--optimize | optimize policy script\n");
fprintf(stderr, " -P|--prime-dns | prime DNS\n"); fprintf(stderr, " -P|--prime-dns | prime DNS\n");
fprintf(stderr, " -Q|--time | print execution time summary to stderr\n");
fprintf(stderr, " -R|--replay <events.bst> | replay events\n"); fprintf(stderr, " -R|--replay <events.bst> | replay events\n");
fprintf(stderr, " -S|--debug-rules | enable rule debugging\n"); fprintf(stderr, " -S|--debug-rules | enable rule debugging\n");
fprintf(stderr, " -T|--re-level <level> | set 'RE_level' for rules\n"); fprintf(stderr, " -T|--re-level <level> | set 'RE_level' for rules\n");
@ -220,8 +221,10 @@ void usage()
fprintf(stderr, " -n|--idmef-dtd <idmef-msg.dtd> | specify path to IDMEF DTD file\n"); fprintf(stderr, " -n|--idmef-dtd <idmef-msg.dtd> | specify path to IDMEF DTD file\n");
#endif #endif
fprintf(stderr, " $BROPATH | file search path (%s)\n", bro_path()); fprintf(stderr, " $BROPATH | file search path (%s)\n", bro_path().c_str());
fprintf(stderr, " $BROMAGIC | libmagic mime magic database search path (%s)\n", bro_magic_path()); fprintf(stderr, " $BROMAGIC | libmagic mime magic database search path (%s)\n", bro_magic_path());
fprintf(stderr, " $BRO_PLUGINS | plugin search path (%s)\n", bro_plugin_path());
fprintf(stderr, " $BRO_PREFIXES | prefix list (%s)\n", bro_prefixes());
fprintf(stderr, " $BRO_PREFIXES | prefix list (%s)\n", bro_prefixes().c_str()); fprintf(stderr, " $BRO_PREFIXES | prefix list (%s)\n", bro_prefixes().c_str());
fprintf(stderr, " $BRO_DNS_FAKE | disable DNS lookups (%s)\n", bro_dns_fake()); fprintf(stderr, " $BRO_DNS_FAKE | disable DNS lookups (%s)\n", bro_dns_fake());
fprintf(stderr, " $BRO_SEED_FILE | file to load seeds from (not set)\n"); fprintf(stderr, " $BRO_SEED_FILE | file to load seeds from (not set)\n");
@ -435,6 +438,8 @@ int main(int argc, char** argv)
{ {
std::set_new_handler(bro_new_handler); std::set_new_handler(bro_new_handler);
double time_start = current_time(true);
brofiler.ReadStats(); brofiler.ReadStats();
bro_argc = argc; bro_argc = argc;
@ -464,6 +469,7 @@ int main(int argc, char** argv)
int rule_debug = 0; int rule_debug = 0;
int RE_level = 4; int RE_level = 4;
int print_plugins = 0; int print_plugins = 0;
int time_bro = 0;
static struct option long_opts[] = { static struct option long_opts[] = {
{"bare-mode", no_argument, 0, 'b'}, {"bare-mode", no_argument, 0, 'b'},
@ -545,7 +551,7 @@ int main(int argc, char** argv)
opterr = 0; opterr = 0;
char opts[256]; char opts[256];
safe_strncpy(opts, "B:D:e:f:I:i:K:l:n:p:R:r:s:T:t:U:w:x:X:y:Y:z:CFGLNOPSWbdghvZ", safe_strncpy(opts, "B:D:e:f:I:i:K:l:n:p:R:r:s:T:t:U:w:x:X:y:Y:z:CFGLNOPSWbdghvZQ",
sizeof(opts)); sizeof(opts));
#ifdef USE_PERFTOOLS_DEBUG #ifdef USE_PERFTOOLS_DEBUG
@ -674,6 +680,10 @@ int main(int argc, char** argv)
dns_type = DNS_PRIME; dns_type = DNS_PRIME;
break; break;
case 'Q':
time_bro = 1;
break;
case 'R': case 'R':
events_file = optarg; events_file = optarg;
break; break;
@ -760,6 +770,7 @@ int main(int argc, char** argv)
reporter = new Reporter(); reporter = new Reporter();
thread_mgr = new threading::Manager(); thread_mgr = new threading::Manager();
plugin_mgr = new plugin::Manager();
#ifdef DEBUG #ifdef DEBUG
if ( debug_streams ) if ( debug_streams )
@ -810,6 +821,8 @@ int main(int argc, char** argv)
if ( ! bare_mode ) if ( ! bare_mode )
add_input_file("base/init-default.bro"); add_input_file("base/init-default.bro");
plugin_mgr->LoadPluginsFrom(bro_plugin_path());
if ( optind == argc && if ( optind == argc &&
read_files.length() == 0 && flow_files.length() == 0 && read_files.length() == 0 && flow_files.length() == 0 &&
interfaces.length() == 0 && interfaces.length() == 0 &&
@ -842,7 +855,6 @@ int main(int argc, char** argv)
analyzer_mgr = new analyzer::Manager(); analyzer_mgr = new analyzer::Manager();
log_mgr = new logging::Manager(); log_mgr = new logging::Manager();
input_mgr = new input::Manager(); input_mgr = new input::Manager();
plugin_mgr = new plugin::Manager();
file_mgr = new file_analysis::Manager(); file_mgr = new file_analysis::Manager();
plugin_mgr->InitPreScript(); plugin_mgr->InitPreScript();
@ -877,9 +889,13 @@ int main(int argc, char** argv)
yyparse(); yyparse();
plugin_mgr->InitPostScript(); init_general_global_var();
analyzer_mgr->InitPostScript(); init_net_var();
file_mgr->InitPostScript();
plugin_mgr->InitBifs();
if ( reporter->Errors() > 0 )
exit(1);
if ( print_plugins ) if ( print_plugins )
{ {
@ -887,6 +903,10 @@ int main(int argc, char** argv)
exit(1); exit(1);
} }
plugin_mgr->InitPostScript();
analyzer_mgr->InitPostScript();
file_mgr->InitPostScript();
#ifdef USE_PERFTOOLS_DEBUG #ifdef USE_PERFTOOLS_DEBUG
} }
#endif #endif
@ -916,8 +936,6 @@ int main(int argc, char** argv)
reporter->InitOptions(); reporter->InitOptions();
init_general_global_var();
if ( user_pcap_filter ) if ( user_pcap_filter )
{ {
ID* id = global_scope()->Lookup("cmd_line_bpf_filter"); ID* id = global_scope()->Lookup("cmd_line_bpf_filter");
@ -1079,7 +1097,7 @@ int main(int argc, char** argv)
if ( ! reading_live && ! reading_traces ) if ( ! reading_live && ! reading_traces )
// Set up network_time to track real-time, since // Set up network_time to track real-time, since
// we don't have any other source for it. // we don't have any other source for it.
network_time = current_time(); net_update_time(current_time());
EventHandlerPtr bro_init = internal_handler("bro_init"); EventHandlerPtr bro_init = internal_handler("bro_init");
if ( bro_init ) //### this should be a function if ( bro_init ) //### this should be a function
@ -1165,7 +1183,43 @@ int main(int argc, char** argv)
#endif #endif
double time_net_start = current_time(true);;
unsigned int mem_net_start_total;
unsigned int mem_net_start_malloced;
if ( time_bro )
{
get_memory_usage(&mem_net_start_total, &mem_net_start_malloced);
fprintf(stderr, "# initialization %.6f\n", time_net_start - time_start);
fprintf(stderr, "# initialization %uM/%uM\n",
mem_net_start_total / 1024 / 1024,
mem_net_start_malloced / 1024 / 1024);
}
net_run(); net_run();
double time_net_done = current_time(true);;
unsigned int mem_net_done_total;
unsigned int mem_net_done_malloced;
if ( time_bro )
{
get_memory_usage(&mem_net_done_total, &mem_net_done_malloced);
fprintf(stderr, "# total time %.6f, processing %.6f\n",
time_net_done - time_start, time_net_done - time_net_start);
fprintf(stderr, "# total mem %uM/%uM, processing %uM/%uM\n",
mem_net_done_total / 1024 / 1024,
mem_net_done_malloced / 1024 / 1024,
(mem_net_done_total - mem_net_start_total) / 1024 / 1024,
(mem_net_done_malloced - mem_net_start_malloced) / 1024 / 1024);
}
done_with_network(); done_with_network();
net_delete(); net_delete();

View file

@ -83,8 +83,6 @@ public:
*/ */
T GetComponentTag(Val* v) const; T GetComponentTag(Val* v) const;
protected:
/** /**
* Add a component the internal maps used to keep track of it and create * Add a component the internal maps used to keep track of it and create
* a script-layer ID for the component's enum value. * a script-layer ID for the component's enum value.

View file

@ -28,18 +28,15 @@
* must be unique across all loaded plugins. * must be unique across all loaded plugins.
*/ */
#define BRO_PLUGIN_BEGIN(_ns, _name) \ #define BRO_PLUGIN_BEGIN(_ns, _name) \
namespace plugin { namespace _ns ## _ ## _name {\ namespace plugin { namespace _ns ## _ ## _name { \
class Plugin : public plugin::Plugin { \ class Plugin : public plugin::Plugin { \
protected: \ protected: \
void InitPreScript() \ void InitPreScript() \
{ \ { \
SetName(#_ns "::" #_name); \ SetName(#_ns "::" #_name); \
SetVersion(-1);\ SetVersion(-1); \
SetAPIVersion(BRO_PLUGIN_API_VERSION);\ SetAPIVersion(BRO_PLUGIN_API_VERSION); \
SetDynamicPlugin(false); SetDynamicPlugin(! BRO_PLUGIN_INTERNAL_BUILD);\
// TODO: The SetDynamicPlugin() call is currently hardcoded to false. Change
// once we have dynamic plugins as well.
/** /**
* Ends the definition of a plugin. * Ends the definition of a plugin.
@ -76,8 +73,8 @@
* interpreter. * interpreter.
*/ */
#define BRO_PLUGIN_BIF_FILE(file) \ #define BRO_PLUGIN_BIF_FILE(file) \
extern std::list<std::pair<const char*, int> > __bif_##file##_init(); \ extern void __bif_##file##_init(plugin::Plugin*); \
AddBifInitFunction(&__bif_##file##_init); __AddBifInitFunction(&__bif_##file##_init);
/** /**
* Defines a component implementing a protocol analyzer. * Defines a component implementing a protocol analyzer.

View file

@ -1,11 +1,23 @@
// See the file "COPYING" in the main distribution directory for copyright. // See the file "COPYING" in the main distribution directory for copyright.
#include <sstream>
#include <dirent.h>
#include <glob.h>
#include <dlfcn.h>
#include <errno.h>
#include <sys/stat.h>
#include "Manager.h" #include "Manager.h"
#include "../Reporter.h" #include "../Reporter.h"
#include "../Func.h"
#include "../Event.h"
using namespace plugin; using namespace plugin;
string Manager::current_dir;
string Manager::current_sopath;
Manager::Manager() Manager::Manager()
{ {
init = false; init = false;
@ -16,18 +28,162 @@ Manager::~Manager()
assert(! init); assert(! init);
} }
bool Manager::LoadPlugin(const std::string& path) void Manager::LoadPluginsFrom(const string& dir)
{ {
assert(! init); assert(! init);
reporter->InternalError("plugin::Manager::LoadPlugin not yet implemented");
return false; if ( dir.empty() )
return;
if ( dir.find(":") != string::npos )
{
// Split at ":".
std::stringstream s(dir);
std::string d;
while ( std::getline(s, d, ':') )
LoadPluginsFrom(d);
return;
} }
bool Manager::LoadPluginsFrom(const std::string& dir) if ( ! is_dir(dir) )
{
DBG_LOG(DBG_PLUGINS, "Not a valid plugin directory: %s", dir.c_str());
return;
}
int rc = LoadPlugin(dir);
if ( rc >= 0 )
return;
DBG_LOG(DBG_PLUGINS, "Searching directory %s recursively for plugins", dir.c_str());
DIR* d = opendir(dir.c_str());
if ( ! d )
{
DBG_LOG(DBG_PLUGINS, "Cannot open directory %s", dir.c_str());
return;
}
bool found = false;
struct dirent *dp;
while ( (dp = readdir(d)) )
{
struct stat st;
if ( strcmp(dp->d_name, "..") == 0
|| strcmp(dp->d_name, ".") == 0 )
continue;
string path = dir + "/" + dp->d_name;
if( stat(path.c_str(), &st) < 0 )
{
DBG_LOG(DBG_PLUGINS, "Cannot stat %s: %s", path.c_str(), strerror(errno));
continue;
}
if ( st.st_mode & S_IFDIR )
LoadPluginsFrom(path);
}
}
int Manager::LoadPlugin(const std::string& dir)
{ {
assert(! init); assert(! init);
reporter->InternalError("plugin::Manager::LoadPluginsFrom not yet implemented");
return false; // Check if it's a plugin dirctory.
if ( ! is_file(dir + "/__bro_plugin__") )
return -1;
DBG_LOG(DBG_PLUGINS, "Loading plugin from %s", dir.c_str());
// Add the "scripts" and "bif" directories to BROPATH.
string scripts = dir + "/scripts";
if ( is_dir(scripts) )
{
DBG_LOG(DBG_PLUGINS, " Adding %s to BROPATH", scripts.c_str());
add_to_bro_path(scripts);
}
string bif = dir + "/bif";
if ( is_dir(bif) )
{
DBG_LOG(DBG_PLUGINS, " Adding %s to BROPATH", bif.c_str());
add_to_bro_path(bif);
}
// Load dylib/scripts/__load__.bro automatically.
string dyinit = dir + "/dylib/scripts/__load__.bro";
if ( is_file(dyinit) )
{
DBG_LOG(DBG_PLUGINS, " Adding %s for loading", dyinit.c_str());
add_input_file(dyinit.c_str());
}
// Load scripts/__load__.bro automatically.
string init = scripts + "/__load__.bro";
if ( is_file(init) )
{
DBG_LOG(DBG_PLUGINS, " Adding %s for loading", init.c_str());
add_input_file(init.c_str());
}
// Load bif/__load__.bro automatically.
init = bif + "/__load__.bro";
if ( is_file(init) )
{
DBG_LOG(DBG_PLUGINS, " Adding %s for loading", init.c_str());
add_input_file(init.c_str());
}
// Load shared libraries.
string dypattern = dir + "/dylib/*." + HOST_ARCHITECTURE + SHARED_LIBRARY_SUFFIX;
DBG_LOG(DBG_PLUGINS, " Searching for shared libraries %s", dypattern.c_str());
glob_t gl;
if ( glob(dypattern.c_str(), 0, 0, &gl) == 0 )
{
for ( size_t i = 0; i < gl.gl_pathc; i++ )
{
const char* path = gl.gl_pathv[i];
current_dir = dir;
current_sopath = path;
void* hdl = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
current_dir.clear();
current_sopath.clear();
if ( ! hdl )
{
const char* err = dlerror();
reporter->FatalError("cannot load plugin library %s: %s", path, err ? err : "<unknown error>");
}
DBG_LOG(DBG_PLUGINS, " Loaded %s", path);
}
}
else
{
DBG_LOG(DBG_PLUGINS, " No shared library found");
return 1;
}
return 1;
} }
static bool plugin_cmp(const Plugin* a, const Plugin* b) static bool plugin_cmp(const Plugin* a, const Plugin* b)
@ -39,22 +195,61 @@ bool Manager::RegisterPlugin(Plugin *plugin)
{ {
Manager::PluginsInternal()->push_back(plugin); Manager::PluginsInternal()->push_back(plugin);
if ( current_dir.size() && current_sopath.size() )
plugin->SetPluginLocation(current_dir.c_str(), current_sopath.c_str());
// Sort plugins by name to make sure we have a deterministic order. // Sort plugins by name to make sure we have a deterministic order.
PluginsInternal()->sort(plugin_cmp); PluginsInternal()->sort(plugin_cmp);
return true; return true;
} }
static bool interpreter_plugin_cmp(const InterpreterPlugin* a, const InterpreterPlugin* b)
{
if ( a->Priority() == b->Priority() )
return a->Name() < b->Name();
// Reverse sort.
return a->Priority() > b->Priority();
}
void Manager::InitPreScript() void Manager::InitPreScript()
{ {
assert(! init); assert(! init);
for ( plugin_list::iterator i = Manager::PluginsInternal()->begin(); i != Manager::PluginsInternal()->end(); i++ ) for ( plugin_list::iterator i = Manager::PluginsInternal()->begin(); i != Manager::PluginsInternal()->end(); i++ )
(*i)->InitPreScript(); {
Plugin* plugin = *i;
if ( plugin->PluginType() == Plugin::INTERPRETER )
interpreter_plugins.push_back(dynamic_cast<InterpreterPlugin *>(plugin));
plugin->InitPreScript();
// Track the file extensions the plugin can handle.
std::stringstream ext(plugin->FileExtensions());
// Split at ":".
std::string e;
while ( std::getline(ext, e, ':') )
{
DBG_LOG(DBG_PLUGINS, "Plugin %s handles *.%s", plugin->Name(), e.c_str());
extensions.insert(std::make_pair(e, plugin));
}
}
interpreter_plugins.sort(interpreter_plugin_cmp);
init = true; init = true;
} }
void Manager::InitBifs()
{
for ( plugin_list::iterator i = Manager::PluginsInternal()->begin(); i != Manager::PluginsInternal()->end(); i++ )
(*i)->InitBifs();
}
void Manager::InitPostScript() void Manager::InitPostScript()
{ {
assert(init); assert(init);
@ -78,6 +273,28 @@ void Manager::FinishPlugins()
init = false; init = false;
} }
int Manager::TryLoadFile(const char* file)
{
assert(file);
const char* ext = strrchr(file, '.');
if ( ! ext )
return -1;
extension_map::iterator i = extensions.find(++ext);
if ( i == extensions.end() )
return -1;
Plugin* plugin = i->second;
DBG_LOG(DBG_PLUGINS, "Loading %s with %s", file, plugin->Name());
if ( i->second->LoadFile(file) )
return 1;
return 0;
}
Manager::plugin_list Manager::Plugins() const Manager::plugin_list Manager::Plugins() const
{ {
return *Manager::PluginsInternal(); return *Manager::PluginsInternal();
@ -92,3 +309,67 @@ Manager::plugin_list* Manager::PluginsInternal()
return plugins; return plugins;
} }
Val* Manager::CallFunction(const Func* func, val_list* args) const
{
Val* result = 0;
for ( interpreter_plugin_list::const_iterator i = interpreter_plugins.begin();
i != interpreter_plugins.end() && ! result; i++ )
{
result = (*i)->CallFunction(func, args);
if ( result )
{
DBG_LOG(DBG_PLUGINS, "Plugin %s replaced call to %s", (*i)->Name(), func->Name());
return result;
}
}
return 0;
}
bool Manager::QueueEvent(Event* event) const
{
for ( interpreter_plugin_list::const_iterator i = interpreter_plugins.begin();
i != interpreter_plugins.end(); i++ )
{
if ( (*i)->QueueEvent(event) )
{
DBG_LOG(DBG_PLUGINS, "Plugin %s handled queueing of event %s", (*i)->Name(), event->Handler()->Name());
return true;
}
}
return false;
}
void Manager::UpdateNetworkTime(double network_time) const
{
for ( interpreter_plugin_list::const_iterator i = interpreter_plugins.begin();
i != interpreter_plugins.end(); i++ )
(*i)->UpdateNetworkTime(network_time);
}
void Manager::DrainEvents() const
{
for ( interpreter_plugin_list::const_iterator i = interpreter_plugins.begin();
i != interpreter_plugins.end(); i++ )
(*i)->DrainEvents();
}
void Manager::DisableInterpreterPlugin(const InterpreterPlugin* plugin)
{
for ( interpreter_plugin_list::iterator i = interpreter_plugins.begin();
i != interpreter_plugins.end(); i++ )
{
if ( *i == plugin )
{
interpreter_plugins.erase(i);
return;
}
}
}

View file

@ -3,6 +3,8 @@
#ifndef PLUGIN_MANAGER_H #ifndef PLUGIN_MANAGER_H
#define PLUGIN_MANAGER_H #define PLUGIN_MANAGER_H
#include <map>
#include "Plugin.h" #include "Plugin.h"
#include "Component.h" #include "Component.h"
@ -17,6 +19,7 @@ class Manager
{ {
public: public:
typedef std::list<Plugin*> plugin_list; typedef std::list<Plugin*> plugin_list;
typedef std::list<InterpreterPlugin*> interpreter_plugin_list;
typedef Plugin::component_list component_list; typedef Plugin::component_list component_list;
/** /**
@ -30,24 +33,17 @@ public:
~Manager(); ~Manager();
/** /**
* Loads a plugin dynamically from a file. This must be called only * Loads all plugins dynamically from a set of directories. Multiple
* before InitPluginsPreScript() * directories are split by ':'. If a directory does not contain a
* plugin itself, the method searches for plugins recursively. For
* plugins found, the method loads the plugin's shared library and
* makes its scripts available to the interpreter.
* *
* This is not currently implemented. * This must be called only before InitPluginsPreScript().
*
* @param file The path to the plugin to load.
*/
bool LoadPlugin(const std::string& file);
/**
* Loads plugins dynamically found in a directory. This must be
* called only before InitPluginsPreScript().
*
* This is not currently implemented.
* *
* @param dir The directory to search for plugins. * @param dir The directory to search for plugins.
*/ */
bool LoadPluginsFrom(const std::string& dir); void LoadPluginsFrom(const std::string& dir);
/** /**
* First-stage initializion of the manager. This is called early on * First-stage initializion of the manager. This is called early on
@ -57,7 +53,13 @@ public:
void InitPreScript(); void InitPreScript();
/** /**
* Second-stage initialization of the manager. This is called late * Second-stage initialization of the manager. This is called in
* between pre- and post-script to make BiFs available.
*/
void InitBifs();
/**
* Third-stage initialization of the manager. This is called late
* during Bro's initialization after any scripts are processed, and * during Bro's initialization after any scripts are processed, and
* forwards to the corresponding Plugin methods. * forwards to the corresponding Plugin methods.
*/ */
@ -69,6 +71,22 @@ public:
*/ */
void FinishPlugins(); void FinishPlugins();
/**
* This tries to load the given file by searching for a plugin that
* support that extension. If a correspondign plugin is found, it's
* asked to loead the file. If that fails, the method reports an
* error message.
*
* This method must be called only between InitPreScript() and
* InitPostScript().
*
* @return 1 if the file was sucessfully loaded by a plugin; 0 if a
* plugin was found that supports the file's extension, yet it
* encountered a problem loading the file; and -1 if we don't have a
* plugin that supports this extension.
*/
int TryLoadFile(const char* file);
/** /**
* Returns a list of all available plugins. This includes all that * Returns a list of all available plugins. This includes all that
* are compiled in statically, as well as those loaded dynamically so * are compiled in statically, as well as those loaded dynamically so
@ -83,6 +101,52 @@ public:
*/ */
template<class T> std::list<T *> Components() const; template<class T> std::list<T *> Components() const;
/**
* Filters a function/event/hook call through all interpreter plugins.
*
* @param func The function to be called.
*
* @param args The function call's arguments; they may be modified.
*
* @return If a plugin handled the call, a +1 Val with the result
* value to pass back to the interpreter (for void functions and
* events, it may be any Val and must be ignored). If no plugin
* handled the call, the method returns null.
*/
Val* CallFunction(const Func* func, val_list* args) const;
/**
* Filter the queuing of an event through all interpreter plugins.
*
* @param event The event to be queued; it may be modified.
*
* @return Returns true if a plugin handled the queuing; in that case the
* plugin will have taken ownership.
*
*/
bool QueueEvent(Event* event) const;
/**
* Informs all interpreter plugins about an update in network time.
*
* @param networkt_time The new network time.
*/
void UpdateNetworkTime(double network_time) const;
/**
* Informs all interpreter plugins that the event queue has been drained.
*/
void DrainEvents() const;
/**
* Disables an interpreter plugin's hooking of the script interpreter.
* The remaining functionality of the Plugin base class remains
* available.
*
* @param plugin The plugin to disable.
*/
void DisableInterpreterPlugin(const InterpreterPlugin* plugin);
/** /**
* Internal method that registers a freshly instantiated plugin with * Internal method that registers a freshly instantiated plugin with
* the manager. * the manager.
@ -91,12 +155,38 @@ public:
* ownership, yet assumes the pointer will stay valid at least until * ownership, yet assumes the pointer will stay valid at least until
* the Manager is destroyed. * the Manager is destroyed.
*/ */
static bool RegisterPlugin(Plugin *plugin); static bool RegisterPlugin(Plugin* plugin);
protected:
/**
* Loads a plugin dynamically from a given directory. It loads the
* plugin's shared library, and makes its scripts available to the
* interpreter. Different from LoadPluginsFrom() this method does not
* further descend the directory tree recursively to search for
* plugins.
*
* This must be called only before InitPluginsPreScript()
*
* @param file The path to the plugin to load.
*
* @return 0 if there's a plugin in this directory, but there was a
* problem loading it; -1 if there's no plugin at all in this
* directory; 1 if there's a plugin in this directory and we loaded
* it successfully.
*/
int LoadPlugin(const std::string& dir);
private: private:
static plugin_list* PluginsInternal(); static plugin_list* PluginsInternal();
bool init; bool init;
typedef std::map<std::string, Plugin*> extension_map;
extension_map extensions;
interpreter_plugin_list interpreter_plugins;
static string current_dir;
static string current_sopath;
}; };
template<class T> template<class T>

View file

@ -10,9 +10,9 @@
using namespace plugin; using namespace plugin;
BifItem::BifItem(const std::string& arg_id, Type arg_type) BifItem::BifItem(const char* arg_id, Type arg_type)
{ {
id = copy_string(arg_id.c_str()); id = copy_string(arg_id);
type = arg_type; type = arg_type;
} }
@ -47,6 +47,9 @@ Plugin::Plugin()
version = -9999; version = -9999;
api_version = -9999; api_version = -9999;
dynamic = false; dynamic = false;
base_dir = 0;
sopath = 0;
extensions = 0;
Manager::RegisterPlugin(this); Manager::RegisterPlugin(this);
} }
@ -57,6 +60,14 @@ Plugin::~Plugin()
delete [] name; delete [] name;
delete [] description; delete [] description;
delete [] base_dir;
delete [] sopath;
delete [] extensions;
}
Plugin::Type Plugin::PluginType() const
{
return STANDARD;
} }
const char* Plugin::Name() const const char* Plugin::Name() const
@ -66,6 +77,7 @@ const char* Plugin::Name() const
void Plugin::SetName(const char* arg_name) void Plugin::SetName(const char* arg_name)
{ {
delete [] name;
name = copy_string(arg_name); name = copy_string(arg_name);
} }
@ -76,6 +88,7 @@ const char* Plugin::Description() const
void Plugin::SetDescription(const char* arg_description) void Plugin::SetDescription(const char* arg_description)
{ {
delete [] description;
description = copy_string(arg_description); description = copy_string(arg_description);
} }
@ -99,6 +112,16 @@ bool Plugin::DynamicPlugin() const
return dynamic; return dynamic;
} }
const char* Plugin::PluginDirectory() const
{
return base_dir;
}
const char* Plugin::PluginPath() const
{
return sopath;
}
void Plugin::SetAPIVersion(int arg_version) void Plugin::SetAPIVersion(int arg_version)
{ {
api_version = arg_version; api_version = arg_version;
@ -109,22 +132,26 @@ void Plugin::SetDynamicPlugin(bool arg_dynamic)
dynamic = arg_dynamic; dynamic = arg_dynamic;
} }
void Plugin::SetPluginLocation(const char* arg_dir, const char* arg_sopath)
{
delete [] base_dir;
delete [] sopath;
base_dir = copy_string(arg_dir);
sopath = copy_string(arg_sopath);
}
void Plugin::InitPreScript() void Plugin::InitPreScript()
{ {
} }
void Plugin::InitPostScript() void Plugin::InitPostScript()
{ {
for ( bif_init_func_list::const_iterator f = bif_inits.begin(); f != bif_inits.end(); f++ ) }
{
bif_init_func_result items = (**f)();
for ( bif_init_func_result::const_iterator i = items.begin(); i != items.end(); i++ ) void Plugin::InitBifs()
{ {
BifItem bi((*i).first, (BifItem::Type)(*i).second); for ( bif_init_func_list::const_iterator f = bif_inits.begin(); f != bif_inits.end(); f++ )
bif_items.push_back(bi); (**f)(this);
}
}
} }
Plugin::bif_item_list Plugin::BifItems() const Plugin::bif_item_list Plugin::BifItems() const
@ -143,6 +170,22 @@ Plugin::bif_item_list Plugin::CustomBifItems() const
return bif_item_list(); return bif_item_list();
} }
const char* Plugin::FileExtensions() const
{
return extensions ? extensions : "";
}
void Plugin::SetFileExtensions(const char* ext)
{
extensions = copy_string(ext);
}
bool Plugin::LoadFile(const char* file)
{
reporter->InternalError("Plugin::LoadFile not overriden for %s", file);
return false;
}
void Plugin::Done() void Plugin::Done()
{ {
for ( component_list::const_iterator i = components.begin(); i != components.end(); i++ ) for ( component_list::const_iterator i = components.begin(); i != components.end(); i++ )
@ -170,11 +213,23 @@ void Plugin::AddComponent(Component* c)
components.sort(component_cmp); components.sort(component_cmp);
} }
void Plugin::AddBifInitFunction(bif_init_func c) bool Plugin::LoadBroFile(const char* file)
{
add_input_file(file);
return true;
}
void Plugin::__AddBifInitFunction(bif_init_func c)
{ {
bif_inits.push_back(c); bif_inits.push_back(c);
} }
void Plugin::AddBifItem(const char* name, BifItem::Type type)
{
BifItem bi(name, (BifItem::Type)type);
bif_items.push_back(bi);
}
void Plugin::Describe(ODesc* d) const void Plugin::Describe(ODesc* d) const
{ {
d->Add("Plugin: "); d->Add("Plugin: ");
@ -190,7 +245,7 @@ void Plugin::Describe(ODesc* d) const
{ {
if ( version > 0 ) if ( version > 0 )
{ {
d->Add(" (version "); d->Add(" (dynamic, version ");
d->Add(version); d->Add(version);
d->Add(")"); d->Add(")");
} }
@ -201,6 +256,18 @@ void Plugin::Describe(ODesc* d) const
else else
d->Add(" (built-in)"); d->Add(" (built-in)");
switch ( PluginType() ) {
case STANDARD:
break;
case INTERPRETER:
d->Add( " (interpreter plugin)");
break;
default:
reporter->InternalError("unknown plugin type in Plugin::Describe");
}
d->Add("\n"); d->Add("\n");
if ( d->IsShort() ) if ( d->IsShort() )
@ -252,4 +319,46 @@ void Plugin::Describe(ODesc* d) const
} }
} }
InterpreterPlugin::InterpreterPlugin(int arg_priority)
{
priority = arg_priority;
}
InterpreterPlugin::~InterpreterPlugin()
{
}
int InterpreterPlugin::Priority() const
{
return priority;
}
Plugin::Type InterpreterPlugin::PluginType() const
{
return INTERPRETER;
}
Val* InterpreterPlugin::CallFunction(const Func* func, val_list* args)
{
return 0;
}
bool InterpreterPlugin::QueueEvent(Event* event)
{
return false;
}
void InterpreterPlugin::UpdateNetworkTime(double network_time)
{
}
void InterpreterPlugin::DrainEvents()
{
}
void InterpreterPlugin::DisableInterpreterPlugin() const
{
plugin_mgr->DisableInterpreterPlugin(this);
}

View file

@ -9,6 +9,8 @@
#include "Macros.h" #include "Macros.h"
class ODesc; class ODesc;
class Func;
class Event;
namespace plugin { namespace plugin {
@ -22,8 +24,6 @@ class BifItem {
public: public:
/** /**
* Type of the item. * Type of the item.
*
* The values here must match the integers that \c bifcl generated.
*/ */
enum Type { FUNCTION = 1, EVENT = 2, CONSTANT = 3, GLOBAL = 4, TYPE = 5 }; enum Type { FUNCTION = 1, EVENT = 2, CONSTANT = 3, GLOBAL = 4, TYPE = 5 };
@ -35,7 +35,7 @@ public:
* *
* @param type The type of the item. * @param type The type of the item.
*/ */
BifItem(const std::string& id, Type type); BifItem(const char* id, Type type);
/** /**
* Copy constructor. * Copy constructor.
@ -88,6 +88,26 @@ class Plugin {
public: public:
typedef std::list<Component *> component_list; typedef std::list<Component *> component_list;
typedef std::list<BifItem> bif_item_list; typedef std::list<BifItem> bif_item_list;
typedef std::list<std::pair<const char*, int> > bif_init_func_result;
typedef void (*bif_init_func)(Plugin *);
/**
* Type of a plugin. Plugin types are set implicitly by deriving from
* the corresponding base class. */
enum Type {
/**
* A standard plugin. This is the type for all plugins
* derived directly from \a Plugin. */
STANDARD,
/**
* An interpreter plugin. These plugins get hooked into the
* script interpreter and can modify, or even replace, its
* execution of Bro script code. To create an interpreter
* plugin, derive from \aInterpreterPlugin.
*/
INTERPRETER
};
/** /**
* Constructor. * Constructor.
@ -99,6 +119,11 @@ public:
*/ */
virtual ~Plugin(); virtual ~Plugin();
/**
* Returns the type of the plugin.
*/
virtual Type PluginType() const;
/** /**
* Returns the name of the plugin. * Returns the name of the plugin.
*/ */
@ -121,6 +146,23 @@ public:
*/ */
bool DynamicPlugin() const; bool DynamicPlugin() const;
/**
* Returns a colon-separated list of file extensions the plugin handles.
*/
const char* FileExtensions() const;
/**
* For dynamic plugins, returns the base directory from which it was
* loaded. For static plugins, returns null.
**/
const char* PluginDirectory() const;
/**
* For dynamic plugins, returns the full path to the shared library
* from which it was loaded. For static plugins, returns null.
**/
const char* PluginPath() const;
/** /**
* Returns the internal API version that this plugin relies on. Only * Returns the internal API version that this plugin relies on. Only
* plugins that match Bro's current API version may be used. For * plugins that match Bro's current API version may be used. For
@ -173,9 +215,38 @@ public:
*/ */
void Describe(ODesc* d) const; void Describe(ODesc* d) const;
/**
* Registering an individual BiF that the plugin defines. The
* information is for informational purpuses only and will show up in
* the result of BifItems() as well as in the Describe() output.
* Another way to add this information is via overriding
* CustomBifItems().
*
* @param name The name of the BiF item.
*
* @param type The item's type.
*/
void AddBifItem(const char* name, BifItem::Type type);
/**
* Adds a file to the list of files Bro loads at startup. This will
* normally be a Bro script, but it passes through the plugin system
* as well to load files with other extensions as supported by any of
* the current plugins. In other words, calling this method is
* similar to given a file on the command line. Note that the file
* may be only queued for now, and actually loaded later.
*
* This method must not be called after InitPostScript().
*
* @param file The file to load. It will be searched along the standard paths.
*
* @return True if successful (which however may only mean
* "successfully queued").
*/
bool LoadBroFile(const char* file);
protected: protected:
typedef std::list<std::pair<const char*, int> > bif_init_func_result; friend class Manager;
typedef bif_init_func_result (*bif_init_func)();
/** /**
* Sets the plugins name. * Sets the plugins name.
@ -212,6 +283,28 @@ protected:
*/ */
void SetDynamicPlugin(bool dynamic); void SetDynamicPlugin(bool dynamic);
/**
* Reports the extensions of input files the plugin handles. If Bro
* wants to load a file with one of these extensions it will pass
* them to LoadFile() and then then ignore otherwise.
*
* ext: A list of colon-separated file extensions the plugin handles.
*/
void SetFileExtensions(const char* ext);
/**
* Sets the base directory and shared library path from which the
* plugin was loaded. This should be called only from the manager for
* dynamic plugins.
*
* @param dir The plugin directory. The functions makes an internal
* copy of string.
*
* @param sopath The full path the shared library loaded. The
* functions makes an internal copy of string.
*/
void SetPluginLocation(const char* dir, const char* sopath);
/** /**
* Takes ownership. * Takes ownership.
*/ */
@ -227,17 +320,41 @@ protected:
*/ */
virtual bif_item_list CustomBifItems() const; virtual bif_item_list CustomBifItems() const;
/**
* Virtual method that can be overriden by derived class to load
* files with extensions reported via SetFileExtension().
*
* This method will be called between InitPreScript() and
* InitPostScript(), but with no further order or timing guaranteed.
* It will be called once for each file encountered with of the
* specificed extensions (i.e., duplicates are filtered out
* automatically).
*
* @return True if the file was loaded successfuly, false if not. Bro
* will abort in the latter case.
*/
virtual bool LoadFile(const char* file);
/**
* Initializes the BiF items added with AddBifItem(). Internal method
* that will be called by the manager at the right time.
*/
void InitBifs();
/** /**
* Internal function adding an entry point for registering * Internal function adding an entry point for registering
* auto-generated BiFs. * auto-generated BiFs.
*/ */
void AddBifInitFunction(bif_init_func c); void __AddBifInitFunction(bif_init_func c);
private: private:
typedef std::list<bif_init_func> bif_init_func_list; typedef std::list<bif_init_func> bif_init_func_list;
const char* name; const char* name;
const char* description; const char* description;
const char* base_dir;
const char* sopath;
const char* extensions;
int version; int version;
int api_version; int api_version;
bool dynamic; bool dynamic;
@ -247,6 +364,105 @@ private:
bif_init_func_list bif_inits; bif_init_func_list bif_inits;
}; };
/**
* Class for hooking into script execution. An interpreter plugin can do
* everything a normal plugin can, yet will also be interfaced to the script
* interpreter for learning about, modidying, or potentially replacing
* standard functionality.
*/
class InterpreterPlugin : public Plugin {
public:
/**
* Constructor.
*
* @param priority Imposes an order on InterpreterPlugins in which
* they'll be chained. The higher the prioritu, the earlier a plugin
* is called when the interpreter goes through the chain. */
InterpreterPlugin(int priority);
/**
* Destructor.
*/
virtual ~InterpreterPlugin();
/**
* Returns the plugins priority.
*/
int Priority() const;
/**
* Callback for executing a function/event/hook. Whenever the script
* interpreter is about to execution a function, it first gives all
* InterpreterPlugins a chance to handle the call (in the order of their
* priorities). A plugin can either just inspect the call, or replace it
* (i.e., prevent the interpreter from executing it). In the latter case
* it must provide a matching return value.
*
* The default implementation does never handle the call in any way.
*
* @param func The function being called.
*
* @param args The function arguments. The method can modify the list
* in place long as it ensures matching types and correct reference
* counting.
*
* @return If the plugin handled the call, a +1 Val with the result
* value to pass back to the interpreter (for void functions and
* events any \a Val is fine; it will be ignored; best to use a \c
* TYPE_ANY). If the plugin did not handle the call, it must return
* null.
*/
virtual Val* CallFunction(const Func* func, val_list* args);
/**
* Callback for raising an event. Whenever the script interpreter is
* about to queue an event for later execution, it first gives all
* InterpreterPlugins a chance to handle the queuing otherwise (in the
* order of their priorities). A plugin can either just inspect the
* event, or take it over (i.e., prevent the interpreter from queuing it
* it).
*
* The default implementation does never handle the queuing in any way.
*
* @param event The even to be queued. The method can modify it in in
* place long as it ensures matching types and correct reference
* counting.
*
* @return True if the plugin took charge of the event; in that case it
* must have assumed ownership of the event and the intpreter will not do
* anything further with it. False otherwise.
*
*/
virtual bool QueueEvent(Event* event);
/**
* Callback for updates in network time. This method will be called
* whenever network time is advanced.
*
* @param networkt_time The new network time.
*/
virtual void UpdateNetworkTime(double network_time);
/**
* Callback for event queue draining. This method will be called
* whenever the event manager has drained it queue.
*/
virtual void DrainEvents();
/**
* Disables interpreter hooking. The functionality of the Plugin base
* class remains available.
*/
void DisableInterpreterPlugin() const;
// Overridden from base class.
virtual Type PluginType() const;
private:
int priority;
};
} }
#endif #endif

View file

@ -30,6 +30,8 @@
#include "analyzer/Analyzer.h" #include "analyzer/Analyzer.h"
#include "plugin/Manager.h"
extern YYLTYPE yylloc; // holds start line and column of token extern YYLTYPE yylloc; // holds start line and column of token
extern int print_loaded_scripts; extern int print_loaded_scripts;
extern int generate_documentation; extern int generate_documentation;
@ -579,6 +581,24 @@ YYLTYPE GetCurrentLocation()
static int load_files(const char* orig_file) static int load_files(const char* orig_file)
{ {
int rc = plugin_mgr->TryLoadFile(orig_file);
if ( rc == 1 )
return 0; // A plugin took care of it, just skip.
if ( rc == 0 )
{
if ( ! reporter->Errors() )
// This is just in case the plugin failed to report
// the error itself, in which case we want to at
// least tell the user that something went wrong.
reporter->Error("Plugin reported error loading %s", orig_file);
exit(1);
}
assert(rc == -1); // No plugin in charge of this file.
// Whether we pushed on a FileInfo that will restore the // Whether we pushed on a FileInfo that will restore the
// current module after the final file has been scanned. // current module after the final file has been scanned.
bool did_module_restore = false; bool did_module_restore = false;

View file

@ -3,3 +3,4 @@
#define BRO_BUILD_SOURCE_PATH "@CMAKE_BINARY_DIR@/src" #define BRO_BUILD_SOURCE_PATH "@CMAKE_BINARY_DIR@/src"
#define BRO_BUILD_SCRIPTS_PATH "@CMAKE_BINARY_DIR@/scripts" #define BRO_BUILD_SCRIPTS_PATH "@CMAKE_BINARY_DIR@/scripts"
#define BRO_MAGIC_INSTALL_PATH "@BRO_MAGIC_INSTALL_PATH@" #define BRO_MAGIC_INSTALL_PATH "@BRO_MAGIC_INSTALL_PATH@"
#define BRO_PLUGIN_INSTALL_PATH "@BRO_PLUGIN_INSTALL_PATH@"

View file

@ -619,13 +619,13 @@ bool ensure_dir(const char *dirname)
return true; return true;
} }
bool is_dir(const char* path) bool is_dir(const std::string& path)
{ {
struct stat st; struct stat st;
if ( stat(path, &st) < 0 ) if ( stat(path.c_str(), &st) < 0 )
{ {
if ( errno != ENOENT ) if ( errno != ENOENT )
reporter->Warning("can't stat %s: %s", path, strerror(errno)); reporter->Warning("can't stat %s: %s", path.c_str(), strerror(errno));
return false; return false;
} }
@ -633,6 +633,37 @@ bool is_dir(const char* path)
return S_ISDIR(st.st_mode); return S_ISDIR(st.st_mode);
} }
bool is_file(const std::string& path)
{
struct stat st;
if ( stat(path.c_str(), &st) < 0 )
{
if ( errno != ENOENT )
reporter->Warning("can't stat %s: %s", path.c_str(), strerror(errno));
return false;
}
return S_ISREG(st.st_mode);
}
string strreplace(const string& s, const string& o, const string& n)
{
string r = s;
while ( true )
{
size_t i = r.find(o);
if ( i == std::string::npos )
break;
r.replace(i, o.size(), n);
}
return r;
}
int hmac_key_set = 0; int hmac_key_set = 0;
uint8 shared_hmac_md5_key[16]; uint8 shared_hmac_md5_key[16];
@ -872,7 +903,11 @@ int int_list_cmp(const void* v1, const void* v2)
return 1; return 1;
} }
const char* bro_path() static string bro_path_value;
const std::string& bro_path()
{
if ( bro_path_value.empty() )
{ {
const char* path = getenv("BROPATH"); const char* path = getenv("BROPATH");
if ( ! path ) if ( ! path )
@ -881,7 +916,18 @@ const char* bro_path()
BRO_SCRIPT_INSTALL_PATH "/policy" ":" BRO_SCRIPT_INSTALL_PATH "/policy" ":"
BRO_SCRIPT_INSTALL_PATH "/site"; BRO_SCRIPT_INSTALL_PATH "/site";
return path; bro_path_value = path;
}
return bro_path_value;
}
extern void add_to_bro_path(const string& dir)
{
// Make sure path is initialized.
bro_path();
bro_path_value += string(":") + dir;
} }
const char* bro_magic_path() const char* bro_magic_path()
@ -894,6 +940,16 @@ const char* bro_magic_path()
return path; return path;
} }
const char* bro_plugin_path()
{
const char* path = getenv("BRO_PLUGINS");
if ( ! path )
path = BRO_PLUGIN_INSTALL_PATH;
return path;
}
string bro_prefixes() string bro_prefixes()
{ {
string rval; string rval;
@ -1089,9 +1145,9 @@ FILE* search_for_file(const char* filename, const char* ext,
// @loads can be referenced relatively. // @loads can be referenced relatively.
if ( current_scanned_file_path != "" && filename[0] == '.' ) if ( current_scanned_file_path != "" && filename[0] == '.' )
safe_snprintf(path, sizeof(path), "%s:%s", safe_snprintf(path, sizeof(path), "%s:%s",
current_scanned_file_path.c_str(), bro_path()); current_scanned_file_path.c_str(), bro_path().c_str());
else else
safe_strncpy(path, bro_path(), sizeof(path)); safe_strncpy(path, bro_path().c_str(), sizeof(path));
char* dir_beginning = path; char* dir_beginning = path;
char* dir_ending = path; char* dir_ending = path;
@ -1505,25 +1561,16 @@ void get_memory_usage(unsigned int* total, unsigned int* malloced)
if ( malloced ) if ( malloced )
*malloced = mi.uordblks; *malloced = mi.uordblks;
ret_total = mi.arena; #endif
if ( total )
*total = ret_total;
#else
struct rusage r; struct rusage r;
getrusage(RUSAGE_SELF, &r); getrusage(RUSAGE_SELF, &r);
if ( malloced )
*malloced = 0;
// At least on FreeBSD it's in KB. // At least on FreeBSD it's in KB.
ret_total = r.ru_maxrss * 1024; ret_total = r.ru_maxrss * 1024;
if ( total ) if ( total )
*total = ret_total; *total = ret_total;
#endif
// return ret_total;
} }
#ifdef malloc #ifdef malloc

View file

@ -147,7 +147,13 @@ extern const char* fmt_access_time(double time);
extern bool ensure_dir(const char *dirname); extern bool ensure_dir(const char *dirname);
// Returns true if path exists and is a directory. // Returns true if path exists and is a directory.
bool is_dir(const char* path); bool is_dir(const std::string& path);
// Returns true if path exists and is a file.
bool is_file(const std::string& path);
// Replaces all occurences of *o* in *s* with *n*.
extern std::string strreplace(const std::string& s, const std::string& o, const std::string& n);
extern uint8 shared_hmac_md5_key[16]; extern uint8 shared_hmac_md5_key[16];
@ -202,9 +208,12 @@ static const SourceID SOURCE_LOCAL = 0;
extern void pinpoint(); extern void pinpoint();
extern int int_list_cmp(const void* v1, const void* v2); extern int int_list_cmp(const void* v1, const void* v2);
extern const char* bro_path(); extern const std::string& bro_path();
extern void add_to_bro_path(const std::string& dir);
extern const char* bro_magic_path(); extern const char* bro_magic_path();
extern std::string bro_prefixes(); extern const char* bro_plugin_path();
extern const char* bro_prefixes();
std::string dot_canon(std::string path, std::string file, std::string prefix = ""); std::string dot_canon(std::string path, std::string file, std::string prefix = "");
const char* normalize_path(const char* path); const char* normalize_path(const char* path);
void get_script_subpath(const std::string& full_filename, const char** subpath); void get_script_subpath(const std::string& full_filename, const char** subpath);

View file

@ -0,0 +1,6 @@
Plugin: Demo::Foo - A Foo test analyzer (dynamic, version 1)
[Analyzer] Foo (ANALYZER_FOO, enabled)
[Event] foo_message
===
foo_message, [orig_h=::1, orig_p=37927/tcp, resp_h=::1, resp_p=4242/tcp], Hello, Foo!^J

View file

@ -0,0 +1,11 @@
Plugin: Demo::Foo - <Insert brief description of plugin> (dynamic, version 1)
[Event] plugin_event
[Function] hello_plugin_world
===
plugin: automatically loaded at startup
calling bif, Hello from the plugin!
===
plugin: automatically loaded at startup
calling bif, Hello from the plugin!
plugin: manually loaded

Binary file not shown.

View file

@ -0,0 +1,20 @@
project(Bro-Plugin-Demo-Foo)
cmake_minimum_required(VERSION 2.6.3)
if ( NOT BRO_DIST )
message(FATAL_ERROR "BRO_DIST not set")
endif ()
set(CMAKE_MODULE_PATH ${BRO_DIST}/cmake)
include(BroPlugin)
bro_plugin_begin(Demo Foo)
bro_plugin_cc(src/Plugin.cc)
bro_plugin_cc(src/Foo.cc)
bro_plugin_bif(src/events.bif)
bro_plugin_bif(src/functions.bif)
bro_plugin_pac(src/foo.pac src/foo-protocol.pac src/foo-analyzer.pac)
bro_plugin_end()

View file

@ -0,0 +1,2 @@
A place-holder file that marks a directory as holding a Bro plugin.
Content is ignored.

View file

@ -0,0 +1,7 @@
const ports = { 4242/tcp };
event bro_init() &priority=5
{
Analyzer::register_for_ports(Analyzer::ANALYZER_FOO, ports);
}

View file

@ -0,0 +1 @@
@load Demo/Foo/base/main

View file

@ -0,0 +1,59 @@
#include "Foo.h"
#include "foo_pac.h"
#include "events.bif.h"
#include <analyzer/protocol/tcp/TCP_Reassembler.h>
using namespace analyzer::Foo;
Foo_Analyzer::Foo_Analyzer(Connection* conn)
: tcp::TCP_ApplicationAnalyzer("Foo", conn)
{
interp = new binpac::Foo::Foo_Conn(this);
}
Foo_Analyzer::~Foo_Analyzer()
{
delete interp;
}
void Foo_Analyzer::Done()
{
tcp::TCP_ApplicationAnalyzer::Done();
interp->FlowEOF(true);
interp->FlowEOF(false);
}
void Foo_Analyzer::EndpointEOF(bool is_orig)
{
tcp::TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
interp->FlowEOF(is_orig);
}
void Foo_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
{
tcp::TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
assert(TCP());
if ( TCP()->IsPartial() )
// punt on partial.
return;
try
{
interp->NewData(orig, data, data + len);
}
catch ( const binpac::Exception& e )
{
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
}
}
void Foo_Analyzer::Undelivered(int seq, int len, bool orig)
{
tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
interp->NewGap(orig, len);
}

View file

@ -0,0 +1,31 @@
#ifndef BRO_PLUGIN_DEMO_FOO_H
#define BRO_PLUGIN_DEMO_FOO_H
#include "analyzer/protocol/tcp/TCP.h"
#include "analyzer/protocol/pia/PIA.h"
namespace binpac { namespace Foo { class Foo_Conn; } }
namespace analyzer { namespace Foo {
class Foo_Analyzer : public tcp::TCP_ApplicationAnalyzer {
public:
Foo_Analyzer(Connection* conn);
~Foo_Analyzer();
virtual void Done();
virtual void DeliverStream(int len, const u_char* data, bool orig);
virtual void Undelivered(int seq, int len, bool orig);
virtual void EndpointEOF(bool is_orig);
static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn)
{ return new Foo_Analyzer(conn); }
protected:
binpac::Foo::Foo_Conn* interp;
};
} } // namespace analyzer::*
#endif

View file

@ -0,0 +1,12 @@
#include <plugin/Plugin.h>
#include "Foo.h"
BRO_PLUGIN_BEGIN(Demo, Foo)
BRO_PLUGIN_VERSION(1);
BRO_PLUGIN_DESCRIPTION("A Foo test analyzer");
BRO_PLUGIN_ANALYZER("Foo", Foo::Foo_Analyzer);
BRO_PLUGIN_BIF_FILE(events);
BRO_PLUGIN_BIF_FILE(functions);
BRO_PLUGIN_END

View file

@ -0,0 +1,2 @@
event foo_message%(c: connection, data: string%);

View file

@ -0,0 +1,15 @@
refine connection Foo_Conn += {
function Foo_data(msg: Foo_Message): bool
%{
StringVal* data = new StringVal(${msg.data}.length(), (const char*) ${msg.data}.data());
BifEvent::generate_foo_message(bro_analyzer(), bro_analyzer()->Conn(), data);
return true;
%}
};
refine typeattr Foo_Message += &let {
proc: bool = $context.connection.Foo_data(this);
};

View file

@ -0,0 +1,4 @@
type Foo_Message(is_orig: bool) = record {
data: bytestring &restofdata;
};

View file

@ -0,0 +1,26 @@
%include binpac.pac
%include bro.pac
%extern{
#include "Foo.h"
#include "events.bif.h"
%}
analyzer Foo withcontext {
connection: Foo_Conn;
flow: Foo_Flow;
};
connection Foo_Conn(bro_analyzer: BroAnalyzer) {
upflow = Foo_Flow(true);
downflow = Foo_Flow(false);
};
%include foo-protocol.pac
flow Foo_Flow(is_orig: bool) {
datagram = Foo_Message(is_orig) withcontext(connection, this);
};
%include foo-analyzer.pac

View file

@ -0,0 +1,13 @@
# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin Demo Foo
# @TEST-EXEC: cp -r %DIR/analyzer-plugin/* .
# @TEST-EXEC: make BRO=${DIST}
# @TEST-EXEC: BROPLUGINS=`pwd` bro -NN | awk '/^Plugin:.*Demo/ {p=1; print; next} /^Plugin:/{p=0} p==1{print}' >>output
# @TEST-EXEC: echo === >>output
# @TEST-EXEC: BROPLUGINS=`pwd` bro -r $TRACES/port4242.trace %INPUT >>output
# @TEST-EXEC: btest-diff output
event foo_message(c: connection, data: string)
{
print "foo_message", c$id, data;
}

View file

@ -0,0 +1,45 @@
# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin Demo Foo
# @TEST-EXEC: bash %INPUT
# @TEST-EXEC: make BRO=${DIST}
# @TEST-EXEC: BROPLUGINS=`pwd` bro -NN | awk '/^Plugin:.*Demo/ {p=1; print; next} /^Plugin:/{p=0} p==1{print}' >>output
# @TEST-EXEC: echo === >>output
# @TEST-EXEC: BROPLUGINS=`pwd` bro -r $TRACES/empty.trace >>output
# @TEST-EXEC: echo === >>output
# @TEST-EXEC: BROPLUGINS=`pwd` bro demo/foo -r $TRACES/empty.trace >>output
# @TEST-EXEC: btest-diff output
cat >scripts/__load__.bro <<EOF
@load ./demo/foo/base/at-startup.bro
EOF
cat >scripts/demo/foo/__load__.bro <<EOF
@load ./manually.bro
EOF
cat >scripts/demo/foo/manually.bro <<EOF
event bro_init()
{
print "plugin: manually loaded";
}
EOF
mkdir -p scripts/demo/foo/base/
cat >scripts/demo/foo/base/at-startup.bro <<EOF
event bro_init()
{
print "plugin: automatically loaded at startup";
print "calling bif", hello_plugin_world();
}
EOF
cat >src/functions.bif <<EOF
function hello_plugin_world%(%): string
%{
return new StringVal("Hello from the plugin!");
%}
EOF
cat >src/events.bif <<EOF
event plugin_event%(foo: count%);
EOF