// See the file "COPYING" in the main distribution directory for copyright. #include "zeek/Event.h" #include #include "zeek/Desc.h" #include "zeek/EventRegistry.h" #include "zeek/Trigger.h" #include "zeek/Type.h" #include "zeek/Val.h" #include "zeek/iosource/Manager.h" #include "zeek/plugin/Manager.h" #include "zeek/util.h" #include "const.bif.netvar_h" #include "event.bif.netvar_h" zeek::EventMgr zeek::event_mgr; namespace zeek { detail::EventMetadataVectorPtr detail::MakeEventMetadataVector(double t) { auto tv = make_intrusive(t); auto entry = detail::MetadataEntry{static_cast(detail::MetadataType::NetworkTimestamp), std::move(tv)}; return std::make_unique(std::vector{std::move(entry)}); } RecordValPtr detail::MetadataEntry::BuildVal() const { static const auto rt = id::find_type("EventMetadata::Entry"); auto rv = make_intrusive(rt); const auto* desc = event_registry->LookupMetadata(id); if ( ! desc ) { zeek::reporter->InternalWarning("unable to find metadata descriptor for id %" PRIu64, id); return rv; } rv->Assign(0, desc->IdVal()); rv->Assign(1, val); return rv; } Event::Event(const EventHandlerPtr& arg_handler, zeek::Args arg_args, util::detail::SourceID arg_src, analyzer::ID arg_aid, Obj* arg_obj, double arg_ts) : handler(arg_handler), args(std::move(arg_args)), meta(detail::MakeEventMetadataVector(arg_ts)), src(arg_src), aid(arg_aid), obj(zeek::NewRef{}, arg_obj), next_event(nullptr) {} Event::Event(detail::EventMetadataVectorPtr arg_meta, const EventHandlerPtr& arg_handler, zeek::Args arg_args, util::detail::SourceID arg_src, analyzer::ID arg_aid, Obj* arg_obj) : handler(arg_handler), args(std::move(arg_args)), meta(std::move(arg_meta)), src(arg_src), aid(arg_aid), obj(zeek::NewRef{}, arg_obj), next_event(nullptr) {} zeek::VectorValPtr Event::MetadataValues(const EnumValPtr& id) const { static const auto& any_vec_t = zeek::id::find_type("any_vec"); auto result = zeek::make_intrusive(any_vec_t); if ( ! meta ) return result; auto id_int = id->Get(); if ( id_int < 0 ) zeek::reporter->InternalError("Negative enum value %s: %" PRId64, obj_desc_short(id.get()).c_str(), id_int); zeek_uint_t uintid = static_cast(id_int); const auto* desc = event_registry->LookupMetadata(uintid); if ( ! desc ) return result; for ( const auto& entry : *meta ) { if ( entry.Id() != uintid ) continue; // Sanity check the type. if ( ! same_type(desc->Type(), entry.Val()->GetType()) ) { zeek::reporter->InternalWarning("metadata has unexpected type %s, wanted %s", obj_desc_short(entry.Val()->GetType().get()).c_str(), obj_desc_short(desc->Type().get()).c_str()); continue; } result->Append(entry.Val()); } return result; } double Event::Time() const { if ( ! meta ) return detail::NO_TIMESTAMP; for ( const auto& m : *meta ) if ( m.Id() == static_cast(detail::MetadataType::NetworkTimestamp) ) { if ( m.Val()->GetType()->Tag() != TYPE_TIME ) { // This should've been caught during parsing. zeek::reporter->InternalError("event metadata timestamp has wrong type: %s", obj_desc_short(m.Val()->GetType().get()).c_str()); } return m.Val()->AsTime(); } return detail::NO_TIMESTAMP; } void Event::Describe(ODesc* d) const { if ( d->IsReadable() ) d->AddSP("event"); bool s = d->IsShort(); d->SetShort(s); if ( ! d->IsBinary() ) d->Add("("); describe_vals(args, d); if ( ! d->IsBinary() ) d->Add("("); } void Event::Dispatch(bool no_remote) { if ( src == util::detail::SOURCE_BROKER ) no_remote = true; if ( handler->ErrorHandler() ) reporter->BeginErrorHandler(); try { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // Replace in v8.1 with handler->Call(&args). handler->Call(&args, no_remote, Time()); #pragma GCC diagnostic pop } catch ( InterpreterException& e ) { // Already reported. } // Unref obj obj.reset(); if ( handler->ErrorHandler() ) reporter->EndErrorHandler(); } EventMgr::~EventMgr() { while ( head ) { Event* n = head->NextEvent(); Unref(head); head = n; } } void EventMgr::Enqueue(const EventHandlerPtr& h, Args vl, util::detail::SourceID src, analyzer::ID aid, Obj* obj, DeprecatedTimestamp deprecated_ts) { detail::EventMetadataVectorPtr meta; double ts = double(deprecated_ts); // If this is a local event and EventMetadata::add_network_timestamp is // enabled, automatically set the network timestamp for this event to the // current network time when it is < 0 (default of deprecated_ts is -1.0). // // See the other Enqueue() implementation for the local vs broker/remote // motivation of want_network_timestamp. bool want_network_timestamp = BifConst::EventMetadata::add_network_timestamp && ((src == util::detail::SOURCE_LOCAL) || (src == util::detail::SOURCE_BROKER && BifConst::EventMetadata::add_missing_remote_network_timestamp)); if ( want_network_timestamp ) { if ( ts < 0.0 ) ts = run_state::network_time; // In v8.1 when the deprecated_ts parameters is gone: Just use run_state::network_time directly here. meta = detail::MakeEventMetadataVector(ts); } else if ( ts >= 0.0 ) { // EventMetadata::add_network_timestamp is false, but EventMgr::Enqueue() // with an explicit (non-negative) timestamp is used. That's a deprecated // API, but we continue to support it until v8.1. meta = detail::MakeEventMetadataVector(ts); } QueueEvent(new Event(std::move(meta), h, std::move(vl), src, aid, obj)); } void EventMgr::Enqueue(detail::EventMetadataVectorPtr meta, const EventHandlerPtr& h, Args vl, util::detail::SourceID src, analyzer::ID aid, Obj* obj) { // Attach network timestamps to all events if EventMetadata::add_network_timestamp is T and // // 1) this event is locally generated // or // 2) this is a remote event and EventMetadata::add_missing_remote_network_timestamp is T // // Why so complicated? It seems less surprising behavior to keep network timestamp metadata unset // if a remote event didn't have any attached. It should help to more easily figure out what's // actually going on compared to setting it to the local network time. If all nodes are required to // send *their* network timestamp, filling it with this node's network time seems more confusing // and error prone compared to just leaving it unset and having the consumer deal with the situation. bool want_network_timestamp = BifConst::EventMetadata::add_network_timestamp && ((src == util::detail::SOURCE_LOCAL) || (src == util::detail::SOURCE_BROKER && BifConst::EventMetadata::add_missing_remote_network_timestamp)); if ( want_network_timestamp ) { bool has_time = false; if ( ! meta ) { // No metadata vector at all, make one with a timestamp. meta = detail::MakeEventMetadataVector(run_state::network_time); } else { // Check all entries for a network timestamp for ( const auto& m : *meta ) { if ( m.Id() == static_cast(detail::MetadataType::NetworkTimestamp) ) { has_time = true; if ( m.Val()->GetType()->Tag() != TYPE_TIME ) { // This should've been caught during parsing. zeek::reporter->InternalError("event metadata timestamp has wrong type: %s", obj_desc_short(m.Val()->GetType().get()).c_str()); } } } if ( ! has_time ) { auto tv = zeek::make_intrusive(run_state::network_time); meta->push_back({static_cast(detail::MetadataType::NetworkTimestamp), std::move(tv)}); } } } QueueEvent(new Event(std::move(meta), h, std::move(vl), src, aid, obj)); } void EventMgr::QueueEvent(Event* event) { bool done = PLUGIN_HOOK_WITH_RESULT(HOOK_QUEUE_EVENT, HookQueueEvent(event), false); if ( done ) return; if ( ! head ) { head = tail = event; } else { tail->SetNext(event); tail = event; } ++event_mgr.num_events_queued; } void EventMgr::Dispatch(Event* event, bool no_remote) { Event* old_current = current; current = event; event->Dispatch(no_remote); current = old_current; Unref(event); } void EventMgr::Dispatch(const EventHandlerPtr& h, zeek::Args vl) { detail::EventMetadataVectorPtr meta; // If all events should have network timestamps, create the vector holding one. if ( BifConst::EventMetadata::add_network_timestamp ) meta = detail::MakeEventMetadataVector(run_state::network_time); auto* ev = new Event(std::move(meta), h, std::move(vl), util::detail::SOURCE_LOCAL, 0, nullptr); // Technically this isn't queued, but still give plugins a chance to // intercept the event and cancel or modify it if really wanted. bool done = PLUGIN_HOOK_WITH_RESULT(HOOK_QUEUE_EVENT, HookQueueEvent(ev), false); if ( done ) return; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // TODO: Open-code the old Dispatch() implementation here in v8.1. Dispatch(ev); #pragma GCC diagnostic pop } void EventMgr::Drain() { if ( event_queue_flush_point ) Enqueue(event_queue_flush_point, Args{}); PLUGIN_HOOK_VOID(HOOK_DRAIN_EVENTS, HookDrainEvents()); // Past Zeek versions drained as long as there events, including when // a handler queued new events during its execution. This could lead // to endless loops in case a handler kept triggering its own event. // We now limit this to just a couple of rounds. We do more than // just one round to make it less likely to break existing scripts // that expect the old behavior to trigger something quickly. for ( int round = 0; head && round < 2; round++ ) { Event* event = head; head = nullptr; tail = nullptr; while ( event ) { Event* next = event->NextEvent(); current = event; event->Dispatch(); Unref(event); ++event_mgr.num_events_dispatched; event = next; } } // Note: we might eventually need a general way to specify things to // do after draining events. current = nullptr; // Make sure all of the triggers get processed every time the events // drain. detail::trigger_mgr->Process(); } void EventMgr::Describe(ODesc* d) const { int n = 0; Event* e; for ( e = head; e; e = e->NextEvent() ) ++n; d->AddCount(n); for ( e = head; e; e = e->NextEvent() ) { e->Describe(d); d->NL(); } } void EventMgr::Process() { // While it semes like the most logical thing to do, we dont want // to call Drain() as part of this method. It will get called at // the end of run_loop after all of the sources have been processed // and had the opportunity to spawn new events. } void EventMgr::InitPostScript() { // Check if expected types and identifiers are available. const auto& et = zeek::id::find_type("EventMetadata::ID"); if ( ! et ) zeek::reporter->FatalError("Failed to find EventMetadata::ID"); const auto& net_ts_val = et->GetEnumVal(et->Lookup("EventMetadata::NETWORK_TIMESTAMP")); if ( ! net_ts_val ) zeek::reporter->FatalError("Failed to lookup EventMetadata::NETWORK_TIMESTAMP"); if ( ! zeek::event_registry->RegisterMetadata(net_ts_val, zeek::base_type(zeek::TYPE_TIME)) ) zeek::reporter->FatalError("Failed to register NETWORK_TIMESTAMP metadata"); // Remove this if there's ever a use-case to not use them together. if ( BifConst::EventMetadata::add_missing_remote_network_timestamp && ! BifConst::EventMetadata::add_network_timestamp ) zeek::reporter->FatalError( "Setting EventMetadata::add_missing_remote_network_timestamp is only valid together with " "EventMetadata::add_network_timestamp"); iosource_mgr->Register(this, true, false); } } // namespace zeek