diff --git a/scripts/base/frameworks/file-analysis/main.bro b/scripts/base/frameworks/file-analysis/main.bro index 43c0c7a3ac..49e8ed363a 100644 --- a/scripts/base/frameworks/file-analysis/main.bro +++ b/scripts/base/frameworks/file-analysis/main.bro @@ -33,8 +33,13 @@ export { ## TODO: what's a reasonable default? const default_data_event_len: count = 1024*1024 &redef; + # Needed a forward declaration for event parameters... + type Info: record {}; + type ActionArgs: record { extract_filename: string &optional; + chunk_event: event(info: Info, data: string, off: count) &optional; + stream_event: event(info: Info, data: string) &optional; }; type ActionResults: record { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b1efabf60e..3f8fd07be5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -455,6 +455,7 @@ set(bro_SRCS file_analysis/Action.h file_analysis/Extract.cc file_analysis/Hash.cc + file_analysis/DataEvent.cc nb_dns.c digest.h diff --git a/src/file_analysis.bif b/src/file_analysis.bif index 3ee8865b8f..a814f2e574 100644 --- a/src/file_analysis.bif +++ b/src/file_analysis.bif @@ -61,11 +61,12 @@ enum Action %{ ACTION_MD5, ACTION_SHA1, ACTION_SHA256, + ACTION_DATA_EVENT, %} function FileAnalysis::postpone_timeout%(file_id: string%): bool %{ - using namespace file_analysis; + using file_analysis::FileID; bool result = file_mgr->PostponeTimeout(FileID(file_id->CheckString())); return new Val(result, TYPE_BOOL); %} @@ -74,9 +75,9 @@ function FileAnalysis::add_action%(file_id: string, action: FileAnalysis::Action, args: any%): bool %{ - using namespace file_analysis; - RecordVal* rv = args->AsRecordVal()->CoerceTo( - BifType::Record::FileAnalysis::ActionArgs); + using file_analysis::FileID; + using BifType::Record::FileAnalysis::ActionArgs; + RecordVal* rv = args->AsRecordVal()->CoerceTo(ActionArgs); bool result = file_mgr->AddAction(FileID(file_id->CheckString()), action->AsEnumVal(), rv); Unref(rv); @@ -86,7 +87,7 @@ function FileAnalysis::add_action%(file_id: string, function FileAnalysis::remove_action%(file_id: string, action: FileAnalysis::Action%): bool %{ - using namespace file_analysis; + using file_analysis::FileID; bool result = file_mgr->RemoveAction(FileID(file_id->CheckString()), action->AsEnumVal()); return new Val(result, TYPE_BOOL); @@ -94,7 +95,7 @@ function FileAnalysis::remove_action%(file_id: string, function FileAnalysis::stop%(file_id: string%): bool %{ - using namespace file_analysis; + using file_analysis::FileID; bool result = file_mgr->RemoveFile(FileID(file_id->CheckString())); return new Val(result, TYPE_BOOL); %} diff --git a/src/file_analysis/DataEvent.cc b/src/file_analysis/DataEvent.cc new file mode 100644 index 0000000000..fb8e4e39aa --- /dev/null +++ b/src/file_analysis/DataEvent.cc @@ -0,0 +1,69 @@ +#include + +#include "DataEvent.h" +#include "EventRegistry.h" +#include "Event.h" +#include "util.h" + +using namespace file_analysis; + +DataEvent::DataEvent(Info* arg_info, EventHandlerPtr ce, EventHandlerPtr se) + : Action(arg_info, BifEnum::FileAnalysis::ACTION_DATA_EVENT), + chunk_event(ce), stream_event(se) + { + } + +Action* DataEvent::Instantiate(const RecordVal* args, Info* info) + { + using BifType::Record::FileAnalysis::ActionArgs; + + const char* chunk_field = "chunk_event"; + const char* stream_field = "stream_event"; + int chunk_off = ActionArgs->FieldOffset(chunk_field); + int stream_off = ActionArgs->FieldOffset(stream_field); + + Val* chunk_val = args->Lookup(chunk_off); + Val* stream_val = args->Lookup(stream_off); + + if ( ! chunk_val && ! stream_val ) return 0; + + EventHandlerPtr chunk; + EventHandlerPtr stream; + + if ( chunk_val ) + chunk = event_registry->Lookup(chunk_val->AsFunc()->GetID()->Name()); + + if ( stream_val ) + stream = event_registry->Lookup(stream_val->AsFunc()->GetID()->Name()); + + return new DataEvent(info, chunk, stream); + } + +bool DataEvent::DeliverChunk(const u_char* data, uint64 len, uint64 offset) + { + Action::DeliverChunk(data, len, offset); + + if ( ! chunk_event ) return true; + + val_list* args = new val_list; + args->append(info->GetVal()->Ref()); + args->append(new StringVal(new BroString(data, len, 0))); + args->append(new Val(offset, TYPE_COUNT)); + mgr.QueueEvent(chunk_event, args); + + return true; + } + +bool DataEvent::DeliverStream(const u_char* data, uint64 len) + { + Action::DeliverStream(data, len); + + if ( ! stream_event ) return true; + + val_list* args = new val_list; + args->append(info->GetVal()->Ref()); + args->append(new StringVal(new BroString(data, len, 0))); + mgr.QueueEvent(stream_event, args); + + return true; + } diff --git a/src/file_analysis/DataEvent.h b/src/file_analysis/DataEvent.h new file mode 100644 index 0000000000..e7df47cc8c --- /dev/null +++ b/src/file_analysis/DataEvent.h @@ -0,0 +1,34 @@ +#ifndef FILE_ANALYSIS_DATAEVENT_H +#define FILE_ANALYSIS_DATAEVENT_H + +#include + +#include "Val.h" +#include "Info.h" +#include "Action.h" + +namespace file_analysis { + +/** + * An action to send file data to script-layer events. + */ +class DataEvent : public Action { +public: + + static Action* Instantiate(const RecordVal* args, Info* info); + + virtual bool DeliverChunk(const u_char* data, uint64 len, uint64 offset); + + virtual bool DeliverStream(const u_char* data, uint64 len); + +protected: + + DataEvent(Info* arg_info, EventHandlerPtr ce, EventHandlerPtr se); + + EventHandlerPtr chunk_event; + EventHandlerPtr stream_event; +}; + +} // namespace file_analysis + +#endif diff --git a/src/file_analysis/Hash.cc b/src/file_analysis/Hash.cc index 1bb8e48e5d..7a095039f4 100644 --- a/src/file_analysis/Hash.cc +++ b/src/file_analysis/Hash.cc @@ -50,5 +50,5 @@ void Hash::Finalize() if ( i < 0 ) reporter->InternalError("Hash Action result field not found"); - info->Results()->Assign(i, sv); + info->GetResults()->Assign(i, sv); } diff --git a/src/file_analysis/Info.cc b/src/file_analysis/Info.cc index e2e0961c28..ad4ebb2074 100644 --- a/src/file_analysis/Info.cc +++ b/src/file_analysis/Info.cc @@ -9,6 +9,7 @@ #include "Action.h" #include "Extract.h" #include "Hash.h" +#include "DataEvent.h" using namespace file_analysis; @@ -18,6 +19,7 @@ static ActionInstantiator action_factory[] = { MD5::Instantiate, SHA1::Instantiate, SHA256::Instantiate, + DataEvent::Instantiate, }; static TableVal* empty_conn_id_set() @@ -150,12 +152,12 @@ int Info::Idx(const string& field) return rval; } -double Info::TimeoutInterval() const +double Info::GetTimeoutInterval() const { return LookupFieldDefaultInterval(timeout_interval_idx); } -RecordVal* Info::Results() const +RecordVal* Info::GetResults() const { return val->Lookup(action_results_idx)->AsRecordVal(); } @@ -182,7 +184,7 @@ bool Info::IsComplete() const void Info::ScheduleInactivityTimer() const { - timer_mgr->Add(new InfoTimer(network_time, file_id, TimeoutInterval())); + timer_mgr->Add(new InfoTimer(network_time, file_id, GetTimeoutInterval())); } bool Info::AddAction(ActionTag act, RecordVal* args) diff --git a/src/file_analysis/Info.h b/src/file_analysis/Info.h index 7a89e7d898..e61e375ec1 100644 --- a/src/file_analysis/Info.h +++ b/src/file_analysis/Info.h @@ -19,10 +19,15 @@ public: ~Info(); + /** + * @return the #val record. + */ + RecordVal* GetVal() const { return val; } + /** * @return value (seconds) of the "timeout_interval" field from #val record. */ - double TimeoutInterval() const; + double GetTimeoutInterval() const; /** * @return value of the "file_id" field from #val record. @@ -32,17 +37,17 @@ public: /** * @return record val of the "action_results" field from #val record. */ - RecordVal* Results() const; + RecordVal* GetResults() const; /** * @return the string which uniquely identifies the file. */ - string Unique() const { return unique; } + string GetUnique() const { return unique; } /** * @return #last_activity_time */ - double LastActivityTime() const { return last_activity_time; } + double GetLastActivityTime() const { return last_activity_time; } /** * Refreshes #last_activity_time with current network time. diff --git a/src/file_analysis/InfoTimer.cc b/src/file_analysis/InfoTimer.cc index f1bb524d40..c855d0a54d 100644 --- a/src/file_analysis/InfoTimer.cc +++ b/src/file_analysis/InfoTimer.cc @@ -9,7 +9,7 @@ void InfoTimer::Dispatch(double t, int is_expire) if ( ! info ) return; - double last_active = info->LastActivityTime(); + double last_active = info->GetLastActivityTime(); double inactive_time = t > last_active ? t - last_active : 0.0; DBG_LOG(DBG_FILE_ANALYSIS, "Checking inactivity for %s, last active at %f, " @@ -23,7 +23,7 @@ void InfoTimer::Dispatch(double t, int is_expire) return; } - if ( inactive_time >= info->TimeoutInterval() ) + if ( inactive_time >= info->GetTimeoutInterval() ) file_mgr->Timeout(file_id); else if ( ! is_expire ) info->ScheduleInactivityTimer(); diff --git a/src/file_analysis/Manager.cc b/src/file_analysis/Manager.cc index dbb8366ded..7b0df9055b 100644 --- a/src/file_analysis/Manager.cc +++ b/src/file_analysis/Manager.cc @@ -185,7 +185,7 @@ bool Manager::RemoveFile(const FileID& file_id) if ( it == id_map.end() ) return false; - if ( ! str_map.erase(it->second->Unique()) ) + if ( ! str_map.erase(it->second->GetUnique()) ) reporter->Error("No string mapping for file ID %s", file_id.c_str()); delete it->second; id_map.erase(it);