diff --git a/doc/script-reference/log-files.rst b/doc/script-reference/log-files.rst index e8550ee41c..a0720ff7c9 100644 --- a/doc/script-reference/log-files.rst +++ b/doc/script-reference/log-files.rst @@ -14,6 +14,8 @@ Network Protocols +============================+=======================================+=================================+ | conn.log | TCP/UDP/ICMP connections | :bro:type:`Conn::Info` | +----------------------------+---------------------------------------+---------------------------------+ +| config.log | Configuration option changes | :bro:type:`Config::Info` | ++----------------------------+---------------------------------------+---------------------------------+ | dce_rpc.log | Distributed Computing Environment/RPC | :bro:type:`DCE_RPC::Info` | +----------------------------+---------------------------------------+---------------------------------+ | dhcp.log | DHCP leases | :bro:type:`DHCP::Info` | diff --git a/scripts/base/frameworks/config/README b/scripts/base/frameworks/config/README new file mode 100644 index 0000000000..95193b49a3 --- /dev/null +++ b/scripts/base/frameworks/config/README @@ -0,0 +1,2 @@ +The configuration famework provides a way to change the Bro configuration +in "option" values at run-time. diff --git a/scripts/base/frameworks/config/__load__.bro b/scripts/base/frameworks/config/__load__.bro new file mode 100644 index 0000000000..0a7a8d0713 --- /dev/null +++ b/scripts/base/frameworks/config/__load__.bro @@ -0,0 +1,2 @@ +@load ./main +@load ./input diff --git a/scripts/base/frameworks/config/input.bro b/scripts/base/frameworks/config/input.bro new file mode 100644 index 0000000000..9ebcd3a4a9 --- /dev/null +++ b/scripts/base/frameworks/config/input.bro @@ -0,0 +1,76 @@ +##! File input for the configuration framework using the input framework. + +@load ./main +@load base/frameworks/cluster + +module Config; + +export { + ## Configuration files that will be read off disk. Files are reread + ## every time they are updated so updates should be atomic with "mv" + ## instead of writing the file in place. + ## + ## If the same configuration option is defined in several files with + ## different values, behavior is unspecified. + const config_files: set[string] = {} &redef; + + ## Read specified configuration file and apply values; updates to file + ## are not tracked. + global read_config: function(filename: string); +} + +global current_config: table[string] of string = table(); + +type ConfigItem: record { + option_nv: string; +}; + +type EventFields: record { + option_name: string; + option_val: string; +}; + +event config_line(description: Input::EventDescription, tpe: Input::Event, p: EventFields) + { + } + +event bro_init() &priority=5 + { + if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) + return; + + for ( fi in config_files ) + Input::add_table([$reader=Input::READER_CONFIG, + $mode=Input::REREAD, + $source=fi, + $name=cat("config-", fi), + $idx=ConfigItem, + $val=ConfigItem, + $want_record=F, + $destination=current_config]); + } + +event InputConfig::new_value(name: string, source: string, id: string, value: any) + { + if ( sub_bytes(name, 1, 7) != "config-" || source !in config_files ) + if ( sub_bytes(name, 1, 15) != "config-oneshot-" ) + return; + + Option::set(id, value, source); + } + +function read_config(filename: string) + { + if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) + return; + + local iname = cat("config-oneshot-", filename); + + Input::add_event([$reader=Input::READER_CONFIG, + $mode=Input::MANUAL, + $source=filename, + $name=iname, + $fields=EventFields, + $ev=config_line]); + Input::remove(iname); + } diff --git a/scripts/base/frameworks/config/main.bro b/scripts/base/frameworks/config/main.bro new file mode 100644 index 0000000000..372bb10eb4 --- /dev/null +++ b/scripts/base/frameworks/config/main.bro @@ -0,0 +1,77 @@ +##! The configuration framework provides a way to change Bro options +##! (as specified by the option keyword) at runtime. It also logs runtime changes +##! to options to config.log. + +module Config; + +export { + ## The config logging stream identifier. + redef enum Log::ID += { LOG }; + + ## Represents the data in config.log. + type Info: record { + ## Timestamp at which the configuration change occured. + ts: time &log; + ## ID of the value that was changed. + id: string &log; + ## Value before the change. + old_value: string &log; + ## Value after the change. + new_value: string &log; + ## Optional location that triggered the change. + location: string &optional &log; + }; + + ## Event that can be handled to access the :bro:type:`Config::Info` + ## record as it is sent on to the logging framework. + global log_config: event(rec: Info); +} + +function format_value(value: any) : string + { + local tn = type_name(value); + local part: string_vec = vector(); + if ( /^set/ in tn ) + { + local it: set[bool] = value; + for ( sv in it ) + part[|part|] = cat(sv); + return join_string_vec(part, ","); + } + else if ( /^vector/ in tn ) + { + local vit: vector of any = value; + for ( i in vit ) + part[|part|] = cat(vit[i]); + return join_string_vec(part, ","); + } + else if ( tn == "string" ) + return value; + + return cat(value); + } + +function config_option_changed(ID: string, new_value: any, location: string): any + { + local log = Info($ts=network_time(), $id=ID, $old_value=format_value(lookup_ID(ID)), $new_value=format_value(new_value)); + if ( location != "" ) + log$location = location; + Log::write(LOG, log); + return new_value; + } + +event bro_init() &priority=10 + { + Log::create_stream(LOG, [$columns=Info, $ev=log_config, $path="config"]); + + # Iterate over all existing options and add ourselves as change handlers with + # a low priority so that we can log the changes. + local gids = global_ids(); + for ( i in gids ) + { + if ( ! gids[i]$option_value ) + next; + + Option::set_change_handler(i, config_option_changed, -100); + } + } diff --git a/scripts/base/frameworks/input/__load__.bro b/scripts/base/frameworks/input/__load__.bro index 9280af0258..1d81e43573 100644 --- a/scripts/base/frameworks/input/__load__.bro +++ b/scripts/base/frameworks/input/__load__.bro @@ -3,4 +3,5 @@ @load ./readers/raw @load ./readers/benchmark @load ./readers/binary +@load ./readers/config @load ./readers/sqlite diff --git a/scripts/base/frameworks/input/readers/ascii.bro b/scripts/base/frameworks/input/readers/ascii.bro index 1d4072e118..ff1d30f3a9 100644 --- a/scripts/base/frameworks/input/readers/ascii.bro +++ b/scripts/base/frameworks/input/readers/ascii.bro @@ -9,7 +9,7 @@ export { ## Please note that the separator has to be exactly one character long. const separator = Input::separator &redef; - ## Separator between set elements. + ## Separator between set and vector elements. ## Please note that the separator has to be exactly one character long. const set_separator = Input::set_separator &redef; diff --git a/scripts/base/frameworks/input/readers/config.bro b/scripts/base/frameworks/input/readers/config.bro new file mode 100644 index 0000000000..f97323d4a5 --- /dev/null +++ b/scripts/base/frameworks/input/readers/config.bro @@ -0,0 +1,42 @@ +##! Interface for the config input reader. + +module InputConfig; + +export { + ## Separator between set and vector elements. + ## Please note that the separator has to be exactly one character long. + const set_separator = Input::set_separator &redef; + + ## String to use for empty fields. + const empty_field = Input::empty_field &redef; + + ## Fail on file read problems. If set to true, the config + ## input reader will fail when encountering any problems + ## while reading a file different from invalid lines. + ## Examples of such problems are permission problems, or + ## missing files. + ## When set to false, these problems will be ignored. This + ## has an especially big effect for the REREAD mode, which will + ## seamlessly recover from read errors when a file is + ## only temporarily inaccessible. For MANUAL or STREAM files, + ## errors will most likely still be fatal since no automatic + ## re-reading of the file is attempted. + ## Individual readers can use a different value using + ## the $config table. + const fail_on_file_problem = F &redef; + + ## Event that is called when a config option is added or changes. + ## + ## Note - this does not track the reason for a change (new, changed), + ## and also does not track removals. If you need this, combine the event + ## with a table reader. + ## + ## name: name of the input stream. + ## + ## source: source of the input stream. + ## + ## id: ID of the configuration option being set. + ## + ## value: new value of the configuration option being set. + global new_value: event(name: string, source: string, id: string, value: any); +} diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index f2a6816d9d..0756ce1762 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -631,6 +631,7 @@ type script_id: record { exported: bool; ##< True if the identifier is exported. constant: bool; ##< True if the identifier is a constant. enum_constant: bool; ##< True if the identifier is an enum value. + option_value: bool; ##< True if the identifier is an option. redefinable: bool; ##< True if the identifier is declared with the :bro:attr:`&redef` attribute. value: any &optional; ##< The current value of the identifier. }; diff --git a/scripts/base/init-default.bro b/scripts/base/init-default.bro index 65b41305c7..8a942eb4df 100644 --- a/scripts/base/init-default.bro +++ b/scripts/base/init-default.bro @@ -36,6 +36,7 @@ @load base/frameworks/control @load base/frameworks/cluster @load base/frameworks/intel +@load base/frameworks/config @load base/frameworks/reporter @load base/frameworks/sumstats @load base/frameworks/tunnels diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a564f16b32..982c37a79b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -124,6 +124,7 @@ set(BIF_SRCS types.bif strings.bif reporter.bif + option.bif ) foreach (bift ${BIF_SRCS}) diff --git a/src/Expr.cc b/src/Expr.cc index bea43ff7c4..a0456fda31 100644 --- a/src/Expr.cc +++ b/src/Expr.cc @@ -283,6 +283,9 @@ Expr* NameExpr::MakeLvalue() if ( id->IsConst() && ! in_const_init ) ExprError("const is not a modifiable lvalue"); + if ( id->IsOption() && ! in_const_init ) + ExprError("option is not a modifiable lvalue"); + return new RefExpr(this); } diff --git a/src/Func.cc b/src/Func.cc index f7877d04a0..e7167eb8ee 100644 --- a/src/Func.cc +++ b/src/Func.cc @@ -647,11 +647,13 @@ void builtin_error(const char* msg, BroObj* arg) #include "stats.bif.func_h" #include "reporter.bif.func_h" #include "strings.bif.func_h" +#include "option.bif.func_h" #include "bro.bif.func_def" #include "stats.bif.func_def" #include "reporter.bif.func_def" #include "strings.bif.func_def" +#include "option.bif.func_def" #include "__all__.bif.cc" // Autogenerated for compiling in the bif_target() code. #include "__all__.bif.register.cc" // Autogenerated for compiling in the bif_target() code. @@ -676,6 +678,7 @@ void init_builtin_funcs() #include "stats.bif.func_init" #include "reporter.bif.func_init" #include "strings.bif.func_init" +#include "option.bif.func_init" did_builtin_init = true; } diff --git a/src/ID.cc b/src/ID.cc index efc488449b..a12c856ac0 100644 --- a/src/ID.cc +++ b/src/ID.cc @@ -21,12 +21,13 @@ ID::ID(const char* arg_name, IDScope arg_scope, bool arg_is_export) name = copy_string(arg_name); scope = arg_scope; is_export = arg_is_export; + is_option = false; type = 0; val = 0; attrs = 0; - is_const = 0; - is_enum_const = 0; - is_type = 0; + is_const = false; + is_enum_const = false; + is_type = false; offset = 0; infer_return_type = false; @@ -41,6 +42,8 @@ ID::~ID() Unref(type); Unref(attrs); + for ( auto element : option_handlers ) + Unref(element.second); if ( ! weak_ref ) Unref(val); } @@ -772,3 +775,18 @@ void ID::UpdateValID() } #endif +void ID::AddOptionHandler(Func* callback, int priority) + { + option_handlers.insert({priority, callback}); + } + +vector ID::GetOptionHandlers() const + { + // multimap is sorted + // It might be worth caching this if we expect it to be called + // a lot... + vector v; + for ( auto& element : option_handlers ) + v.push_back(element.second); + return v; + } diff --git a/src/ID.h b/src/ID.h index 2e0d5708a9..7e384d0046 100644 --- a/src/ID.h +++ b/src/ID.h @@ -11,6 +11,7 @@ class Val; class SerialInfo; +class Func; typedef enum { INIT_NONE, INIT_FULL, INIT_EXTRA, INIT_REMOVE, } init_class; typedef enum { SCOPE_FUNCTION, SCOPE_MODULE, SCOPE_GLOBAL } IDScope; @@ -34,7 +35,7 @@ public: BroType* Type() { return type; } const BroType* Type() const { return type; } - void MakeType() { is_type = 1; } + void MakeType() { is_type = true; } BroType* AsType() { return is_type ? Type() : 0; } const BroType* AsType() const { return is_type ? Type() : 0; } @@ -51,21 +52,24 @@ public: void SetVal(Val* v, init_class c); void SetVal(Expr* ev, init_class c); - int HasVal() const { return val != 0; } + bool HasVal() const { return val != 0; } Val* ID_Val() { return val; } const Val* ID_Val() const { return val; } void ClearVal(); - void SetConst() { is_const = 1; } - int IsConst() const { return is_const; } + void SetConst() { is_const = true; } + bool IsConst() const { return is_const; } - void SetEnumConst() { is_enum_const = 1; } - int IsEnumConst() const { return is_enum_const; } + void SetOption() { is_option = true; } + bool IsOption() const { return is_option; } + + void SetEnumConst() { is_enum_const = true; } + bool IsEnumConst() const { return is_enum_const; } void SetOffset(int arg_offset) { offset = arg_offset; } int Offset() const { return offset; } - int IsRedefinable() const { return FindAttr(ATTR_REDEF) != 0; } + bool IsRedefinable() const { return FindAttr(ATTR_REDEF) != 0; } // Returns true if ID is one of those internal globally unique IDs // to which MutableVals are bound (there name start with a '#'). @@ -97,12 +101,18 @@ public: bool Serialize(SerialInfo* info) const; static ID* Unserialize(UnserialInfo* info); - bool DoInferReturnType() { return infer_return_type; } + bool DoInferReturnType() const + { return infer_return_type; } void SetInferReturnType(bool infer) - { infer_return_type = infer; } + { infer_return_type = infer; } virtual TraversalCode Traverse(TraversalCallback* cb) const; + bool HasOptionHandlers() const + { return !option_handlers.empty(); } + void AddOptionHandler(Func* callback, int priority); + vector GetOptionHandlers() const; + protected: ID() { name = 0; type = 0; val = 0; attrs = 0; } @@ -119,10 +129,12 @@ protected: IDScope scope; bool is_export; BroType* type; - int is_const, is_enum_const, is_type; + bool is_const, is_enum_const, is_type, is_option; int offset; Val* val; Attributes* attrs; + // contains list of functions that are called when an option changes + std::multimap option_handlers; bool infer_return_type; bool weak_ref; diff --git a/src/Var.cc b/src/Var.cc index e923e2ec37..ec7c1322bf 100644 --- a/src/Var.cc +++ b/src/Var.cc @@ -142,6 +142,11 @@ static void make_var(ID* id, BroType* t, init_class c, Expr* init, id->Error("&persistant/synchronized with constant"); return; } + else if ( dt == VAR_OPTION ) + { + id->Error("&persistant/synchronized with option"); + return; + } if ( ! id->IsGlobal() ) { @@ -206,6 +211,13 @@ static void make_var(ID* id, BroType* t, init_class c, Expr* init, id->SetConst(); } + if ( dt == VAR_OPTION ) + { + if ( ! init ) + id->Error("option variable must be initialized"); + + id->SetOption(); + } id->UpdateValAttrs(); diff --git a/src/Var.h b/src/Var.h index bcdd45dad2..831c10e90c 100644 --- a/src/Var.h +++ b/src/Var.h @@ -10,7 +10,7 @@ class Func; class EventHandlerPtr; -typedef enum { VAR_REGULAR, VAR_CONST, VAR_REDEF, } decl_type; +typedef enum { VAR_REGULAR, VAR_CONST, VAR_REDEF, VAR_OPTION, } decl_type; extern void add_global(ID* id, BroType* t, init_class c, Expr* init, attr_list* attr, decl_type dt); diff --git a/src/bro.bif b/src/bro.bif index 852f806230..fbf2979f37 100644 --- a/src/bro.bif +++ b/src/bro.bif @@ -1775,13 +1775,14 @@ function global_ids%(%): id_table rec->Assign(1, new Val(id->IsExport(), TYPE_BOOL)); rec->Assign(2, new Val(id->IsConst(), TYPE_BOOL)); rec->Assign(3, new Val(id->IsEnumConst(), TYPE_BOOL)); - rec->Assign(4, new Val(id->IsRedefinable(), TYPE_BOOL)); + rec->Assign(4, new Val(id->IsOption(), TYPE_BOOL)); + rec->Assign(5, new Val(id->IsRedefinable(), TYPE_BOOL)); if ( id->HasVal() ) { Val* val = id->ID_Val(); Ref(val); - rec->Assign(5, val); + rec->Assign(6, val); } Val* id_name = new StringVal(id->Name()); diff --git a/src/broxygen/ScriptInfo.cc b/src/broxygen/ScriptInfo.cc index f9c5bf2288..c3a9ab8031 100644 --- a/src/broxygen/ScriptInfo.cc +++ b/src/broxygen/ScriptInfo.cc @@ -160,7 +160,7 @@ ScriptInfo::ScriptInfo(const string& arg_name, const string& arg_path) name(arg_name), path(arg_path), is_pkg_loader(SafeBasename(name).result == PACKAGE_LOADER), dependencies(), module_usages(), comments(), id_info(), - options(), constants(), state_vars(), types(), events(), hooks(), + redef_options(), constants(), state_vars(), types(), events(), hooks(), functions(), redefs() { } @@ -219,9 +219,9 @@ void ScriptInfo::DoInitPostScript() { if ( id->FindAttr(ATTR_REDEF) ) { - DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as an option", + DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as a redef_option", id->Name(), name.c_str()); - options.push_back(info); + redef_options.push_back(info); } else { @@ -232,6 +232,14 @@ void ScriptInfo::DoInitPostScript() continue; } + else if ( id->IsOption() ) + { + DBG_LOG(DBG_BROXYGEN, "Filter id '%s' in '%s' as a redef_option", + id->Name(), name.c_str()); + options.push_back(info); + + continue; + } if ( id->Type()->Tag() == TYPE_ENUM ) // Enums are always referenced/documented from the type's @@ -309,7 +317,8 @@ string ScriptInfo::DoReStructuredText(bool roles_only) const rval += fmt(":Source File: :download:`/scripts/%s`\n", name.c_str()); rval += "\n"; rval += broxygen::make_heading("Summary", '~'); - rval += make_summary("Options", '#', '=', options); + rval += make_summary("Runtime Options", '#', '=', options); + rval += make_summary("Redefinable Options", '#', '=', redef_options); rval += make_summary("Constants", '#', '=', constants); rval += make_summary("State Variables", '#', '=', state_vars); rval += make_summary("Types", '#', '=', types); @@ -319,7 +328,8 @@ string ScriptInfo::DoReStructuredText(bool roles_only) const rval += make_summary("Functions", '#', '=', functions); rval += "\n"; rval += broxygen::make_heading("Detailed Interface", '~'); - rval += make_details("Options", '#', options); + rval += make_details("Runtime Options", '#', options); + rval += make_details("Redefinable Options", '#', redef_options); rval += make_details("Constants", '#', constants); rval += make_details("State Variables", '#', state_vars); rval += make_details("Types", '#', types); diff --git a/src/broxygen/ScriptInfo.h b/src/broxygen/ScriptInfo.h index 146d66f05f..178ebdab44 100644 --- a/src/broxygen/ScriptInfo.h +++ b/src/broxygen/ScriptInfo.h @@ -108,6 +108,7 @@ private: string_set module_usages; std::vector comments; id_info_map id_info; + id_info_list redef_options; id_info_list options; id_info_list constants; id_info_list state_vars; diff --git a/src/input/Manager.cc b/src/input/Manager.cc index d029e38092..85bd127677 100644 --- a/src/input/Manager.cc +++ b/src/input/Manager.cc @@ -711,7 +711,7 @@ bool Manager::CreateTableStream(RecordVal* fval) return true; } -bool Manager::CheckErrorEventTypes(std::string stream_name, Func* ev, bool table) +bool Manager::CheckErrorEventTypes(std::string stream_name, const Func* ev, bool table) const { if ( ev == nullptr ) return true; @@ -899,7 +899,7 @@ bool Manager::RemoveStreamContinuation(ReaderFrontend* reader) } bool Manager::UnrollRecordType(vector *fields, const RecordType *rec, - const string& nameprepend, bool allow_file_func) + const string& nameprepend, bool allow_file_func) const { for ( int i = 0; i < rec->NumFields(); i++ ) { @@ -1007,7 +1007,7 @@ bool Manager::ForceUpdate(const string &name) } -Val* Manager::RecordValToIndexVal(RecordVal *r) +Val* Manager::RecordValToIndexVal(RecordVal *r) const { Val* idxval; @@ -1032,7 +1032,7 @@ Val* Manager::RecordValToIndexVal(RecordVal *r) } -Val* Manager::ValueToIndexVal(const Stream* i, int num_fields, const RecordType *type, const Value* const *vals, bool& have_error) +Val* Manager::ValueToIndexVal(const Stream* i, int num_fields, const RecordType *type, const Value* const *vals, bool& have_error) const { Val* idxval; int position = 0; @@ -1810,7 +1810,7 @@ bool Manager::Delete(ReaderFrontend* reader, Value* *vals) return success; } -bool Manager::CallPred(Func* pred_func, const int numvals, ...) +bool Manager::CallPred(Func* pred_func, const int numvals, ...) const { bool result = false; val_list vl(numvals); @@ -1835,7 +1835,7 @@ bool Manager::CallPred(Func* pred_func, const int numvals, ...) // Raise everything in here as warnings so it is passed to scriptland without // looking "fatal". In addition to these warnings, ReaderBackend will queue // one reporter message. -bool Manager::SendEvent(ReaderFrontend* reader, const string& name, const int num_vals, Value* *vals) +bool Manager::SendEvent(ReaderFrontend* reader, const string& name, const int num_vals, Value* *vals) const { Stream *i = FindStream(reader); if ( i == 0 ) @@ -1871,7 +1871,15 @@ bool Manager::SendEvent(ReaderFrontend* reader, const string& name, const int nu val_list* vl = new val_list; for ( int j = 0; j < num_vals; j++) - vl->append(ValueToVal(i, vals[j], type->FieldType(j), convert_error)); + { + Val* v = ValueToVal(i, vals[j], convert_error); + vl->append(v); + if ( v && ! convert_error && ! same_type(type->FieldType(j), v->Type()) ) + { + convert_error = true; + type->FieldType(j)->Error("SendEvent types do not match", v->Type()); + } + } delete_value_ptr_array(vals, num_vals); @@ -1886,7 +1894,7 @@ bool Manager::SendEvent(ReaderFrontend* reader, const string& name, const int nu return true; } -void Manager::SendEvent(EventHandlerPtr ev, const int numvals, ...) +void Manager::SendEvent(EventHandlerPtr ev, const int numvals, ...) const { val_list* vl = new val_list; @@ -1905,7 +1913,7 @@ void Manager::SendEvent(EventHandlerPtr ev, const int numvals, ...) mgr.QueueEvent(ev, vl, SOURCE_LOCAL); } -void Manager::SendEvent(EventHandlerPtr ev, list events) +void Manager::SendEvent(EventHandlerPtr ev, list events) const { val_list* vl = new val_list; @@ -1924,7 +1932,7 @@ void Manager::SendEvent(EventHandlerPtr ev, list events) // Convert a bro list value to a bro record value. // I / we could think about moving this functionality to val.cc -RecordVal* Manager::ListValToRecordVal(ListVal* list, RecordType *request_type, int* position) +RecordVal* Manager::ListValToRecordVal(ListVal* list, RecordType *request_type, int* position) const { assert(position != 0 ); // we need the pointer to point to data; @@ -1954,7 +1962,7 @@ RecordVal* Manager::ListValToRecordVal(ListVal* list, RecordType *request_type, // Convert a threading value to a record value RecordVal* Manager::ValueToRecordVal(const Stream* stream, const Value* const *vals, - RecordType *request_type, int* position, bool& have_error) + RecordType *request_type, int* position, bool& have_error) const { assert(position != 0); // we need the pointer to point to data. @@ -1991,7 +1999,8 @@ RecordVal* Manager::ValueToRecordVal(const Stream* stream, const Value* const *v // Count the length of the values used to create a correct length buffer for // hashing later -int Manager::GetValueLength(const Value* val) { +int Manager::GetValueLength(const Value* val) const + { assert( val->present ); // presence has to be checked elsewhere int length = 0; @@ -2081,7 +2090,7 @@ int Manager::GetValueLength(const Value* val) { // Given a threading::value, copy the raw data bytes into *data and return how many bytes were copied. // Used for hashing the values for lookup in the bro table -int Manager::CopyValue(char *data, const int startpos, const Value* val) +int Manager::CopyValue(char *data, const int startpos, const Value* val) const { assert( val->present ); // presence has to be checked elsewhere @@ -2205,7 +2214,7 @@ int Manager::CopyValue(char *data, const int startpos, const Value* val) } // Hash num_elements threading values and return the HashKey for them. At least one of the vals has to be ->present. -HashKey* Manager::HashValues(const int num_elements, const Value* const *vals) +HashKey* Manager::HashValues(const int num_elements, const Value* const *vals) const { int length = 0; @@ -2251,19 +2260,19 @@ HashKey* Manager::HashValues(const int num_elements, const Value* const *vals) // have_error is a reference to a boolean which is set to true as soon as an error occured. // When have_error is set to true at the beginning of the function, it is assumed that // an error already occured in the past and processing is aborted. -Val* Manager::ValueToVal(const Stream* i, const Value* val, BroType* request_type, bool& have_error) +Val* Manager::ValueToVal(const Stream* i, const Value* val, BroType* request_type, bool& have_error) const { if ( have_error ) - return 0; + return nullptr; if ( request_type->Tag() != TYPE_ANY && request_type->Tag() != val->type ) { reporter->InternalError("Typetags don't match: %d vs %d in stream %s", request_type->Tag(), val->type, i->name.c_str()); - return 0; + return nullptr; } if ( !val->present ) - return 0; // unset field + return nullptr; // unset field switch ( val->type ) { case TYPE_BOOL: @@ -2312,7 +2321,7 @@ Val* Manager::ValueToVal(const Stream* i, const Value* val, BroType* request_typ case TYPE_SUBNET: { - IPAddr* addr = 0; + IPAddr* addr = nullptr; switch ( val->val.subnet_val.prefix.family ) { case IPv4: addr = new IPAddr(val->val.subnet_val.prefix.in.in4); @@ -2359,7 +2368,7 @@ Val* Manager::ValueToVal(const Stream* i, const Value* val, BroType* request_typ VectorVal* v = new VectorVal(vt); for ( int j = 0; j < val->val.vector_val.size; j++ ) { - v->Assign(j, ValueToVal(i, val->val.set_val.vals[j], type, have_error)); + v->Assign(j, ValueToVal(i, val->val.vector_val.vals[j], type, have_error)); } Unref(vt); @@ -2384,7 +2393,7 @@ Val* Manager::ValueToVal(const Stream* i, const Value* val, BroType* request_typ enum_string.c_str(), i->name.c_str()); have_error = true; - return 0; + return nullptr; } return new EnumVal(index, request_type->Ref()->AsEnumType()); @@ -2398,9 +2407,177 @@ Val* Manager::ValueToVal(const Stream* i, const Value* val, BroType* request_typ return NULL; } -Manager::Stream* Manager::FindStream(const string &name) +Val* Manager::ValueToVal(const Stream* i, const Value* val, bool& have_error) const { - for ( map::iterator s = readers.begin(); s != readers.end(); ++s ) + if ( have_error ) + return nullptr; + + if ( !val->present ) + return nullptr; // unset field + + switch ( val->type ) { + case TYPE_BOOL: + case TYPE_INT: + return new Val(val->val.int_val, val->type); + break; + + case TYPE_COUNT: + case TYPE_COUNTER: + return new Val(val->val.uint_val, val->type); + + case TYPE_DOUBLE: + case TYPE_TIME: + case TYPE_INTERVAL: + return new Val(val->val.double_val, val->type); + + case TYPE_STRING: + { + BroString *s = new BroString((const u_char*)val->val.string_val.data, val->val.string_val.length, 1); + return new StringVal(s); + } + + case TYPE_PORT: + return new PortVal(val->val.port_val.port, val->val.port_val.proto); + + case TYPE_ADDR: + { + IPAddr* addr = 0; + switch ( val->val.addr_val.family ) { + case IPv4: + addr = new IPAddr(val->val.addr_val.in.in4); + break; + + case IPv6: + addr = new IPAddr(val->val.addr_val.in.in6); + break; + + default: + assert(false); + } + + AddrVal* addrval = new AddrVal(*addr); + delete addr; + return addrval; + } + + case TYPE_SUBNET: + { + IPAddr* addr = nullptr; + switch ( val->val.subnet_val.prefix.family ) { + case IPv4: + addr = new IPAddr(val->val.subnet_val.prefix.in.in4); + break; + + case IPv6: + addr = new IPAddr(val->val.subnet_val.prefix.in.in6); + break; + + default: + assert(false); + } + + SubNetVal* subnetval = new SubNetVal(*addr, val->val.subnet_val.length); + delete addr; + return subnetval; + } + + case TYPE_TABLE: + { + TypeList* set_index; + if ( val->val.set_val.size == 0 && val->subtype == TYPE_VOID ) + // don't know type - unspecified table. + set_index = new TypeList(); + else + { + // all entries have to have the same type... + TypeTag stag = val->subtype; + if ( stag == TYPE_VOID ) + TypeTag stag = val->val.set_val.vals[0]->type; + + set_index = new TypeList(base_type(stag)->Ref()); + set_index->Append(base_type(stag)->Ref()); + } + SetType* s = new SetType(set_index, 0); + TableVal* t = new TableVal(s); + for ( int j = 0; j < val->val.set_val.size; j++ ) + { + Val* assignval = ValueToVal(i, val->val.set_val.vals[j], have_error); + + t->Assign(assignval, 0); + Unref(assignval); // index is not consumed by assign. + } + + Unref(s); + return t; + } + + case TYPE_VECTOR: + { + BroType* type; + if ( val->val.vector_val.size == 0 && val->subtype == TYPE_VOID ) + // don't know type - unspecified table. + type = base_type(TYPE_ANY); + else + { + // all entries have to have the same type... + if ( val->subtype == TYPE_VOID ) + type = base_type(val->val.vector_val.vals[0]->type); + else + type = base_type(val->subtype); + } + + VectorType* vt = new VectorType(type->Ref()); + VectorVal* v = new VectorVal(vt); + for ( int j = 0; j < val->val.vector_val.size; j++ ) + { + v->Assign(j, ValueToVal(i, val->val.vector_val.vals[j], have_error)); + } + + Unref(vt); + return v; + } + + case TYPE_ENUM: { + // Convert to string first to not have to deal with missing + // \0's... + string enum_string(val->val.string_val.data, val->val.string_val.length); + + // let's try looking it up by global ID. + ID* id = lookup_ID(enum_string.c_str(), GLOBAL_MODULE_NAME); + if ( ! id || ! id->IsEnumConst() ) + { + Warning(i, "Value '%s' for stream '%s' is not a valid enum.", + enum_string.c_str(), i->name.c_str()); + + have_error = true; + return nullptr; + } + + EnumType* t = id->Type()->AsEnumType(); + int intval = t->Lookup(id->ModuleName(), id->Name()); + if ( intval < 0 ) + { + Warning(i, "Enum value '%s' for stream '%s' not found.", + enum_string.c_str(), i->name.c_str()); + + have_error = true; + return nullptr; + } + + return new EnumVal(intval, t); + } + + default: + reporter->InternalError("Unsupported type for input_read in stream %s", i->name.c_str()); + } + + assert(false); + return NULL; + } + +Manager::Stream* Manager::FindStream(const string &name) const + { + for ( auto s = readers.begin(); s != readers.end(); ++s ) { if ( (*s).second->name == name ) return (*s).second; @@ -2409,9 +2586,9 @@ Manager::Stream* Manager::FindStream(const string &name) return 0; } -Manager::Stream* Manager::FindStream(ReaderFrontend* reader) +Manager::Stream* Manager::FindStream(ReaderFrontend* reader) const { - map::iterator s = readers.find(reader); + auto s = readers.find(reader); if ( s != readers.end() ) return s->second; @@ -2433,7 +2610,7 @@ void Manager::Terminate() } -void Manager::Info(ReaderFrontend* reader, const char* msg) +void Manager::Info(ReaderFrontend* reader, const char* msg) const { Stream *i = FindStream(reader); if ( !i ) @@ -2445,7 +2622,7 @@ void Manager::Info(ReaderFrontend* reader, const char* msg) ErrorHandler(i, ErrorType::INFO, false, "%s", msg); } -void Manager::Warning(ReaderFrontend* reader, const char* msg) +void Manager::Warning(ReaderFrontend* reader, const char* msg) const { Stream *i = FindStream(reader); if ( !i ) @@ -2457,7 +2634,7 @@ void Manager::Warning(ReaderFrontend* reader, const char* msg) ErrorHandler(i, ErrorType::WARNING, false, "%s", msg); } -void Manager::Error(ReaderFrontend* reader, const char* msg) +void Manager::Error(ReaderFrontend* reader, const char* msg) const { Stream *i = FindStream(reader); if ( !i ) @@ -2469,7 +2646,7 @@ void Manager::Error(ReaderFrontend* reader, const char* msg) ErrorHandler(i, ErrorType::ERROR, false, "%s", msg); } -void Manager::Info(const Stream* i, const char* fmt, ...) +void Manager::Info(const Stream* i, const char* fmt, ...) const { va_list ap; va_start(ap, fmt); @@ -2477,7 +2654,7 @@ void Manager::Info(const Stream* i, const char* fmt, ...) va_end(ap); } -void Manager::Warning(const Stream* i, const char* fmt, ...) +void Manager::Warning(const Stream* i, const char* fmt, ...) const { va_list ap; va_start(ap, fmt); @@ -2485,7 +2662,7 @@ void Manager::Warning(const Stream* i, const char* fmt, ...) va_end(ap); } -void Manager::Error(const Stream* i, const char* fmt, ...) +void Manager::Error(const Stream* i, const char* fmt, ...) const { va_list ap; va_start(ap, fmt); @@ -2493,7 +2670,7 @@ void Manager::Error(const Stream* i, const char* fmt, ...) va_end(ap); } -void Manager::ErrorHandler(const Stream* i, ErrorType et, bool reporter_send, const char* fmt, ...) +void Manager::ErrorHandler(const Stream* i, ErrorType et, bool reporter_send, const char* fmt, ...) const { va_list ap; va_start(ap, fmt); @@ -2501,7 +2678,7 @@ void Manager::ErrorHandler(const Stream* i, ErrorType et, bool reporter_send, co va_end(ap); } -void Manager::ErrorHandler(const Stream* i, ErrorType et, bool reporter_send, const char* fmt, va_list ap) +void Manager::ErrorHandler(const Stream* i, ErrorType et, bool reporter_send, const char* fmt, va_list ap) const { char* buf; diff --git a/src/input/Manager.h b/src/input/Manager.h index 8296ce9f8b..76f82835a2 100644 --- a/src/input/Manager.h +++ b/src/input/Manager.h @@ -98,6 +98,21 @@ public: */ void Terminate(); + /** + * Checks if a Bro type can be used for data reading. Note that + * this function only applies to input streams; the logging framework + * has an equivalent function; however we support logging of a wider + * variety of types (e.g. functions). + * + * @param t The type to check. + * + * @param atomic_only Set to true to forbid non-atomic types + * (records/sets/vectors). + * + * @return True if the type is compatible with the input framework. + */ + static bool IsCompatibleType(BroType* t, bool atomic_only=false); + protected: friend class ReaderFrontend; friend class PutMessage; @@ -130,7 +145,7 @@ protected: // Allows readers to directly send Bro events. The num_vals and vals // must be the same the named event expects. Takes ownership of // threading::Value fields. - bool SendEvent(ReaderFrontend* reader, const string& name, const int num_vals, threading::Value* *vals); + bool SendEvent(ReaderFrontend* reader, const string& name, const int num_vals, threading::Value* *vals) const; // Instantiates a new ReaderBackend of the given type (note that // doing so creates a new thread!). @@ -147,9 +162,9 @@ protected: // Signal Informational messages, warnings and errors. These will be // passed to the error function in scriptland. Note that the messages // are not passed to reporter - this is done in ReaderBackend. - void Info(ReaderFrontend* reader, const char* msg); - void Warning(ReaderFrontend* reader, const char* msg); - void Error(ReaderFrontend* reader, const char* msg); + void Info(ReaderFrontend* reader, const char* msg) const; + void Warning(ReaderFrontend* reader, const char* msg) const; + void Error(ReaderFrontend* reader, const char* msg) const; /** * Deletes an existing input stream. @@ -176,7 +191,7 @@ private: // Check if the types of the error_ev event are correct. If table is // true, check for tablestream type, otherwhise check for eventstream // type. - bool CheckErrorEventTypes(std::string stream_name, Func* error_event, bool table); + bool CheckErrorEventTypes(std::string stream_name, const Func* error_event, bool table) const; // SendEntry implementation for Table stream. int SendEntryTable(Stream* i, const threading::Value* const *vals); @@ -187,63 +202,63 @@ private: // SendEntry and Put implementation for Event stream. int SendEventStreamEvent(Stream* i, EnumVal* type, const threading::Value* const *vals); - // Checks that a Bro type can be used for data reading. The - // equivalend in threading cannot be used, because we have support - // different types from the log framework - bool IsCompatibleType(BroType* t, bool atomic_only=false); // Check if a record is made up of compatible types and return a list // of all fields that are in the record in order. Recursively unrolls // records - bool UnrollRecordType(vector *fields, const RecordType *rec, const string& nameprepend, bool allow_file_func); + bool UnrollRecordType(vector *fields, const RecordType *rec, const string& nameprepend, bool allow_file_func) const; // Send events - void SendEvent(EventHandlerPtr ev, const int numvals, ...); - void SendEvent(EventHandlerPtr ev, list events); + void SendEvent(EventHandlerPtr ev, const int numvals, ...) const; + void SendEvent(EventHandlerPtr ev, list events) const; // Implementation of SendEndOfData (send end_of_data event). void SendEndOfData(const Stream *i); // Call predicate function and return result. - bool CallPred(Func* pred_func, const int numvals, ...); + bool CallPred(Func* pred_func, const int numvals, ...) const; // Get a hashkey for a set of threading::Values. - HashKey* HashValues(const int num_elements, const threading::Value* const *vals); + HashKey* HashValues(const int num_elements, const threading::Value* const *vals) const; // Get the memory used by a specific value. - int GetValueLength(const threading::Value* val); + int GetValueLength(const threading::Value* val) const; // Copies the raw data in a specific threading::Value to position // startpos. - int CopyValue(char *data, const int startpos, const threading::Value* val); + int CopyValue(char *data, const int startpos, const threading::Value* val) const; // Convert Threading::Value to an internal Bro Type (works also with // Records). - Val* ValueToVal(const Stream* i, const threading::Value* val, BroType* request_type, bool& have_error); + Val* ValueToVal(const Stream* i, const threading::Value* val, BroType* request_type, bool& have_error) const; - // Convert Threading::Value to an internal Bro List type. - Val* ValueToIndexVal(const Stream* i, int num_fields, const RecordType* type, const threading::Value* const *vals, bool& have_error); + // Convert Threading::Value to an internal Bro type just using the information given in the threading::Value. + // This allows more flexibility, especially given structures in script-land that contain any types. + Val* ValueToVal(const Stream* i, const threading::Value* val, bool& have_error) const; + + // Convert Threading::Value to an internal Bro list type. + Val* ValueToIndexVal(const Stream* i, int num_fields, const RecordType* type, const threading::Value* const *vals, bool& have_error) const; // Converts a threading::value to a record type. Mostly used by // ValueToVal. - RecordVal* ValueToRecordVal(const Stream* i, const threading::Value* const *vals, RecordType *request_type, int* position, bool& have_error); + RecordVal* ValueToRecordVal(const Stream* i, const threading::Value* const *vals, RecordType *request_type, int* position, bool& have_error) const; - Val* RecordValToIndexVal(RecordVal *r); + Val* RecordValToIndexVal(RecordVal *r) const; // Converts a Bro ListVal to a RecordVal given the record type. - RecordVal* ListValToRecordVal(ListVal* list, RecordType *request_type, int* position); + RecordVal* ListValToRecordVal(ListVal* list, RecordType *request_type, int* position) const; // Internally signal errors, warnings, etc. // These are sent on to input scriptland and reporter.log - void Info(const Stream* i, const char* fmt, ...) __attribute__((format(printf, 3, 4))); - void Warning(const Stream* i, const char* fmt, ...) __attribute__((format(printf, 3, 4))); - void Error(const Stream* i, const char* fmt, ...) __attribute__((format(printf, 3, 4))); + void Info(const Stream* i, const char* fmt, ...) const __attribute__((format(printf, 3, 4))); + void Warning(const Stream* i, const char* fmt, ...) const __attribute__((format(printf, 3, 4))); + void Error(const Stream* i, const char* fmt, ...) const __attribute__((format(printf, 3, 4))); enum class ErrorType { INFO, WARNING, ERROR }; - void ErrorHandler(const Stream* i, ErrorType et, bool reporter_send, const char* fmt, ...) __attribute__((format(printf, 5, 6))); - void ErrorHandler(const Stream* i, ErrorType et, bool reporter_send, const char* fmt, va_list ap) __attribute__((format(printf, 5, 0))); + void ErrorHandler(const Stream* i, ErrorType et, bool reporter_send, const char* fmt, ...) const __attribute__((format(printf, 5, 6))); + void ErrorHandler(const Stream* i, ErrorType et, bool reporter_send, const char* fmt, va_list ap) const __attribute__((format(printf, 5, 0))); - Stream* FindStream(const string &name); - Stream* FindStream(ReaderFrontend* reader); + Stream* FindStream(const string &name) const; + Stream* FindStream(ReaderFrontend* reader) const; enum StreamType { TABLE_STREAM, EVENT_STREAM, ANALYSIS_STREAM }; diff --git a/src/input/readers/CMakeLists.txt b/src/input/readers/CMakeLists.txt index 36b7439052..d653789847 100644 --- a/src/input/readers/CMakeLists.txt +++ b/src/input/readers/CMakeLists.txt @@ -2,5 +2,6 @@ add_subdirectory(ascii) add_subdirectory(benchmark) add_subdirectory(binary) +add_subdirectory(config) add_subdirectory(raw) add_subdirectory(sqlite) diff --git a/src/input/readers/benchmark/Benchmark.cc b/src/input/readers/benchmark/Benchmark.cc index 9d962c8c64..49e989909c 100644 --- a/src/input/readers/benchmark/Benchmark.cc +++ b/src/input/readers/benchmark/Benchmark.cc @@ -125,7 +125,7 @@ bool Benchmark::DoUpdate() threading::Value* Benchmark::EntryToVal(TypeTag type, TypeTag subtype) { - Value* val = new Value(type, true); + Value* val = new Value(type, subtype, true); // basically construct something random from the fields that we want. diff --git a/src/input/readers/config/CMakeLists.txt b/src/input/readers/config/CMakeLists.txt new file mode 100644 index 0000000000..8e4c1aa5aa --- /dev/null +++ b/src/input/readers/config/CMakeLists.txt @@ -0,0 +1,9 @@ + +include(BroPlugin) + +include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) + +bro_plugin_begin(Bro ConfigReader) +bro_plugin_cc(Config.cc Plugin.cc) +bro_plugin_bif(config.bif) +bro_plugin_end() diff --git a/src/input/readers/config/Config.cc b/src/input/readers/config/Config.cc new file mode 100644 index 0000000000..d3f0d9e0a5 --- /dev/null +++ b/src/input/readers/config/Config.cc @@ -0,0 +1,310 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +#include +#include +#include + +#include +#include +#include +#include + +#include "Config.h" +#include "config.bif.h" + +#include "input/Manager.h" +#include "threading/SerialTypes.h" + +using namespace input::reader; +using namespace threading; +using threading::Value; +using threading::Field; + +Config::Config(ReaderFrontend *frontend) : ReaderBackend(frontend) + { + mtime = 0; + suppress_warnings = false; + fail_on_file_problem = false; + + // find all option names and their types. + auto globals = global_scope()->Vars(); + auto c = globals->InitForIteration(); + + while ( auto id = globals->NextEntry(c) ) + { + if ( id->IsInternalGlobal() || ! id->IsOption() ) + continue; + + if ( id->Type()->Tag() == TYPE_RECORD || + ! input::Manager::IsCompatibleType(id->Type()) ) + { + option_types[id->Name()] = std::make_tuple(TYPE_ERROR, id->Type()->Tag()); + continue; + } + + TypeTag primary = id->Type()->Tag(); + TypeTag secondary = TYPE_VOID; + if ( primary == TYPE_TABLE ) + secondary = id->Type()->AsSetType()->Indices()->PureType()->Tag(); + else if ( primary == TYPE_VECTOR ) + secondary = id->Type()->AsVectorType()->YieldType()->Tag(); + + option_types[id->Name()] = std::make_tuple(primary, secondary); + } + } + +Config::~Config() + { + } + +void Config::DoClose() + { + } + +bool Config::DoInit(const ReaderInfo& info, int num_fields, const Field* const* fields) + { + fail_on_file_problem = BifConst::InputConfig::fail_on_file_problem; + + set_separator.assign( (const char*) BifConst::InputConfig::set_separator->Bytes(), + BifConst::InputConfig::set_separator->Len()); + + empty_field.assign( (const char*) BifConst::InputConfig::empty_field->Bytes(), + BifConst::InputConfig::empty_field->Len()); + + + formatter::Ascii::SeparatorInfo sep_info("\t", set_separator, "", empty_field); + formatter = unique_ptr(new formatter::Ascii(this, sep_info)); + + return DoUpdate(); + } + +bool Config::OpenFile() + { + if ( file.is_open() ) + return true; + + file.open(Info().source); + + if ( ! file.is_open() ) + { + FailWarn(fail_on_file_problem, Fmt("Init: cannot open %s", Info().source), true); + return ! fail_on_file_problem; + } + + suppress_warnings = false; + return true; + } + +void Config::FailWarn(bool is_error, const char *msg, bool suppress_future) + { + if ( is_error ) + Error(msg); + else + { + // suppress error message when we are already in error mode. + // There is no reason to repeat it every second. + if ( ! suppress_warnings ) + Warning(msg); + + if ( suppress_future ) + suppress_warnings = true; + } + } + +bool Config::GetLine(string& str) + { + while ( getline(file, str) ) + { + if ( ! str.size() ) + continue; + + if ( str.back() == '\r' ) // deal with \r\n by removing \r + str.pop_back(); + + if ( str[0] != '#' ) + return true; + } + + return false; + } + +// read the entire file and send appropriate thingies back to InputMgr +bool Config::DoUpdate() + { + if ( ! OpenFile() ) + return ! fail_on_file_problem; + + switch ( Info().mode ) { + case MODE_REREAD: + { + // check if the file has changed + struct stat sb; + if ( stat(Info().source, &sb) == -1 ) + { + FailWarn(fail_on_file_problem, Fmt("Could not get stat for %s", Info().source), true); + + file.close(); + return ! fail_on_file_problem; + } + + if ( sb.st_mtime <= mtime ) // no change + return true; + + mtime = sb.st_mtime; + // file changed. reread. + + // fallthrough + } + + case MODE_MANUAL: + case MODE_STREAM: + { + // dirty, fix me. (well, apparently after trying seeking, etc + // - this is not that bad) + if ( file.is_open() ) + { + if ( Info().mode == MODE_STREAM ) + { + file.clear(); // remove end of file evil bits + break; + } + + file.close(); + } + + OpenFile(); + + break; + } + + default: + assert(false); + } + + string line; + file.sync(); + + // keep a list of options to remove because they were no longer in the input file. + // Start out with all element and removes while going along + std::unordered_set unseen_options; + for ( auto i : option_values ) + { + unseen_options.insert(i.first); + } + + while ( GetLine(line) ) + { + static std::regex re("^(.*?)\\s+(.*)$"); + std::smatch match; + if ( ! std::regex_search(line, match, re) ) + { + Warning(Fmt("Could not parse '%s'; line has invalid format. Ignoring line.", line.c_str())); + continue; + } + + string key = match[1]; + string value = match[2]; + + auto typeit = option_types.find(key); + if ( typeit == option_types.end() ) + { + Warning(Fmt("Option '%s' does not exist. Ignoring line.", key.c_str())); + continue; + } + + if ( std::get<0>((*typeit).second) == TYPE_ERROR ) + { + Warning(Fmt("Option '%s' has type '%s', which is not supported for file input. Ignoring line.", key.c_str(), type_name(std::get<1>((*typeit).second)))); + continue; + } + + Value* eventval = formatter->ParseValue(value, key, std::get<0>((*typeit).second), std::get<1>((*typeit).second)); + if ( ! eventval ) + { + Warning(Fmt("Could not convert line '%s' to Val. Ignoring line.", line.c_str())); + continue; + } + else if ( ! eventval->present ) + { + Warning(Fmt("Line '%s' has no Value. Ignoring line.", line.c_str())); + delete eventval; + continue; + } + + // we only send the event if the underlying value has changed. Let's check that. + // (Yes, this means we keep all configuration options in memory twice - once here in + // the reader and once in memory in Bro; that is difficult to change. + auto search = option_values.find(key); + if ( search != option_values.end() && search->second == value ) + { + delete eventval; + continue; + } + + option_values[key] = value; + unseen_options.erase(key); + + { + Value** fields = new Value*[2]; + Value* keyval = new threading::Value(TYPE_STRING, true); + keyval->val.string_val.length = key.size(); + keyval->val.string_val.data = copy_string(key.c_str()); + fields[0] = keyval; + Value* val = new threading::Value(TYPE_STRING, true); + val->val.string_val.length = value.size(); + val->val.string_val.data = copy_string(value.c_str()); + fields[1] = val; + + if ( Info().mode == MODE_STREAM ) + Put(fields); + else + SendEntry(fields); + } + { + Value** vals = new Value*[4]; + vals[0] = new Value(TYPE_STRING, true); + vals[0]->val.string_val.data = copy_string(Info().name); + vals[0]->val.string_val.length = strlen(Info().name); + vals[1] = new Value(TYPE_STRING, true); + vals[1]->val.string_val.data = copy_string(Info().source); + vals[1]->val.string_val.length = strlen(Info().source); + vals[2] = new Value(TYPE_STRING, true); + vals[2]->val.string_val.data = copy_string(key.c_str()); + vals[2]->val.string_val.length = key.size(); + vals[3] = eventval; + + SendEvent("InputConfig::new_value", 4, vals); + } + } + + if ( Info().mode != MODE_STREAM ) + EndCurrentSend(); + + // clean up all options we did not see + for ( auto i : unseen_options ) + option_values.erase(i); + + return true; + } + +bool Config::DoHeartbeat(double network_time, double current_time) + { + switch ( Info().mode ) + { + case MODE_MANUAL: + // yay, we do nothing :) + break; + + case MODE_REREAD: + case MODE_STREAM: + Update(); // call update and not DoUpdate, because update + // checks disabled. + break; + + default: + assert(false); + } + + return true; + } + diff --git a/src/input/readers/config/Config.h b/src/input/readers/config/Config.h new file mode 100644 index 0000000000..05f1f6b767 --- /dev/null +++ b/src/input/readers/config/Config.h @@ -0,0 +1,67 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +#ifndef INPUT_READERS_CONFIG_H +#define INPUT_READERS_CONFIG_H + +#include +#include +#include +#include +#include + +#include "input/ReaderBackend.h" +#include "threading/formatters/Ascii.h" + +namespace input { namespace reader { + +/** + * Reader for Configuration files. + */ +class Config : public ReaderBackend { +public: + explicit Config(ReaderFrontend* frontend); + ~Config(); + + // prohibit copying and moving + Config(const Config&) = delete; + Config(Config&&) = delete; + Config& operator=(const Config&) = delete; + Config& operator=(Config&&) = delete; + + static ReaderBackend* Instantiate(ReaderFrontend* frontend) { return new Config(frontend); } + +protected: + bool DoInit(const ReaderInfo& info, int arg_num_fields, const threading::Field* const* fields) override; + void DoClose() override; + bool DoUpdate() override; + bool DoHeartbeat(double network_time, double current_time) override; + +private: + bool GetLine(string& str); + bool OpenFile(); + // Call Warning or Error, depending on the is_error boolean. + // In case of a warning, setting suppress_future to true will suppress all future warnings + // (by setting suppress_warnings to true, until suppress_warnings is set back to false) + void FailWarn(bool is_error, const char *msg, bool suppress_future = false); + + ifstream file; + time_t mtime; + + bool fail_on_file_problem; + // this is an internal indicator in case the read is currently in a failed state + // it's used to suppress duplicate error messages. + bool suppress_warnings; + + string set_separator; + string empty_field; + + std::unique_ptr formatter; + std::unordered_map> option_types; + std::unordered_map option_values; +}; + + +} +} + +#endif /* INPUT_READERS_CONFIG_H */ diff --git a/src/input/readers/config/Plugin.cc b/src/input/readers/config/Plugin.cc new file mode 100644 index 0000000000..77c8a97091 --- /dev/null +++ b/src/input/readers/config/Plugin.cc @@ -0,0 +1,24 @@ +// See the file in the main distribution directory for copyright. + +#include "plugin/Plugin.h" + +#include "Config.h" + +namespace plugin { +namespace Bro_ConfigReader { + +class Plugin : public plugin::Plugin { +public: + plugin::Configuration Configure() + { + AddComponent(new ::input::Component("Config", ::input::reader::Config::Instantiate)); + + plugin::Configuration config; + config.name = "Bro::ConfigReader"; + config.description = "Configuration file input reader"; + return config; + } +} plugin; + +} +} diff --git a/src/input/readers/config/config.bif b/src/input/readers/config/config.bif new file mode 100644 index 0000000000..4ca3ec6690 --- /dev/null +++ b/src/input/readers/config/config.bif @@ -0,0 +1,6 @@ + +module InputConfig; + +const set_separator: string; +const empty_field: string; +const fail_on_file_problem: bool; diff --git a/src/input/readers/sqlite/SQLite.cc b/src/input/readers/sqlite/SQLite.cc index c970e60182..40c0f8a063 100644 --- a/src/input/readers/sqlite/SQLite.cc +++ b/src/input/readers/sqlite/SQLite.cc @@ -128,7 +128,7 @@ bool SQLite::DoInit(const ReaderInfo& info, int arg_num_fields, const threading: Value* SQLite::EntryToVal(sqlite3_stmt *st, const threading::Field *field, int pos, int subpos) { if ( sqlite3_column_type(st, pos ) == SQLITE_NULL ) - return new Value(field->type, false); + return new Value(field->type, field->subtype, false); Value* val = new Value(field->type, true); diff --git a/src/option.bif b/src/option.bif new file mode 100644 index 0000000000..ecbbacc204 --- /dev/null +++ b/src/option.bif @@ -0,0 +1,155 @@ +##! The option built-in functions allow the scripting layer to +##! change the value of option-values and to be notified when +##! option values change. + +module Option; + +%%{ +#include "NetVar.h" +%%} + +## Sets an option to a new value. This change will also cause the option change handlers +## to be called. +## +## ID: The ID of the option to update. +## +## val: The new value of the option. +## +## location: optional parameter detailing where this change originated from. +## +## Returns: true on success, false when an error occured. +## +## .. bro:see:: Option::set_change_handler +function Option::set%(ID: string, val: any, location: string &default=""%): bool + %{ + auto i = global_scope()->Lookup(ID->CheckString()); + if ( ! i ) + { + builtin_error(fmt("Could not find ID named '%s'", ID->CheckString())); + return new Val(0, TYPE_BOOL); + } + if ( ! i->HasVal() ) + { + // should be impossible because initialization is enforced + builtin_error(fmt("ID '%s' has no value", ID->CheckString())); + return new Val(0, TYPE_BOOL); + } + if ( ! i->IsOption() ) + { + builtin_error(fmt("ID '%s' is not an option", ID->CheckString())); + return new Val(0, TYPE_BOOL); + } + if ( ! same_type(i->Type(), val->Type()) ) + { + builtin_error(fmt("Incompatible type for set of ID '%s': got '%s', need '%s'", + ID->CheckString(), type_name(val->Type()->Tag()), type_name(i->Type()->Tag()))); + } + + val->Ref(); + if ( i->HasOptionHandlers() ) + { + for ( auto handler_function : i->GetOptionHandlers() ) + { + val_list vl(2); + vl.append(ID->Ref()); + vl.append(val); + if ( handler_function->FType()->AsFuncType()->ArgTypes()->Types()->length() == 3 ) + vl.append(location->Ref()); + val = handler_function->Call(&vl); // consumed by next call. + if ( ! val ) + { + // Someone messed up, don't change value and just return + return new Val(0, TYPE_BOOL); + } + } + } + + // clone to prevent changes + i->SetVal(val->Clone()); + Unref(val); // Either ref'd once or function call result. + return new Val(1, TYPE_BOOL); + %} + +## Set the change handler for the option *ID*. The change handler will be called anytime +## :bro:id:`Option::set` is called fot *ID*. +## +## ID: The ID of the option for which change notifications are desired. +## +## on_change: The function that will be called when a change occurs. The function can choose to +## receive two or three parameters: the first parameter is a string containing *ID*, +## the second parameter is the new option value. If the third, optional, parameter is the +## location string as passed to Option::set. Note that the global value is not yet changed +## when the function is called. The passed function has to return the new value that +## it wants the option to be set to. This enables it to reject changes, or change values +## that are being set. When several change handlers are set for an option they are chained; +## the second change handler will see the return value of the first change handler as the +## "new value". +## +## priority: The priority of the function that was added; functions with higher priority are +## called first, functions with the same priority are called in the order in which +## they were added. +## +## Returns: true when the change handler was set, false when an error occurred. +## +## .. bro:see:: Option::set +function Option::set_change_handler%(ID: string, on_change: any, priority: int &default=0%): bool + %{ + auto i = global_scope()->Lookup(ID->CheckString()); + if ( ! i ) + { + builtin_error(fmt("Could not find ID named '%s'", ID->CheckString())); + return new Val(0, TYPE_BOOL); + } + if ( ! i->IsOption() ) + { + builtin_error(fmt("ID '%s' is not an option", ID->CheckString())); + return new Val(0, TYPE_BOOL); + } + + if ( on_change->Type()->Tag() != TYPE_FUNC ) + { + builtin_error(fmt("Option::on_change needs function argument; got '%s' for ID '%s'", + type_name(on_change->Type()->Tag()), ID->CheckString())); + return new Val(0, TYPE_BOOL); + } + if ( on_change->Type()->AsFuncType()->Flavor() != FUNC_FLAVOR_FUNCTION ) + { + builtin_error("Option::on_change needs function argument; not hook or event"); + return new Val(0, TYPE_BOOL); + } + + const type_list* args = on_change->Type()->AsFuncType()->ArgTypes()->Types(); + if ( args->length() < 2 || args->length() > 3 ) + { + builtin_error(fmt("Wrong number of arguments for passed function in Option::on_change for ID '%s'; expected 2 or 3, got %d", + ID->CheckString(), args->length())); + return new Val(0, TYPE_BOOL); + } + if ( (*args)[0]->Tag() != TYPE_STRING ) + { + builtin_error(fmt("First argument of passed function has to be string in Option::on_change for ID '%s'; got '%s'", + ID->CheckString(), type_name((*args)[0]->Tag()))); + return new Val(0, TYPE_BOOL); + } + if ( ! same_type((*args)[1], i->Type()) ) + { + builtin_error(fmt("Second argument of passed function has to be %s in Option::on_change for ID '%s'; got '%s'", + type_name(i->Type()->Tag()), ID->CheckString(), type_name((*args)[1]->Tag()))); + return new Val(0, TYPE_BOOL); + } + if ( args->length() == 3 && (*args)[2]->Tag() != TYPE_STRING ) + { + builtin_error(fmt("Third argument of passed function has to be string in Option::on_change for ID '%s'; got '%s'", + ID->CheckString(), type_name((*args)[2]->Tag()))); + return new Val(0, TYPE_BOOL); + } + if ( ! same_type(on_change->Type()->AsFuncType()->YieldType(), i->Type()) ) + { + builtin_error(fmt("Passed function needs to return type '%s' for ID '%s'; got '%s'", + type_name(i->Type()->Tag()), ID->CheckString(), type_name(on_change->Type()->AsFuncType()->YieldType()->Tag()))); + return new Val(0, TYPE_BOOL); + } + + i->AddOptionHandler(on_change->Ref()->AsFunc(), -priority); + return new Val(1, TYPE_BOOL); + %} diff --git a/src/parse.y b/src/parse.y index facd7e55ed..89bb5adbee 100644 --- a/src/parse.y +++ b/src/parse.y @@ -6,7 +6,7 @@ %token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY %token TOK_ATENDIF TOK_ATELSE TOK_ATIF TOK_ATIFDEF TOK_ATIFNDEF -%token TOK_BOOL TOK_BREAK TOK_CASE TOK_CONST +%token TOK_BOOL TOK_BREAK TOK_CASE TOK_OPTION TOK_CONST %token TOK_CONSTANT TOK_COPY TOK_COUNT TOK_COUNTER TOK_DEFAULT TOK_DELETE %token TOK_DOUBLE TOK_ELSE TOK_ENUM TOK_EVENT TOK_EXPORT TOK_FALLTHROUGH %token TOK_FILE TOK_FOR TOK_FUNCTION TOK_GLOBAL TOK_HOOK TOK_ID TOK_IF TOK_INT @@ -1059,6 +1059,12 @@ decl: broxygen_mgr->Identifier($2); } + | TOK_OPTION def_global_id opt_type init_class opt_init opt_attr ';' + { + add_global($2, $3, $4, $5, $6, VAR_OPTION); + broxygen_mgr->Identifier($2); + } + | TOK_CONST def_global_id opt_type init_class opt_init opt_attr ';' { add_global($2, $3, $4, $5, $6, VAR_CONST); diff --git a/src/scan.l b/src/scan.l index cdac72c1cd..ef0d40dadd 100644 --- a/src/scan.l +++ b/src/scan.l @@ -215,6 +215,7 @@ any return TOK_ANY; bool return TOK_BOOL; break return TOK_BREAK; case return TOK_CASE; +option return TOK_OPTION; const return TOK_CONST; copy return TOK_COPY; count return TOK_COUNT; diff --git a/src/threading/SerialTypes.cc b/src/threading/SerialTypes.cc index 3836638e5c..8468d19ea8 100644 --- a/src/threading/SerialTypes.cc +++ b/src/threading/SerialTypes.cc @@ -161,12 +161,13 @@ bool Value::IsCompatibleType(BroType* t, bool atomic_only) bool Value::Read(SerializationFormat* fmt) { - int ty; + int ty, sty; - if ( ! (fmt->Read(&ty, "type") && fmt->Read(&present, "present")) ) + if ( ! (fmt->Read(&ty, "type") && fmt->Read(&sty, "subtype") && fmt->Read(&present, "present")) ) return false; type = (TypeTag)(ty); + subtype = (TypeTag)(sty); if ( ! present ) return true; @@ -311,6 +312,7 @@ bool Value::Read(SerializationFormat* fmt) bool Value::Write(SerializationFormat* fmt) const { if ( ! (fmt->Write((int)type, "type") && + fmt->Write((int)subtype, "subtype") && fmt->Write(present, "present")) ) return false; diff --git a/src/threading/SerialTypes.h b/src/threading/SerialTypes.h index af3f92d416..5a8361feba 100644 --- a/src/threading/SerialTypes.h +++ b/src/threading/SerialTypes.h @@ -26,7 +26,7 @@ struct Field { //! port, one for the type), and this specifies the secondary name. const char* secondary_name; TypeTag type; //! Type of the field. - TypeTag subtype; //! Inner type for sets. + TypeTag subtype; //! Inner type for sets and vectors. bool optional; //! True if field is optional. /** @@ -92,6 +92,7 @@ private: */ struct Value { TypeTag type; //! The type of the value. + TypeTag subtype; //! Inner type for sets and vectors. bool present; //! False for optional record fields that are not set. struct set_t { bro_int_t size; Value** vals; }; @@ -146,7 +147,20 @@ struct Value { * that is not set. */ Value(TypeTag arg_type = TYPE_ERROR, bool arg_present = true) - : type(arg_type), present(arg_present) {} + : type(arg_type), subtype(TYPE_VOID), present(arg_present) {} + + /** + * Constructor. + * + * arg_type: The type of the value. + * + * arg_type: The subtype of the value for sets and vectors. + * + * arg_present: False if the value represents an optional record field + * that is not set. + */ + Value(TypeTag arg_type, TypeTag arg_subtype, bool arg_present = true) + : type(arg_type), subtype(arg_subtype), present(arg_present) {} /** * Destructor. @@ -185,4 +199,4 @@ private: } -#endif /* THREADING_SERIALIZATIONTZPES_H */ +#endif /* THREADING_SERIALIZATIONTYPES_H */ diff --git a/src/threading/formatters/Ascii.cc b/src/threading/formatters/Ascii.cc index bf36c361cc..80edd42a61 100644 --- a/src/threading/formatters/Ascii.cc +++ b/src/threading/formatters/Ascii.cc @@ -207,13 +207,14 @@ bool Ascii::Describe(ODesc* desc, threading::Value* val, const string& name) con threading::Value* Ascii::ParseValue(const string& s, const string& name, TypeTag type, TypeTag subtype) const { - if ( s.compare(separators.unset_field) == 0 ) // field is not set... + if ( ! separators.unset_field.empty() && s.compare(separators.unset_field) == 0 ) // field is not set... return new threading::Value(type, false); - threading::Value* val = new threading::Value(type, true); + threading::Value* val = new threading::Value(type, subtype, true); const char* start = s.c_str(); char* end = 0; errno = 0; + size_t pos; switch ( type ) { case TYPE_ENUM: @@ -260,11 +261,21 @@ threading::Value* Ascii::ParseValue(const string& s, const string& name, TypeTag break; case TYPE_PORT: + val->val.port_val.proto = TRANSPORT_UNKNOWN; + pos = s.find('/'); + if ( pos != std::string::npos && s.length() > pos + 1 ) + { + auto proto = s.substr(pos+1); + if ( strtolower(proto) == "tcp" ) + val->val.port_val.proto = TRANSPORT_TCP; + else if ( strtolower(proto) == "udp" ) + val->val.port_val.proto = TRANSPORT_UDP; + else if ( strtolower(proto) == "icmp" ) + val->val.port_val.proto = TRANSPORT_ICMP; + } val->val.port_val.port = strtoull(start, &end, 10); if ( CheckNumberError(start, end) ) goto parse_error; - - val->val.port_val.proto = TRANSPORT_UNKNOWN; break; case TYPE_SUBNET: diff --git a/testing/btest/Baseline/core.check-unused-event-handlers/.stderr b/testing/btest/Baseline/core.check-unused-event-handlers/.stderr index 1a32ad442c..8c4e4def40 100644 --- a/testing/btest/Baseline/core.check-unused-event-handlers/.stderr +++ b/testing/btest/Baseline/core.check-unused-event-handlers/.stderr @@ -1,2 +1,3 @@ warning in , line 1: event handler never invoked: this_is_never_used +warning in , line 1: event handler never invoked: InputConfig::new_value warning in , line 1: event handler never invoked: InputRaw::process_finished diff --git a/testing/btest/Baseline/core.option-errors-2/.stderr b/testing/btest/Baseline/core.option-errors-2/.stderr new file mode 100644 index 0000000000..90011d5c85 --- /dev/null +++ b/testing/btest/Baseline/core.option-errors-2/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-errors-2/option-errors.bro, line 2: option variable must be initialized (testbool) diff --git a/testing/btest/Baseline/core.option-errors-3/.stderr b/testing/btest/Baseline/core.option-errors-3/.stderr new file mode 100644 index 0000000000..ffe699c739 --- /dev/null +++ b/testing/btest/Baseline/core.option-errors-3/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-errors-3/option-errors.bro, line 3: option is not a modifiable lvalue (testopt) diff --git a/testing/btest/Baseline/core.option-errors-4/.stderr b/testing/btest/Baseline/core.option-errors-4/.stderr new file mode 100644 index 0000000000..b443da2eb9 --- /dev/null +++ b/testing/btest/Baseline/core.option-errors-4/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-errors-4/option-errors.bro, line 2 and /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-errors-4/option-errors.bro, line 3: already defined (testopt) diff --git a/testing/btest/Baseline/core.option-errors/.stderr b/testing/btest/Baseline/core.option-errors/.stderr new file mode 100644 index 0000000000..27a73e180d --- /dev/null +++ b/testing/btest/Baseline/core.option-errors/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-errors/option-errors.bro, line 4: no type given (testbool) diff --git a/testing/btest/Baseline/core.option-priorities/.stdout b/testing/btest/Baseline/core.option-priorities/.stdout new file mode 100644 index 0000000000..5565a18a92 --- /dev/null +++ b/testing/btest/Baseline/core.option-priorities/.stdout @@ -0,0 +1,6 @@ +Old value, T +Higher prio - Value of testbool changed from T to F at location '' +Value of testbool changed from T to T +Higher prio - Value of testbool changed from T to F at location 'here' +Value of testbool changed from T to T +New value, T diff --git a/testing/btest/Baseline/core.option-redef/.stdout b/testing/btest/Baseline/core.option-redef/.stdout new file mode 100644 index 0000000000..1e8b314962 --- /dev/null +++ b/testing/btest/Baseline/core.option-redef/.stdout @@ -0,0 +1 @@ +6 diff --git a/testing/btest/Baseline/core.option-runtime-errors-10/.stderr b/testing/btest/Baseline/core.option-runtime-errors-10/.stderr new file mode 100644 index 0000000000..3b4cf422f5 --- /dev/null +++ b/testing/btest/Baseline/core.option-runtime-errors-10/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-runtime-errors-10/option-runtime-errors.bro, line 7: ID 'A' is not an option (Option::set_change_handler(A, option_changed, (coerce 0 to int))) diff --git a/testing/btest/Baseline/core.option-runtime-errors-11/.stderr b/testing/btest/Baseline/core.option-runtime-errors-11/.stderr new file mode 100644 index 0000000000..8fd7de5d2e --- /dev/null +++ b/testing/btest/Baseline/core.option-runtime-errors-11/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-runtime-errors-11/option-runtime-errors.bro, line 4: Option::on_change needs function argument; got 'count' for ID 'A' (Option::set_change_handler(A, A, (coerce 0 to int))) diff --git a/testing/btest/Baseline/core.option-runtime-errors-12/.stderr b/testing/btest/Baseline/core.option-runtime-errors-12/.stderr new file mode 100644 index 0000000000..635b287c6b --- /dev/null +++ b/testing/btest/Baseline/core.option-runtime-errors-12/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-runtime-errors-12/option-runtime-errors.bro, line 7: Third argument of passed function has to be string in Option::on_change for ID 'A'; got 'count' (Option::set_change_handler(A, option_changed, (coerce 0 to int))) diff --git a/testing/btest/Baseline/core.option-runtime-errors-13/.stderr b/testing/btest/Baseline/core.option-runtime-errors-13/.stderr new file mode 100644 index 0000000000..7b58339d8b --- /dev/null +++ b/testing/btest/Baseline/core.option-runtime-errors-13/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-runtime-errors-13/option-runtime-errors.bro, line 7: Wrong number of arguments for passed function in Option::on_change for ID 'A'; expected 2 or 3, got 4 (Option::set_change_handler(A, option_changed, (coerce 0 to int))) diff --git a/testing/btest/Baseline/core.option-runtime-errors-2/.stderr b/testing/btest/Baseline/core.option-runtime-errors-2/.stderr new file mode 100644 index 0000000000..ad027f69db --- /dev/null +++ b/testing/btest/Baseline/core.option-runtime-errors-2/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-runtime-errors-2/option-runtime-errors.bro, line 3: Incompatible type for set of ID 'A': got 'string', need 'count' (Option::set(A, hi, )) diff --git a/testing/btest/Baseline/core.option-runtime-errors-3/.stderr b/testing/btest/Baseline/core.option-runtime-errors-3/.stderr new file mode 100644 index 0000000000..2c98b170b7 --- /dev/null +++ b/testing/btest/Baseline/core.option-runtime-errors-3/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-runtime-errors-3/option-runtime-errors.bro, line 3: ID 'A' is not an option (Option::set(A, 6, )) diff --git a/testing/btest/Baseline/core.option-runtime-errors-4/.stderr b/testing/btest/Baseline/core.option-runtime-errors-4/.stderr new file mode 100644 index 0000000000..a965ddd3ae --- /dev/null +++ b/testing/btest/Baseline/core.option-runtime-errors-4/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-runtime-errors-4/option-runtime-errors.bro, line 7: Second argument of passed function has to be count in Option::on_change for ID 'A'; got 'bool' (Option::set_change_handler(A, option_changed, (coerce 0 to int))) diff --git a/testing/btest/Baseline/core.option-runtime-errors-5/.stderr b/testing/btest/Baseline/core.option-runtime-errors-5/.stderr new file mode 100644 index 0000000000..d931ff062a --- /dev/null +++ b/testing/btest/Baseline/core.option-runtime-errors-5/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-runtime-errors-5/option-runtime-errors.bro, line 7: Wrong number of arguments for passed function in Option::on_change for ID 'A'; expected 2 or 3, got 1 (Option::set_change_handler(A, option_changed, (coerce 0 to int))) diff --git a/testing/btest/Baseline/core.option-runtime-errors-6/.stderr b/testing/btest/Baseline/core.option-runtime-errors-6/.stderr new file mode 100644 index 0000000000..593c239155 --- /dev/null +++ b/testing/btest/Baseline/core.option-runtime-errors-6/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-runtime-errors-6/option-runtime-errors.bro, line 7: Passed function needs to return type 'count' for ID 'A'; got 'bool' (Option::set_change_handler(A, option_changed, (coerce 0 to int))) diff --git a/testing/btest/Baseline/core.option-runtime-errors-7/.stderr b/testing/btest/Baseline/core.option-runtime-errors-7/.stderr new file mode 100644 index 0000000000..57f7b5c21b --- /dev/null +++ b/testing/btest/Baseline/core.option-runtime-errors-7/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-runtime-errors-7/option-runtime-errors.bro, line 7: Option::on_change needs function argument; not hook or event (Option::set_change_handler(A, option_changed, (coerce 0 to int))) diff --git a/testing/btest/Baseline/core.option-runtime-errors-8/.stderr b/testing/btest/Baseline/core.option-runtime-errors-8/.stderr new file mode 100644 index 0000000000..2e7735f433 --- /dev/null +++ b/testing/btest/Baseline/core.option-runtime-errors-8/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-runtime-errors-8/option-runtime-errors.bro, line 7: Option::on_change needs function argument; not hook or event (Option::set_change_handler(A, option_changed, (coerce 0 to int))) diff --git a/testing/btest/Baseline/core.option-runtime-errors-9/.stderr b/testing/btest/Baseline/core.option-runtime-errors-9/.stderr new file mode 100644 index 0000000000..a95196eef7 --- /dev/null +++ b/testing/btest/Baseline/core.option-runtime-errors-9/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-runtime-errors-9/option-runtime-errors.bro, line 5: Could not find ID named 'A' (Option::set_change_handler(A, option_changed, (coerce 0 to int))) diff --git a/testing/btest/Baseline/core.option-runtime-errors/.stderr b/testing/btest/Baseline/core.option-runtime-errors/.stderr new file mode 100644 index 0000000000..f3ad46d382 --- /dev/null +++ b/testing/btest/Baseline/core.option-runtime-errors/.stderr @@ -0,0 +1 @@ +error in /Users/johanna/corelight/bro/testing/btest/.tmp/core.option-runtime-errors/option-runtime-errors.bro, line 8: Could not find ID named 'B' (Option::set(B, 6, )) diff --git a/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log b/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log index 52a660261c..9a4e03d884 100644 --- a/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log +++ b/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log @@ -38,6 +38,7 @@ scripts/base/init-bare.bro scripts/base/frameworks/input/readers/raw.bro scripts/base/frameworks/input/readers/benchmark.bro scripts/base/frameworks/input/readers/binary.bro + scripts/base/frameworks/input/readers/config.bro scripts/base/frameworks/input/readers/sqlite.bro scripts/base/frameworks/analyzer/__load__.bro scripts/base/frameworks/analyzer/main.bro @@ -51,6 +52,7 @@ scripts/base/init-bare.bro scripts/base/frameworks/files/magic/__load__.bro build/scripts/base/bif/__load__.bro build/scripts/base/bif/stats.bif.bro + build/scripts/base/bif/option.bif.bro build/scripts/base/bif/broxygen.bif.bro build/scripts/base/bif/pcap.bif.bro build/scripts/base/bif/bloom-filter.bif.bro @@ -161,6 +163,7 @@ scripts/base/init-bare.bro build/scripts/base/bif/plugins/Bro_AsciiReader.ascii.bif.bro build/scripts/base/bif/plugins/Bro_BenchmarkReader.benchmark.bif.bro build/scripts/base/bif/plugins/Bro_BinaryReader.binary.bif.bro + build/scripts/base/bif/plugins/Bro_ConfigReader.config.bif.bro build/scripts/base/bif/plugins/Bro_RawReader.raw.bif.bro build/scripts/base/bif/plugins/Bro_SQLiteReader.sqlite.bif.bro build/scripts/base/bif/plugins/Bro_AsciiWriter.ascii.bif.bro diff --git a/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log b/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log index 75ef872a95..9f29b5b8ae 100644 --- a/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log +++ b/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log @@ -38,6 +38,7 @@ scripts/base/init-bare.bro scripts/base/frameworks/input/readers/raw.bro scripts/base/frameworks/input/readers/benchmark.bro scripts/base/frameworks/input/readers/binary.bro + scripts/base/frameworks/input/readers/config.bro scripts/base/frameworks/input/readers/sqlite.bro scripts/base/frameworks/analyzer/__load__.bro scripts/base/frameworks/analyzer/main.bro @@ -51,6 +52,7 @@ scripts/base/init-bare.bro scripts/base/frameworks/files/magic/__load__.bro build/scripts/base/bif/__load__.bro build/scripts/base/bif/stats.bif.bro + build/scripts/base/bif/option.bif.bro build/scripts/base/bif/broxygen.bif.bro build/scripts/base/bif/pcap.bif.bro build/scripts/base/bif/bloom-filter.bif.bro @@ -161,6 +163,7 @@ scripts/base/init-bare.bro build/scripts/base/bif/plugins/Bro_AsciiReader.ascii.bif.bro build/scripts/base/bif/plugins/Bro_BenchmarkReader.benchmark.bif.bro build/scripts/base/bif/plugins/Bro_BinaryReader.binary.bif.bro + build/scripts/base/bif/plugins/Bro_ConfigReader.config.bif.bro build/scripts/base/bif/plugins/Bro_RawReader.raw.bif.bro build/scripts/base/bif/plugins/Bro_SQLiteReader.sqlite.bif.bro build/scripts/base/bif/plugins/Bro_AsciiWriter.ascii.bif.bro @@ -238,6 +241,9 @@ scripts/base/init-default.bro scripts/base/frameworks/intel/main.bro scripts/base/frameworks/intel/files.bro scripts/base/frameworks/intel/input.bro + scripts/base/frameworks/config/__load__.bro + scripts/base/frameworks/config/main.bro + scripts/base/frameworks/config/input.bro scripts/base/frameworks/sumstats/__load__.bro scripts/base/frameworks/sumstats/main.bro scripts/base/frameworks/sumstats/plugins/__load__.bro diff --git a/testing/btest/Baseline/coverage.find-bro-logs/out b/testing/btest/Baseline/coverage.find-bro-logs/out index 09a08914fe..1433646a9f 100644 --- a/testing/btest/Baseline/coverage.find-bro-logs/out +++ b/testing/btest/Baseline/coverage.find-bro-logs/out @@ -2,6 +2,7 @@ barnyard2 capture_loss cluster communication +config conn dce__r_pc dhcp diff --git a/testing/btest/Baseline/doc.broxygen.example/example.rst b/testing/btest/Baseline/doc.broxygen.example/example.rst index 48289fe466..109784229d 100644 --- a/testing/btest/Baseline/doc.broxygen.example/example.rst +++ b/testing/btest/Baseline/doc.broxygen.example/example.rst @@ -32,8 +32,8 @@ And a custom directive does the equivalent references: Summary ~~~~~~~ -Options -####### +Redefinable Options +################### ==================================================================================== ======================================================= :bro:id:`BroxygenExample::an_option`: :bro:type:`set` :bro:attr:`&redef` Add documentation for "an_option" here. :bro:id:`BroxygenExample::option_with_init`: :bro:type:`interval` :bro:attr:`&redef` Default initialization will be generated automatically. @@ -81,8 +81,8 @@ Functions Detailed Interface ~~~~~~~~~~~~~~~~~~ -Options -####### +Redefinable Options +################### .. bro:id:: BroxygenExample::an_option :Type: :bro:type:`set` [:bro:type:`addr`, :bro:type:`addr`, :bro:type:`string`] diff --git a/testing/btest/Baseline/plugins.hooks/output b/testing/btest/Baseline/plugins.hooks/output index 42b0b6ef26..511cc3dec9 100644 --- a/testing/btest/Baseline/plugins.hooks/output +++ b/testing/btest/Baseline/plugins.hooks/output @@ -172,6 +172,7 @@ 0.000000 MetaHookPost CallFunction(Files::register_protocol, , (Analyzer::ANALYZER_SSL, [get_file_handle=SSL::get_file_handle{ return ()}, describe=SSL::describe_file{ SSL::cid{ if (SSL::f$source != SSL || !SSL::f?$info || !SSL::f$info?$x509 || !SSL::f$info$x509?$certificate) return ()for ([SSL::cid] in SSL::f$conns) { if (SSL::f$conns[SSL::cid]?$ssl) { SSL::c = SSL::f$conns[SSL::cid]return (cat(SSL::c$id$resp_h, :, SSL::c$id$resp_p))}}return (cat(Serial: , SSL::f$info$x509$certificate$serial, Subject: , SSL::f$info$x509$certificate$subject, Issuer: , SSL::f$info$x509$certificate$issuer))}}])) -> 0.000000 MetaHookPost CallFunction(Log::__add_filter, , (Cluster::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=cluster, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) -> 0.000000 MetaHookPost CallFunction(Log::__add_filter, , (Communication::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=communication, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, , (Config::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=config, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) -> 0.000000 MetaHookPost CallFunction(Log::__add_filter, , (Conn::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=conn, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) -> 0.000000 MetaHookPost CallFunction(Log::__add_filter, , (DCE_RPC::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=dce_rpc, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) -> 0.000000 MetaHookPost CallFunction(Log::__add_filter, , (DHCP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=dhcp, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) -> @@ -215,6 +216,7 @@ 0.000000 MetaHookPost CallFunction(Log::__add_filter, , (mysql::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=mysql, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (Cluster::LOG, [columns=, ev=, path=cluster])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (Communication::LOG, [columns=, ev=, path=communication])) -> +0.000000 MetaHookPost CallFunction(Log::__create_stream, , (Config::LOG, [columns=, ev=Config::log_config, path=config])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (Conn::LOG, [columns=, ev=Conn::log_conn, path=conn])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (DCE_RPC::LOG, [columns=, ev=, path=dce_rpc])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (DHCP::LOG, [columns=, ev=DHCP::log_dhcp, path=dhcp])) -> @@ -256,9 +258,10 @@ 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (Weird::LOG, [columns=, ev=Weird::log_weird, path=weird])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (X509::LOG, [columns=, ev=X509::log_x509, path=x509])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (mysql::LOG, [columns=, ev=MySQL::log_mysql, path=mysql])) -> -0.000000 MetaHookPost CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1510863910.246703, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1511991433.194848, node=bro, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (Cluster::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (Communication::LOG)) -> +0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (Config::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (Conn::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (DCE_RPC::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (DHCP::LOG)) -> @@ -302,6 +305,7 @@ 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (mysql::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_filter, , (Cluster::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) -> 0.000000 MetaHookPost CallFunction(Log::add_filter, , (Communication::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::add_filter, , (Config::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) -> 0.000000 MetaHookPost CallFunction(Log::add_filter, , (Conn::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) -> 0.000000 MetaHookPost CallFunction(Log::add_filter, , (DCE_RPC::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) -> 0.000000 MetaHookPost CallFunction(Log::add_filter, , (DHCP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) -> @@ -345,6 +349,7 @@ 0.000000 MetaHookPost CallFunction(Log::add_filter, , (mysql::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, , (Cluster::LOG, [columns=, ev=, path=cluster])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, , (Communication::LOG, [columns=, ev=, path=communication])) -> +0.000000 MetaHookPost CallFunction(Log::create_stream, , (Config::LOG, [columns=, ev=Config::log_config, path=config])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, , (Conn::LOG, [columns=, ev=Conn::log_conn, path=conn])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, , (DCE_RPC::LOG, [columns=, ev=, path=dce_rpc])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, , (DHCP::LOG, [columns=, ev=DHCP::log_dhcp, path=dhcp])) -> @@ -386,7 +391,7 @@ 0.000000 MetaHookPost CallFunction(Log::create_stream, , (Weird::LOG, [columns=, ev=Weird::log_weird, path=weird])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, , (X509::LOG, [columns=, ev=X509::log_x509, path=x509])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, , (mysql::LOG, [columns=, ev=MySQL::log_mysql, path=mysql])) -> -0.000000 MetaHookPost CallFunction(Log::write, , (PacketFilter::LOG, [ts=1510863910.246703, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::write, , (PacketFilter::LOG, [ts=1511991433.194848, node=bro, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(NetControl::check_plugins, , ()) -> 0.000000 MetaHookPost CallFunction(NetControl::init, , ()) -> 0.000000 MetaHookPost CallFunction(Notice::want_pp, , ()) -> @@ -415,6 +420,7 @@ 0.000000 MetaHookPost CallFunction(current_time, , ()) -> 0.000000 MetaHookPost CallFunction(filter_change_tracking, , ()) -> 0.000000 MetaHookPost CallFunction(getenv, , (CLUSTER_NODE)) -> +0.000000 MetaHookPost CallFunction(global_ids, , ()) -> 0.000000 MetaHookPost CallFunction(network_time, , ()) -> 0.000000 MetaHookPost CallFunction(reading_live_traffic, , ()) -> 0.000000 MetaHookPost CallFunction(reading_traces, , ()) -> @@ -431,6 +437,7 @@ 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_BenchmarkReader.benchmark.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_BinaryReader.binary.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_BitTorrent.events.bif.bro) -> -1 +0.000000 MetaHookPost LoadFile(0, .<...>/Bro_ConfigReader.config.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_ConnSize.events.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_ConnSize.functions.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_DCE_RPC.consts.bif.bro) -> -1 @@ -550,6 +557,7 @@ 0.000000 MetaHookPost LoadFile(0, .<...>/cardinality-counter.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/catch-and-release.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/comm.bif.bro) -> -1 +0.000000 MetaHookPost LoadFile(0, .<...>/config.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/const-dos-error.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/const-nt-status.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/const.bif.bro) -> -1 @@ -588,6 +596,7 @@ 0.000000 MetaHookPost LoadFile(0, .<...>/non-cluster.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/none.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/openflow.bro) -> -1 +0.000000 MetaHookPost LoadFile(0, .<...>/option.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/packetfilter.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/page.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/patterns.bro) -> -1 @@ -637,6 +646,7 @@ 0.000000 MetaHookPost LoadFile(0, base<...>/cluster) -> -1 0.000000 MetaHookPost LoadFile(0, base<...>/comm.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, base<...>/communication) -> -1 +0.000000 MetaHookPost LoadFile(0, base<...>/config) -> -1 0.000000 MetaHookPost LoadFile(0, base<...>/conn) -> -1 0.000000 MetaHookPost LoadFile(0, base<...>/conn-ids.bro) -> -1 0.000000 MetaHookPost LoadFile(0, base<...>/const.bif.bro) -> -1 @@ -907,6 +917,7 @@ 0.000000 MetaHookPre CallFunction(Files::register_protocol, , (Analyzer::ANALYZER_SSL, [get_file_handle=SSL::get_file_handle{ return ()}, describe=SSL::describe_file{ SSL::cid{ if (SSL::f$source != SSL || !SSL::f?$info || !SSL::f$info?$x509 || !SSL::f$info$x509?$certificate) return ()for ([SSL::cid] in SSL::f$conns) { if (SSL::f$conns[SSL::cid]?$ssl) { SSL::c = SSL::f$conns[SSL::cid]return (cat(SSL::c$id$resp_h, :, SSL::c$id$resp_p))}}return (cat(Serial: , SSL::f$info$x509$certificate$serial, Subject: , SSL::f$info$x509$certificate$subject, Issuer: , SSL::f$info$x509$certificate$issuer))}}])) 0.000000 MetaHookPre CallFunction(Log::__add_filter, , (Cluster::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=cluster, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) 0.000000 MetaHookPre CallFunction(Log::__add_filter, , (Communication::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=communication, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, , (Config::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=config, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) 0.000000 MetaHookPre CallFunction(Log::__add_filter, , (Conn::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=conn, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) 0.000000 MetaHookPre CallFunction(Log::__add_filter, , (DCE_RPC::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=dce_rpc, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) 0.000000 MetaHookPre CallFunction(Log::__add_filter, , (DHCP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=dhcp, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) @@ -950,6 +961,7 @@ 0.000000 MetaHookPre CallFunction(Log::__add_filter, , (mysql::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=mysql, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (Cluster::LOG, [columns=, ev=, path=cluster])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (Communication::LOG, [columns=, ev=, path=communication])) +0.000000 MetaHookPre CallFunction(Log::__create_stream, , (Config::LOG, [columns=, ev=Config::log_config, path=config])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (Conn::LOG, [columns=, ev=Conn::log_conn, path=conn])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (DCE_RPC::LOG, [columns=, ev=, path=dce_rpc])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (DHCP::LOG, [columns=, ev=DHCP::log_dhcp, path=dhcp])) @@ -991,9 +1003,10 @@ 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (Weird::LOG, [columns=, ev=Weird::log_weird, path=weird])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (X509::LOG, [columns=, ev=X509::log_x509, path=x509])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (mysql::LOG, [columns=, ev=MySQL::log_mysql, path=mysql])) -0.000000 MetaHookPre CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1510863910.246703, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1511991433.194848, node=bro, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (Cluster::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (Communication::LOG)) +0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (Config::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (Conn::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (DCE_RPC::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (DHCP::LOG)) @@ -1037,6 +1050,7 @@ 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (mysql::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_filter, , (Cluster::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) 0.000000 MetaHookPre CallFunction(Log::add_filter, , (Communication::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::add_filter, , (Config::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) 0.000000 MetaHookPre CallFunction(Log::add_filter, , (Conn::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) 0.000000 MetaHookPre CallFunction(Log::add_filter, , (DCE_RPC::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) 0.000000 MetaHookPre CallFunction(Log::add_filter, , (DHCP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) @@ -1080,6 +1094,7 @@ 0.000000 MetaHookPre CallFunction(Log::add_filter, , (mysql::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}])) 0.000000 MetaHookPre CallFunction(Log::create_stream, , (Cluster::LOG, [columns=, ev=, path=cluster])) 0.000000 MetaHookPre CallFunction(Log::create_stream, , (Communication::LOG, [columns=, ev=, path=communication])) +0.000000 MetaHookPre CallFunction(Log::create_stream, , (Config::LOG, [columns=, ev=Config::log_config, path=config])) 0.000000 MetaHookPre CallFunction(Log::create_stream, , (Conn::LOG, [columns=, ev=Conn::log_conn, path=conn])) 0.000000 MetaHookPre CallFunction(Log::create_stream, , (DCE_RPC::LOG, [columns=, ev=, path=dce_rpc])) 0.000000 MetaHookPre CallFunction(Log::create_stream, , (DHCP::LOG, [columns=, ev=DHCP::log_dhcp, path=dhcp])) @@ -1121,7 +1136,7 @@ 0.000000 MetaHookPre CallFunction(Log::create_stream, , (Weird::LOG, [columns=, ev=Weird::log_weird, path=weird])) 0.000000 MetaHookPre CallFunction(Log::create_stream, , (X509::LOG, [columns=, ev=X509::log_x509, path=x509])) 0.000000 MetaHookPre CallFunction(Log::create_stream, , (mysql::LOG, [columns=, ev=MySQL::log_mysql, path=mysql])) -0.000000 MetaHookPre CallFunction(Log::write, , (PacketFilter::LOG, [ts=1510863910.246703, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::write, , (PacketFilter::LOG, [ts=1511991433.194848, node=bro, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(NetControl::check_plugins, , ()) 0.000000 MetaHookPre CallFunction(NetControl::init, , ()) 0.000000 MetaHookPre CallFunction(Notice::want_pp, , ()) @@ -1150,6 +1165,7 @@ 0.000000 MetaHookPre CallFunction(current_time, , ()) 0.000000 MetaHookPre CallFunction(filter_change_tracking, , ()) 0.000000 MetaHookPre CallFunction(getenv, , (CLUSTER_NODE)) +0.000000 MetaHookPre CallFunction(global_ids, , ()) 0.000000 MetaHookPre CallFunction(network_time, , ()) 0.000000 MetaHookPre CallFunction(reading_live_traffic, , ()) 0.000000 MetaHookPre CallFunction(reading_traces, , ()) @@ -1166,6 +1182,7 @@ 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_BenchmarkReader.benchmark.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_BinaryReader.binary.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_BitTorrent.events.bif.bro) +0.000000 MetaHookPre LoadFile(0, .<...>/Bro_ConfigReader.config.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_ConnSize.events.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_ConnSize.functions.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_DCE_RPC.consts.bif.bro) @@ -1285,6 +1302,7 @@ 0.000000 MetaHookPre LoadFile(0, .<...>/cardinality-counter.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/catch-and-release.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/comm.bif.bro) +0.000000 MetaHookPre LoadFile(0, .<...>/config.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/const-dos-error.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/const-nt-status.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/const.bif.bro) @@ -1323,6 +1341,7 @@ 0.000000 MetaHookPre LoadFile(0, .<...>/non-cluster.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/none.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/openflow.bro) +0.000000 MetaHookPre LoadFile(0, .<...>/option.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/packetfilter.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/page.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/patterns.bro) @@ -1372,6 +1391,7 @@ 0.000000 MetaHookPre LoadFile(0, base<...>/cluster) 0.000000 MetaHookPre LoadFile(0, base<...>/comm.bif.bro) 0.000000 MetaHookPre LoadFile(0, base<...>/communication) +0.000000 MetaHookPre LoadFile(0, base<...>/config) 0.000000 MetaHookPre LoadFile(0, base<...>/conn) 0.000000 MetaHookPre LoadFile(0, base<...>/conn-ids.bro) 0.000000 MetaHookPre LoadFile(0, base<...>/const.bif.bro) @@ -1641,6 +1661,7 @@ 0.000000 | HookCallFunction Files::register_protocol(Analyzer::ANALYZER_SSL, [get_file_handle=SSL::get_file_handle{ return ()}, describe=SSL::describe_file{ SSL::cid{ if (SSL::f$source != SSL || !SSL::f?$info || !SSL::f$info?$x509 || !SSL::f$info$x509?$certificate) return ()for ([SSL::cid] in SSL::f$conns) { if (SSL::f$conns[SSL::cid]?$ssl) { SSL::c = SSL::f$conns[SSL::cid]return (cat(SSL::c$id$resp_h, :, SSL::c$id$resp_p))}}return (cat(Serial: , SSL::f$info$x509$certificate$serial, Subject: , SSL::f$info$x509$certificate$subject, Issuer: , SSL::f$info$x509$certificate$issuer))}}]) 0.000000 | HookCallFunction Log::__add_filter(Cluster::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=cluster, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}]) 0.000000 | HookCallFunction Log::__add_filter(Communication::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=communication, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Config::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=config, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}]) 0.000000 | HookCallFunction Log::__add_filter(Conn::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=conn, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}]) 0.000000 | HookCallFunction Log::__add_filter(DCE_RPC::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=dce_rpc, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}]) 0.000000 | HookCallFunction Log::__add_filter(DHCP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=dhcp, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}]) @@ -1684,6 +1705,7 @@ 0.000000 | HookCallFunction Log::__add_filter(mysql::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=mysql, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}]) 0.000000 | HookCallFunction Log::__create_stream(Cluster::LOG, [columns=, ev=, path=cluster]) 0.000000 | HookCallFunction Log::__create_stream(Communication::LOG, [columns=, ev=, path=communication]) +0.000000 | HookCallFunction Log::__create_stream(Config::LOG, [columns=, ev=Config::log_config, path=config]) 0.000000 | HookCallFunction Log::__create_stream(Conn::LOG, [columns=, ev=Conn::log_conn, path=conn]) 0.000000 | HookCallFunction Log::__create_stream(DCE_RPC::LOG, [columns=, ev=, path=dce_rpc]) 0.000000 | HookCallFunction Log::__create_stream(DHCP::LOG, [columns=, ev=DHCP::log_dhcp, path=dhcp]) @@ -1725,9 +1747,10 @@ 0.000000 | HookCallFunction Log::__create_stream(Weird::LOG, [columns=, ev=Weird::log_weird, path=weird]) 0.000000 | HookCallFunction Log::__create_stream(X509::LOG, [columns=, ev=X509::log_x509, path=x509]) 0.000000 | HookCallFunction Log::__create_stream(mysql::LOG, [columns=, ev=MySQL::log_mysql, path=mysql]) -0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1510863910.246703, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1511991433.194848, node=bro, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction Log::add_default_filter(Cluster::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Communication::LOG) +0.000000 | HookCallFunction Log::add_default_filter(Config::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Conn::LOG) 0.000000 | HookCallFunction Log::add_default_filter(DCE_RPC::LOG) 0.000000 | HookCallFunction Log::add_default_filter(DHCP::LOG) @@ -1771,6 +1794,7 @@ 0.000000 | HookCallFunction Log::add_default_filter(mysql::LOG) 0.000000 | HookCallFunction Log::add_filter(Cluster::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}]) 0.000000 | HookCallFunction Log::add_filter(Communication::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::add_filter(Config::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}]) 0.000000 | HookCallFunction Log::add_filter(Conn::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}]) 0.000000 | HookCallFunction Log::add_filter(DCE_RPC::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}]) 0.000000 | HookCallFunction Log::add_filter(DHCP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}]) @@ -1814,6 +1838,7 @@ 0.000000 | HookCallFunction Log::add_filter(mysql::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=, include=, exclude=, log_local=T, log_remote=T, field_name_map={}, scope_sep=., ext_prefix=_, ext_func=anonymous-function, interv=0 secs, postprocessor=, config={}]) 0.000000 | HookCallFunction Log::create_stream(Cluster::LOG, [columns=, ev=, path=cluster]) 0.000000 | HookCallFunction Log::create_stream(Communication::LOG, [columns=, ev=, path=communication]) +0.000000 | HookCallFunction Log::create_stream(Config::LOG, [columns=, ev=Config::log_config, path=config]) 0.000000 | HookCallFunction Log::create_stream(Conn::LOG, [columns=, ev=Conn::log_conn, path=conn]) 0.000000 | HookCallFunction Log::create_stream(DCE_RPC::LOG, [columns=, ev=, path=dce_rpc]) 0.000000 | HookCallFunction Log::create_stream(DHCP::LOG, [columns=, ev=DHCP::log_dhcp, path=dhcp]) @@ -1855,7 +1880,7 @@ 0.000000 | HookCallFunction Log::create_stream(Weird::LOG, [columns=, ev=Weird::log_weird, path=weird]) 0.000000 | HookCallFunction Log::create_stream(X509::LOG, [columns=, ev=X509::log_x509, path=x509]) 0.000000 | HookCallFunction Log::create_stream(mysql::LOG, [columns=, ev=MySQL::log_mysql, path=mysql]) -0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1510863910.246703, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1511991433.194848, node=bro, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction NetControl::check_plugins() 0.000000 | HookCallFunction NetControl::init() 0.000000 | HookCallFunction Notice::want_pp() @@ -1884,6 +1909,7 @@ 0.000000 | HookCallFunction current_time() 0.000000 | HookCallFunction filter_change_tracking() 0.000000 | HookCallFunction getenv(CLUSTER_NODE) +0.000000 | HookCallFunction global_ids() 0.000000 | HookCallFunction network_time() 0.000000 | HookCallFunction reading_live_traffic() 0.000000 | HookCallFunction reading_traces() @@ -1900,6 +1926,7 @@ 0.000000 | HookLoadFile .<...>/Bro_BenchmarkReader.benchmark.bif.bro 0.000000 | HookLoadFile .<...>/Bro_BinaryReader.binary.bif.bro 0.000000 | HookLoadFile .<...>/Bro_BitTorrent.events.bif.bro +0.000000 | HookLoadFile .<...>/Bro_ConfigReader.config.bif.bro 0.000000 | HookLoadFile .<...>/Bro_ConnSize.events.bif.bro 0.000000 | HookLoadFile .<...>/Bro_ConnSize.functions.bif.bro 0.000000 | HookLoadFile .<...>/Bro_DCE_RPC.consts.bif.bro @@ -2021,6 +2048,7 @@ 0.000000 | HookLoadFile .<...>/cardinality-counter.bif.bro 0.000000 | HookLoadFile .<...>/catch-and-release.bro 0.000000 | HookLoadFile .<...>/comm.bif.bro +0.000000 | HookLoadFile .<...>/config.bro 0.000000 | HookLoadFile .<...>/const-dos-error.bro 0.000000 | HookLoadFile .<...>/const-nt-status.bro 0.000000 | HookLoadFile .<...>/const.bif.bro @@ -2065,6 +2093,7 @@ 0.000000 | HookLoadFile .<...>/non-cluster.bro 0.000000 | HookLoadFile .<...>/none.bro 0.000000 | HookLoadFile .<...>/openflow.bro +0.000000 | HookLoadFile .<...>/option.bif.bro 0.000000 | HookLoadFile .<...>/packetfilter.bro 0.000000 | HookLoadFile .<...>/page.bro 0.000000 | HookLoadFile .<...>/patterns.bro @@ -2115,6 +2144,7 @@ 0.000000 | HookLoadFile base<...>/cluster 0.000000 | HookLoadFile base<...>/comm.bif.bro 0.000000 | HookLoadFile base<...>/communication +0.000000 | HookLoadFile base<...>/config 0.000000 | HookLoadFile base<...>/conn 0.000000 | HookLoadFile base<...>/conn-ids.bro 0.000000 | HookLoadFile base<...>/const.bif.bro @@ -2198,7 +2228,7 @@ 0.000000 | HookLoadFile base<...>/x509 0.000000 | HookLoadFile base<...>/xmpp 0.000000 | HookLogInit packet_filter 1/1 {ts (time), node (string), filter (string), init (bool), success (bool)} -0.000000 | HookLogWrite packet_filter [ts=1510863910.246703, node=bro, filter=ip or not ip, init=T, success=T] +0.000000 | HookLogWrite packet_filter [ts=1511991433.194848, node=bro, filter=ip or not ip, init=T, success=T] 0.000000 | HookQueueEvent NetControl::init() 0.000000 | HookQueueEvent bro_init() 0.000000 | HookQueueEvent filter_change_tracking() diff --git a/testing/btest/Baseline/scripts.base.frameworks.config.basic/bro.config.log b/testing/btest/Baseline/scripts.base.frameworks.config.basic/bro.config.log new file mode 100644 index 0000000000..b1e03411e5 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.config.basic/bro.config.log @@ -0,0 +1,23 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path config +#open 2017-10-11-20-23-11 +#fields ts id old_value new_value location +#types time string string string string +1507753391.587107 testbool T F ../configfile +1507753391.587107 testcount 0 1 ../configfile +1507753391.587107 testcount 1 2 ../configfile +1507753391.587107 testint 0 -1 ../configfile +1507753391.587107 testenum SSH::LOG Conn::LOG ../configfile +1507753391.587107 testport 42/tcp 45/unknown ../configfile +1507753391.587107 testaddr 127.0.0.1 127.0.0.1 ../configfile +1507753391.587107 testaddr 127.0.0.1 2607:f8b0:4005:801::200e ../configfile +1507753391.587107 testinterval 1.0 sec 60.0 ../configfile +1507753391.587107 testtime 0.0 1507321987.0 ../configfile +1507753391.587107 test_set (empty) b,c,a,d,erdbeerschnitzel ../configfile +1507753391.587107 test_vector (empty) 1,2,3,4,5,6 ../configfile +1507753391.587107 test_set b,c,a,d,erdbeerschnitzel (empty) ../configfile +1507753391.587107 test_set (empty) \x2d ../configfile +#close 2017-10-11-20-23-11 diff --git a/testing/btest/Baseline/scripts.base.frameworks.config.read_config/bro.config.log b/testing/btest/Baseline/scripts.base.frameworks.config.read_config/bro.config.log new file mode 100644 index 0000000000..fa56b8455e --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.config.read_config/bro.config.log @@ -0,0 +1,23 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path config +#open 2017-10-11-20-47-09 +#fields ts id old_value new_value location +#types time string string string string +1507754829.092788 testbool T F ../configfile +1507754829.092788 testcount 0 1 ../configfile +1507754829.092788 testcount 1 2 ../configfile +1507754829.092788 testint 0 -1 ../configfile +1507754829.092788 testenum SSH::LOG Conn::LOG ../configfile +1507754829.092788 testport 42/tcp 45/unknown ../configfile +1507754829.092788 testaddr 127.0.0.1 127.0.0.1 ../configfile +1507754829.092788 testaddr 127.0.0.1 2607:f8b0:4005:801::200e ../configfile +1507754829.092788 testinterval 1.0 sec 60.0 ../configfile +1507754829.092788 testtime 0.0 1507321987.0 ../configfile +1507754829.092788 test_set (empty) b,c,a,d,erdbeerschnitzel ../configfile +1507754829.092788 test_vector (empty) 1,2,3,4,5,6 ../configfile +1507754829.092788 test_set b,c,a,d,erdbeerschnitzel (empty) ../configfile +1507754829.092788 test_set (empty) \x2d ../configfile +#close 2017-10-11-20-47-09 diff --git a/testing/btest/Baseline/scripts.base.frameworks.config.several-files/bro.config.log b/testing/btest/Baseline/scripts.base.frameworks.config.several-files/bro.config.log new file mode 100644 index 0000000000..d8e388f60b --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.config.several-files/bro.config.log @@ -0,0 +1,19 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path config +#open 2017-10-11-21-01-26 +#fields ts id old_value new_value location +#types time string string string string +1507755686.516466 testbool T F ../configfile1 +1507755686.516466 testcount 0 2 ../configfile1 +1507755686.516466 testint 0 -1 ../configfile1 +1507755686.516466 testenum SSH::LOG Conn::LOG ../configfile1 +1507755686.516466 test_set (empty) b,c,a,d,erdbeerschnitzel ../configfile1 +1507755686.516466 test_vector (empty) 1,2,3,4,5,6 ../configfile1 +1507755686.516466 testport 42/tcp 45/unknown ../configfile2 +1507755686.516466 testaddr 127.0.0.1 127.0.0.1 ../configfile2 +1507755686.516466 testinterval 1.0 sec 60.0 ../configfile2 +1507755686.516466 testtime 0.0 1507321987.0 ../configfile2 +#close 2017-10-11-21-01-26 diff --git a/testing/btest/Baseline/scripts.base.frameworks.config.updates/bro.config.log b/testing/btest/Baseline/scripts.base.frameworks.config.updates/bro.config.log new file mode 100644 index 0000000000..2e4f75b903 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.config.updates/bro.config.log @@ -0,0 +1,26 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path config +#open 2017-10-11-21-00-06 +#fields ts id old_value new_value location +#types time string string string string +1507755606.563360 testbool T F ../configfile +1507755606.563360 testcount 0 1 ../configfile +1507755606.563360 testcount 1 2 ../configfile +1507755606.563360 testint 0 -1 ../configfile +1507755606.563360 testenum SSH::LOG Conn::LOG ../configfile +1507755606.563360 testport 42/tcp 45/unknown ../configfile +1507755606.563360 testaddr 127.0.0.1 127.0.0.1 ../configfile +1507755606.563360 testaddr 127.0.0.1 2607:f8b0:4005:801::200e ../configfile +1507755606.563360 testinterval 1.0 sec 60.0 ../configfile +1507755606.563360 testtime 0.0 1507321987.0 ../configfile +1507755606.563360 test_set (empty) b,c,a,d,erdbeerschnitzel ../configfile +1507755606.563360 test_vector (empty) 1,2,3,4,5,6 ../configfile +1507755609.793956 testcount 2 1 ../configfile +1507755609.793956 testcount 1 2 ../configfile +1507755609.793956 testaddr 2607:f8b0:4005:801::200e 127.0.0.1 ../configfile +1507755609.793956 testaddr 127.0.0.1 2607:f8b0:4005:801::200e ../configfile +1507755609.793956 test_vector 1,2,3,4,5,6 1,2,3,4,5,9 ../configfile +#close 2017-10-11-21-00-09 diff --git a/testing/btest/Baseline/scripts.base.frameworks.input.basic/out b/testing/btest/Baseline/scripts.base.frameworks.input.basic/out index 694f708fd8..3f288d5c54 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.input.basic/out +++ b/testing/btest/Baseline/scripts.base.frameworks.input.basic/out @@ -1,5 +1,5 @@ { -[-42] = [b=T, e=SSH::LOG, c=21, p=123/unknown, sn=10.0.0.0/24, a=1.2.3.4, d=3.14, t=1315801931.273616, iv=100.0, s=hurz, ns=4242, sc={ +[-42] = [b=T, e=SSH::LOG, c=21, p=123/unknown, pp=5/icmp, sn=10.0.0.0/24, a=1.2.3.4, d=3.14, t=1315801931.273616, iv=100.0, s=hurz, ns=4242, sc={ 2, 4, 1, diff --git a/testing/btest/Baseline/scripts.base.frameworks.input.config.basic/out b/testing/btest/Baseline/scripts.base.frameworks.input.config.basic/out new file mode 100644 index 0000000000..1863c7e8ea --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.input.config.basic/out @@ -0,0 +1,28 @@ +testbool, F +testcount, 1 +testcount, 2 +testint, -1 +testenum, Conn::LOG +testport, 45/unknown +testportandproto, 45/udp +testaddr, 127.0.0.1 +testaddr, 2607:f8b0:4005:801::200e +testinterval, 60.0 +testtime, 1507321987.0 +test_set, { +b, +c, +a, +d, +erdbeerschnitzel +} +test_vector, [1, 2, 3, 4, 5, 6] +test_set, { +(empty) +} +test_set, { + +} +test_set, { +- +} diff --git a/testing/btest/Baseline/scripts.base.frameworks.input.config.errors/errout b/testing/btest/Baseline/scripts.base.frameworks.input.config.errors/errout new file mode 100644 index 0000000000..92877a794c --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.input.config.errors/errout @@ -0,0 +1,14 @@ +warning: ../configfile/Input::READER_CONFIG: Field: testbool Invalid value for boolean: A +warning: ../configfile/Input::READER_CONFIG: Could not convert line 'testbool A' to Val. Ignoring line. +warning: ../configfile/Input::READER_CONFIG: Could not parse 'testtesttesttesttesttest'; line has invalid format. Ignoring line. +warning: ../configfile/Input::READER_CONFIG: Field: testbool Invalid value for boolean: A B +warning: ../configfile/Input::READER_CONFIG: Could not convert line 'testbool A B' to Val. Ignoring line. +warning: ../configfile/Input::READER_CONFIG: String 'A' contained no parseable number +warning: ../configfile/Input::READER_CONFIG: Could not convert line 'testcount A' to Val. Ignoring line. +warning: Value 'unknown' for stream 'configuration' is not a valid enum. +error: SendEvent for event InputConfig::new_value failed +warning: ../configfile/Input::READER_CONFIG: Option 'testbooool' does not exist. Ignoring line. +warning: ../configfile/Input::READER_CONFIG: Option 'test_any' has type 'any', which is not supported for file input. Ignoring line. +warning: ../configfile/Input::READER_CONFIG: Option 'test_table' has type 'table', which is not supported for file input. Ignoring line. +received termination signal +>>> diff --git a/testing/btest/core/option-errors.bro b/testing/btest/core/option-errors.bro new file mode 100644 index 0000000000..6a53598650 --- /dev/null +++ b/testing/btest/core/option-errors.bro @@ -0,0 +1,18 @@ +# @TEST-EXEC-FAIL: bro %INPUT +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff .stderr + +option testbool; + +@TEST-START-NEXT + +option testbool : bool; + +@TEST-START-NEXT + +option testopt = 5; +testopt = 6; + +@TEST-START-NEXT + +option testopt = 5; +redef testopt = 6; diff --git a/testing/btest/core/option-priorities.bro b/testing/btest/core/option-priorities.bro new file mode 100644 index 0000000000..fd352a5459 --- /dev/null +++ b/testing/btest/core/option-priorities.bro @@ -0,0 +1,28 @@ +# @TEST-EXEC: bro %INPUT +# @TEST-EXEC: btest-diff .stdout + +export { + ## Test some documentation here! + option testbool: bool = T; +} + +function option_changed(ID: string, new_value: bool): bool { + print fmt("Value of %s changed from %s to %s", ID, testbool, new_value); + return new_value; +} + +function option_changed_two(ID: string, new_value: bool, location: string): bool { + print fmt("Higher prio - Value of %s changed from %s to %s at location '%s'", ID, testbool, new_value, location); + return T; +} + +event bro_init() + { + print "Old value", testbool; + Option::set_change_handler("testbool", option_changed); + Option::set_change_handler("testbool", option_changed_two, 99); + Option::set("testbool", F); + Option::set("testbool", F, "here"); + print "New value", testbool; + } + diff --git a/testing/btest/core/option-redef.bro b/testing/btest/core/option-redef.bro new file mode 100644 index 0000000000..05706ab48b --- /dev/null +++ b/testing/btest/core/option-redef.bro @@ -0,0 +1,12 @@ +# @TEST-EXEC: bro %INPUT +# @TEST-EXEC: btest-diff .stdout + +# options are allowed to be redef-able. + +option testopt = 5 &redef; +redef testopt = 6; + +event bro_init() { + print testopt; +} + diff --git a/testing/btest/core/option-runtime-errors.bro b/testing/btest/core/option-runtime-errors.bro new file mode 100644 index 0000000000..8ae4b9ca40 --- /dev/null +++ b/testing/btest/core/option-runtime-errors.bro @@ -0,0 +1,104 @@ +# @TEST-EXEC: bro %INPUT +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff .stderr + +# Errors that happen during runtime. At least at the moment we are not checking these early enough +# that Bro will bail out during startup. Perhaps we want to change this later. + +option A = 5; +Option::set("B", 6); + +@TEST-START-NEXT + +option A = 5; +Option::set("A", "hi"); + +@TEST-START-NEXT + +const A = 5; +Option::set("A", 6); + +@TEST-START-NEXT: + +option A = 5; + +function option_changed(ID: string, new_value: bool): bool { +} + +Option::set_change_handler("A", option_changed); + +@TEST-START-NEXT: + +option A = 5; + +function option_changed(ID: string): bool { +} + +Option::set_change_handler("A", option_changed); + +@TEST-START-NEXT: + +option A : count = 5; + +function option_changed(ID: string, new_value: count): bool { +} + +Option::set_change_handler("A", option_changed); + +@TEST-START-NEXT: + +option A : count = 5; + +hook option_changed(ID: string, new_value: count) { +} + +Option::set_change_handler("A", option_changed); + +@TEST-START-NEXT: + +option A : count = 5; + +event option_changed(ID: string, new_value: count) { +} + +Option::set_change_handler("A", option_changed); + +@TEST-START-NEXT: + +function option_changed(ID: string, new_value: count) : count { +} + +Option::set_change_handler("A", option_changed); + + +@TEST-START-NEXT: + +const A : count = 5; + +function option_changed(ID: string, new_value: count) : count { +} + +Option::set_change_handler("A", option_changed); + +@TEST-START-NEXT: + +option A : count = 5; + +Option::set_change_handler("A", A); + +@TEST-START-NEXT: + +option A : count = 5; + +function option_changed(ID: string, new_value: count, location: count) : count { +} + +Option::set_change_handler("A", option_changed); + +@TEST-START-NEXT: + +option A : count = 5; + +function option_changed(ID: string, new_value: count, location: string, a: count) : count { +} + +Option::set_change_handler("A", option_changed); diff --git a/testing/btest/scripts/base/frameworks/config/basic.bro b/testing/btest/scripts/base/frameworks/config/basic.bro new file mode 100644 index 0000000000..19a1ecc64a --- /dev/null +++ b/testing/btest/scripts/base/frameworks/config/basic.bro @@ -0,0 +1,52 @@ +# @TEST-EXEC: btest-bg-run bro bro -b %INPUT +# @TEST-EXEC: btest-bg-wait 10 +# @TEST-EXEC: btest-diff bro/config.log + +@load base/frameworks/config +@load base/protocols/conn + +redef exit_only_after_terminate = T; +redef Config::config_files += {"../configfile"}; + +@TEST-START-FILE configfile +testbool F +testcount 1 +testcount 2 +testcount 2 +testint -1 +testenum Conn::LOG +testport 45 +testaddr 127.0.0.1 +testaddr 2607:f8b0:4005:801::200e +testinterval 60 +testtime 1507321987 +test_set a,b,c,d,erdbeerschnitzel +test_vector 1,2,3,4,5,6 +test_set (empty) +test_set - +@TEST-END-FILE + +@load base/protocols/ssh +@load base/protocols/conn + +export { + option testbool: bool = T; + option testcount: count = 0; + option testint: int = 0; + option testenum = SSH::LOG; + option testport = 42/tcp; + option testaddr = 127.0.0.1; + option testtime = network_time(); + option testinterval = 1sec; + option teststring = "a"; + option test_set: set[string] = {}; + option test_vector: vector of count = {}; +} + +event Input::end_of_data(name: string, source:string) + { + if ( sub_bytes(name, 1, 7) != "config-" ) + return; + + terminate(); + } diff --git a/testing/btest/scripts/base/frameworks/config/read_config.bro b/testing/btest/scripts/base/frameworks/config/read_config.bro new file mode 100644 index 0000000000..847bab6f8a --- /dev/null +++ b/testing/btest/scripts/base/frameworks/config/read_config.bro @@ -0,0 +1,56 @@ +# @TEST-EXEC: btest-bg-run bro bro -b %INPUT +# @TEST-EXEC: btest-bg-wait 10 +# @TEST-EXEC: btest-diff bro/config.log + +@load base/frameworks/config +@load base/protocols/conn + +redef exit_only_after_terminate = T; + +@TEST-START-FILE configfile +testbool F +testcount 1 +testcount 2 +testcount 2 +testint -1 +testenum Conn::LOG +testport 45 +testaddr 127.0.0.1 +testaddr 2607:f8b0:4005:801::200e +testinterval 60 +testtime 1507321987 +test_set a,b,c,d,erdbeerschnitzel +test_vector 1,2,3,4,5,6 +test_set (empty) +test_set - +@TEST-END-FILE + +@load base/protocols/ssh +@load base/protocols/conn + +export { + option testbool: bool = T; + option testcount: count = 0; + option testint: int = 0; + option testenum = SSH::LOG; + option testport = 42/tcp; + option testaddr = 127.0.0.1; + option testtime = network_time(); + option testinterval = 1sec; + option teststring = "a"; + option test_set: set[string] = {}; + option test_vector: vector of count = {}; +} + +event Input::end_of_data(name: string, source:string) + { + if ( sub_bytes(name, 1, 7) != "config-" ) + return; + + terminate(); + } + +event bro_init() + { + Config::read_config("../configfile"); + } diff --git a/testing/btest/scripts/base/frameworks/config/several-files.bro b/testing/btest/scripts/base/frameworks/config/several-files.bro new file mode 100644 index 0000000000..d6b84cdaaa --- /dev/null +++ b/testing/btest/scripts/base/frameworks/config/several-files.bro @@ -0,0 +1,50 @@ +# @TEST-EXEC: btest-bg-run bro bro -b %INPUT +# @TEST-EXEC: btest-bg-wait 10 +# @TEST-EXEC: btest-diff bro/config.log + +@load base/frameworks/config +@load base/protocols/conn + +redef exit_only_after_terminate = T; +redef Config::config_files += {"../configfile1", "../configfile2"}; + +@TEST-START-FILE configfile1 +testbool F +testcount 2 +testint -1 +testenum Conn::LOG +test_set a,b,c,d,erdbeerschnitzel +test_vector 1,2,3,4,5,6 +@TEST-END-FILE + +@TEST-START-FILE configfile2 +testport 45 +testaddr 127.0.0.1 +testinterval 60 +testtime 1507321987 +@TEST-END-FILE + +@load base/protocols/ssh +@load base/protocols/conn + +export { + option testbool: bool = T; + option testcount: count = 0; + option testint: int = 0; + option testenum = SSH::LOG; + option testport = 42/tcp; + option testaddr = 127.0.0.1; + option testtime = network_time(); + option testinterval = 1sec; + option teststring = "a"; + option test_set: set[string] = {}; + option test_vector: vector of count = {}; +} + +event Input::end_of_data(name: string, source:string) + { + if ( sub_bytes(name, 1, 7) != "config-" ) + return; + + terminate(); + } diff --git a/testing/btest/scripts/base/frameworks/config/updates.bro b/testing/btest/scripts/base/frameworks/config/updates.bro new file mode 100644 index 0000000000..7e42162190 --- /dev/null +++ b/testing/btest/scripts/base/frameworks/config/updates.bro @@ -0,0 +1,76 @@ +# @TEST-EXEC: btest-bg-run bro bro -b %INPUT +# @TEST-EXEC: sleep 2 +# @TEST-EXEC: mv configfile2 configfile +# @TEST-EXEC: touch configfile +# @TEST-EXEC: btest-bg-wait 10 +# @TEST-EXEC: btest-diff bro/config.log + +@load base/frameworks/config +@load base/protocols/conn +@load base/frameworks/communication # let network-time run + +redef exit_only_after_terminate = T; +redef Config::config_files += {"../configfile"}; + +@TEST-START-FILE configfile +testbool F +testcount 1 +testcount 2 +testcount 2 +testint -1 +testenum Conn::LOG +testport 45 +testaddr 127.0.0.1 +testaddr 2607:f8b0:4005:801::200e +testinterval 60 +testtime 1507321987 +test_set a,b,c,d,erdbeerschnitzel +test_vector 1,2,3,4,5,6 +@TEST-END-FILE + +@TEST-START-FILE configfile2 +testbool F +testcount 1 +testcount 2 +testcount 2 +testint -1 +testenum Conn::LOG +testport 45 +testaddr 127.0.0.1 +testaddr 2607:f8b0:4005:801::200e +testinterval 60 +testtime 1507321987 +test_set a,b,c,d,erdbeerschnitzel +test_vector 1,2,3,4,5,9 +@TEST-END-FILE + +@load base/protocols/ssh +@load base/protocols/conn + +export { + option testbool: bool = T; + option testcount: count = 0; + option testint: int = 0; + option testenum = SSH::LOG; + option testport = 42/tcp; + option testaddr = 127.0.0.1; + option testtime = network_time(); + option testinterval = 1sec; + option teststring = "a"; + option test_set: set[string] = {}; + option test_vector: vector of count = {}; +} + +global eolcount = 0; + +event Input::end_of_data(name: string, source:string) + { + print "eod"; + if ( sub_bytes(name, 1, 7) != "config-" ) + return; + + eolcount += 1; + + if ( eolcount == 2 ) + terminate(); + } diff --git a/testing/btest/scripts/base/frameworks/input/basic.bro b/testing/btest/scripts/base/frameworks/input/basic.bro index d52af7d6e2..e77a418f0d 100644 --- a/testing/btest/scripts/base/frameworks/input/basic.bro +++ b/testing/btest/scripts/base/frameworks/input/basic.bro @@ -7,9 +7,9 @@ redef exit_only_after_terminate = T; @TEST-START-FILE input.log #separator \x09 #path ssh -#fields b i e c p sn a d t iv s sc ss se vc ve ns -#types bool int enum count port subnet addr double time interval string table table table vector vector string -T -42 SSH::LOG 21 123 10.0.0.0/24 1.2.3.4 3.14 1315801931.273616 100.000000 hurz 2,4,1,3 CC,AA,BB EMPTY 10,20,30 EMPTY 4242 +#fields b i e c p pp sn a d t iv s sc ss se vc ve ns +#types bool int enum count port port subnet addr double time interval string table table table vector vector string +T -42 SSH::LOG 21 123 5/icmp 10.0.0.0/24 1.2.3.4 3.14 1315801931.273616 100.000000 hurz 2,4,1,3 CC,AA,BB EMPTY 10,20,30 EMPTY 4242 @TEST-END-FILE @load base/protocols/ssh @@ -29,6 +29,7 @@ type Val: record { e: Log::ID; c: count; p: port; + pp: port; sn: subnet; a: addr; d: double; diff --git a/testing/btest/scripts/base/frameworks/input/config/basic.bro b/testing/btest/scripts/base/frameworks/input/config/basic.bro new file mode 100644 index 0000000000..c8d68fc822 --- /dev/null +++ b/testing/btest/scripts/base/frameworks/input/config/basic.bro @@ -0,0 +1,75 @@ +# @TEST-EXEC: btest-bg-run bro bro -b %INPUT +# @TEST-EXEC: btest-bg-wait 10 +# @TEST-EXEC: btest-diff out + +redef exit_only_after_terminate = T; +redef InputConfig::empty_field = "EMPTY"; +redef InputConfig::set_separator = "\t"; + +@TEST-START-FILE configfile +testbool F +testcount 1 +testcount 2 +testcount 2 +testint -1 +testenum Conn::LOG +testport 45 +testportandproto 45/udp +testaddr 127.0.0.1 +testaddr 2607:f8b0:4005:801::200e +testinterval 60 +testtime 1507321987 +test_set a b c d erdbeerschnitzel +test_vector 1 2 3 4 5 6 +test_set (empty) +test_set EMPTY +test_set - +@TEST-END-FILE + +@load base/protocols/ssh +@load base/protocols/conn + +global outfile: file; + +export { + option testbool: bool = T; + option testcount: count = 0; + option testint: int = 0; + option testenum = SSH::LOG; + option testport = 42/tcp; + option testportandproto = 42/tcp; + option testaddr = 127.0.0.1; + option testtime = network_time(); + option testinterval = 1sec; + option teststring = "a"; + option test_set: set[string] = {}; + option test_vector: vector of count = {}; +} + +type Idx: record { + option_name: string; +}; + +type Val: record { + option_val: string; +}; + +global currconfig: table[string] of string = table(); + +event InputConfig::new_value(name: string, source: string, id: string, value: any) + { + print outfile, id, value; + } + +event Input::end_of_data(name: string, source:string) + { + close(outfile); + terminate(); + } + +event bro_init() + { + outfile = open("../out"); + Input::add_table([$reader=Input::READER_CONFIG, $source="../configfile", $name="configuration", $idx=Idx, $val=Val, $destination=currconfig, $want_record=F]); + } + diff --git a/testing/btest/scripts/base/frameworks/input/config/errors.bro b/testing/btest/scripts/base/frameworks/input/config/errors.bro new file mode 100644 index 0000000000..4f398956dc --- /dev/null +++ b/testing/btest/scripts/base/frameworks/input/config/errors.bro @@ -0,0 +1,66 @@ +# @TEST-EXEC: btest-bg-run bro bro -b %INPUT +# @TEST-EXEC: btest-bg-wait 10 +# @TEST-EXEC: tail -n +2 .stderr > errout +# @TEST-EXEC: btest-diff errout + +redef exit_only_after_terminate = T; + +@TEST-START-FILE configfile +testbool A +testtesttesttesttesttest +testbool A B +testcount A +testenum unknown +testbooool T +test_any F +test_table whatever +@TEST-END-FILE + +@load base/protocols/ssh +@load base/protocols/conn + +global outfile: file; + +export { + option testbool: bool = T; + option testcount: count = 0; + option testint: int = 0; + option testenum = SSH::LOG; + option testport = 42/tcp; + option testaddr = 127.0.0.1; + option testtime = network_time(); + option testinterval = 1sec; + option teststring = "a"; + option test_set: set[string] = {}; + option test_vector: vector of count = {}; + option test_any: any = 5; + option test_table: table[string] of string = {}; +} + +type Idx: record { + option_name: string; +}; + +type Val: record { + option_val: string; +}; + +global currconfig: table[string] of string = table(); + +event InputConfig::new_value(name: string, source: string, id: string, value: any) + { + print outfile, id, value; + } + +event Input::end_of_data(name: string, source:string) + { + close(outfile); + terminate(); + } + +event bro_init() + { + outfile = open("../out"); + Input::add_table([$reader=Input::READER_CONFIG, $source="../configfile", $name="configuration", $idx=Idx, $val=Val, $destination=currconfig, $want_record=F]); + } +