// See the file "COPYING" in the main distribution directory for copyright. #include "zeek-config.h" #include "Conn.h" #include #include "Desc.h" #include "Net.h" #include "NetVar.h" #include "Event.h" #include "Sessions.h" #include "Reporter.h" #include "Timer.h" #include "iosource/IOSource.h" #include "analyzer/protocol/pia/PIA.h" #include "binpac.h" #include "TunnelEncapsulation.h" #include "analyzer/Analyzer.h" #include "analyzer/Manager.h" #include "iosource/IOSource.h" namespace zeek { namespace detail { void ConnectionTimer::Init(Connection* arg_conn, timer_func arg_timer, bool arg_do_expire) { conn = arg_conn; timer = arg_timer; do_expire = arg_do_expire; Ref(conn); } ConnectionTimer::~ConnectionTimer() { if ( conn->RefCnt() < 1 ) zeek::reporter->InternalError("reference count inconsistency in ~ConnectionTimer"); conn->RemoveTimer(this); Unref(conn); } void ConnectionTimer::Dispatch(double t, bool is_expire) { if ( is_expire && ! do_expire ) return; // Remove ourselves from the connection's set of timers so // it doesn't try to cancel us. conn->RemoveTimer(this); (conn->*timer)(t); if ( conn->RefCnt() < 1 ) zeek::reporter->InternalError("reference count inconsistency in ConnectionTimer::Dispatch"); } } // namespace detail uint64_t Connection::total_connections = 0; uint64_t Connection::current_connections = 0; Connection::Connection(zeek::NetSessions* s, const zeek::detail::ConnIDKey& k, double t, const ConnID* id, uint32_t flow, const zeek::Packet* pkt, const zeek::EncapsulationStack* arg_encap) { sessions = s; key = k; key_valid = true; start_time = last_time = t; orig_addr = id->src_addr; resp_addr = id->dst_addr; orig_port = id->src_port; resp_port = id->dst_port; proto = TRANSPORT_UNKNOWN; orig_flow_label = flow; resp_flow_label = 0; saw_first_orig_packet = 1; saw_first_resp_packet = 0; is_successful = false; if ( pkt->l2_src ) memcpy(orig_l2_addr, pkt->l2_src, sizeof(orig_l2_addr)); else memset(orig_l2_addr, 0, sizeof(orig_l2_addr)); if ( pkt->l2_dst ) memcpy(resp_l2_addr, pkt->l2_dst, sizeof(resp_l2_addr)); else memset(resp_l2_addr, 0, sizeof(resp_l2_addr)); vlan = pkt->vlan; inner_vlan = pkt->inner_vlan; login_conn = nullptr; is_active = 1; skip = 0; weird = 0; suppress_event = 0; record_contents = record_packets = 1; record_current_packet = record_current_content = 0; timers_canceled = 0; inactivity_timeout = 0; installed_status_timer = 0; finished = 0; hist_seen = 0; history = ""; root_analyzer = nullptr; primary_PIA = nullptr; ++current_connections; ++total_connections; if ( arg_encap ) encapsulation = new zeek::EncapsulationStack(*arg_encap); else encapsulation = nullptr; } Connection::~Connection() { if ( ! finished ) zeek::reporter->InternalError("Done() not called before destruction of Connection"); CancelTimers(); if ( conn_val ) conn_val->SetOrigin(nullptr); delete root_analyzer; delete encapsulation; --current_connections; } void Connection::CheckEncapsulation(const zeek::EncapsulationStack* arg_encap) { if ( encapsulation && arg_encap ) { if ( *encapsulation != *arg_encap ) { if ( tunnel_changed ) EnqueueEvent(tunnel_changed, nullptr, ConnVal(), arg_encap->ToVal()); delete encapsulation; encapsulation = new zeek::EncapsulationStack(*arg_encap); } } else if ( encapsulation ) { if ( tunnel_changed ) { zeek::EncapsulationStack empty; EnqueueEvent(tunnel_changed, nullptr, ConnVal(), empty.ToVal()); } delete encapsulation; encapsulation = nullptr; } else if ( arg_encap ) { if ( tunnel_changed ) EnqueueEvent(tunnel_changed, nullptr, ConnVal(), arg_encap->ToVal()); encapsulation = new zeek::EncapsulationStack(*arg_encap); } } void Connection::Done() { finished = 1; if ( root_analyzer && ! root_analyzer->IsFinished() ) root_analyzer->Done(); } void Connection::NextPacket(double t, bool is_orig, const zeek::IP_Hdr* ip, int len, int caplen, const u_char*& data, int& record_packet, int& record_content, // arguments for reproducing packets const zeek::Packet *pkt) { zeek::net::current_timestamp = t; zeek::net::current_pkt = pkt; if ( Skipping() ) return; if ( root_analyzer ) { auto was_successful = is_successful; record_current_packet = record_packet; record_current_content = record_content; root_analyzer->NextPacket(len, data, is_orig, -1, ip, caplen); record_packet = record_current_packet; record_content = record_current_content; if ( ConnTransport() != TRANSPORT_TCP ) is_successful = true; if ( ! was_successful && is_successful && connection_successful ) EnqueueEvent(connection_successful, nullptr, ConnVal()); } else last_time = t; zeek::net::current_timestamp = 0; zeek::net::current_pkt = nullptr; } void Connection::SetLifetime(double lifetime) { ADD_TIMER(&Connection::DeleteTimer, zeek::net::network_time + lifetime, 0, zeek::detail::TIMER_CONN_DELETE); } bool Connection::IsReuse(double t, const u_char* pkt) { return root_analyzer && root_analyzer->IsReuse(t, pkt); } bool Connection::ScaledHistoryEntry(char code, uint32_t& counter, uint32_t& scaling_threshold, uint32_t scaling_base) { if ( ++counter == scaling_threshold ) { AddHistory(code); auto new_threshold = scaling_threshold * scaling_base; if ( new_threshold <= scaling_threshold ) // This can happen due to wrap-around. In that // case, reset the counter but leave the threshold // unchanged. counter = 0; else scaling_threshold = new_threshold; return true; } return false; } void Connection::HistoryThresholdEvent(zeek::EventHandlerPtr e, bool is_orig, uint32_t threshold) { if ( ! e ) return; if ( threshold == 1 ) // This will be far and away the most common case, // and at this stage it's not a *multiple* instance. return; EnqueueEvent(e, nullptr, ConnVal(), zeek::val_mgr->Bool(is_orig), zeek::val_mgr->Count(threshold) ); } void Connection::DeleteTimer(double /* t */) { if ( is_active ) Event(connection_timeout, nullptr); sessions->Remove(this); } void Connection::InactivityTimer(double t) { if ( last_time + inactivity_timeout <= t ) { Event(connection_timeout, nullptr); sessions->Remove(this); ++zeek::detail::killed_by_inactivity; } else ADD_TIMER(&Connection::InactivityTimer, last_time + inactivity_timeout, 0, zeek::detail::TIMER_CONN_INACTIVITY); } void Connection::RemoveConnectionTimer(double t) { RemovalEvent(); sessions->Remove(this); } void Connection::SetInactivityTimeout(double timeout) { if ( timeout == inactivity_timeout ) return; // First cancel and remove any existing inactivity timer. for ( const auto& timer : timers ) if ( timer->Type() == zeek::detail::TIMER_CONN_INACTIVITY ) { zeek::detail::timer_mgr->Cancel(timer); break; } if ( timeout ) ADD_TIMER(&Connection::InactivityTimer, last_time + timeout, 0, zeek::detail::TIMER_CONN_INACTIVITY); inactivity_timeout = timeout; } void Connection::EnableStatusUpdateTimer() { if ( connection_status_update && connection_status_update_interval ) { ADD_TIMER(&Connection::StatusUpdateTimer, zeek::net::network_time + connection_status_update_interval, 0, zeek::detail::TIMER_CONN_STATUS_UPDATE); installed_status_timer = 1; } } void Connection::StatusUpdateTimer(double t) { EnqueueEvent(connection_status_update, nullptr, ConnVal()); ADD_TIMER(&Connection::StatusUpdateTimer, zeek::net::network_time + connection_status_update_interval, 0, zeek::detail::TIMER_CONN_STATUS_UPDATE); } zeek::RecordVal* Connection::BuildConnVal() { return ConnVal()->Ref()->AsRecordVal(); } const zeek::RecordValPtr& Connection::ConnVal() { if ( ! conn_val ) { conn_val = zeek::make_intrusive(zeek::id::connection); TransportProto prot_type = ConnTransport(); auto id_val = zeek::make_intrusive(zeek::id::conn_id); id_val->Assign(0, zeek::make_intrusive(orig_addr)); id_val->Assign(1, zeek::val_mgr->Port(ntohs(orig_port), prot_type)); id_val->Assign(2, zeek::make_intrusive(resp_addr)); id_val->Assign(3, zeek::val_mgr->Port(ntohs(resp_port), prot_type)); auto orig_endp = zeek::make_intrusive(zeek::id::endpoint); orig_endp->Assign(0, zeek::val_mgr->Count(0)); orig_endp->Assign(1, zeek::val_mgr->Count(0)); orig_endp->Assign(4, zeek::val_mgr->Count(orig_flow_label)); const int l2_len = sizeof(orig_l2_addr); char null[l2_len]{}; if ( memcmp(&orig_l2_addr, &null, l2_len) != 0 ) orig_endp->Assign(5, zeek::make_intrusive(fmt_mac(orig_l2_addr, l2_len))); auto resp_endp = zeek::make_intrusive(zeek::id::endpoint); resp_endp->Assign(0, zeek::val_mgr->Count(0)); resp_endp->Assign(1, zeek::val_mgr->Count(0)); resp_endp->Assign(4, zeek::val_mgr->Count(resp_flow_label)); if ( memcmp(&resp_l2_addr, &null, l2_len) != 0 ) resp_endp->Assign(5, zeek::make_intrusive(fmt_mac(resp_l2_addr, l2_len))); conn_val->Assign(0, std::move(id_val)); conn_val->Assign(1, std::move(orig_endp)); conn_val->Assign(2, std::move(resp_endp)); // 3 and 4 are set below. conn_val->Assign(5, zeek::make_intrusive(zeek::id::string_set)); // service conn_val->Assign(6, zeek::val_mgr->EmptyString()); // history if ( ! uid ) uid.Set(bits_per_uid); conn_val->Assign(7, zeek::make_intrusive(uid.Base62("C").c_str())); if ( encapsulation && encapsulation->Depth() > 0 ) conn_val->Assign(8, encapsulation->ToVal()); if ( vlan != 0 ) conn_val->Assign(9, zeek::val_mgr->Int(vlan)); if ( inner_vlan != 0 ) conn_val->Assign(10, zeek::val_mgr->Int(inner_vlan)); } if ( root_analyzer ) root_analyzer->UpdateConnVal(conn_val.get()); conn_val->Assign(3, zeek::make_intrusive(start_time)); // ### conn_val->Assign(4, zeek::make_intrusive(last_time - start_time)); conn_val->Assign(6, zeek::make_intrusive(history.c_str())); conn_val->Assign(11, zeek::val_mgr->Bool(is_successful)); conn_val->SetOrigin(this); return conn_val; } zeek::analyzer::Analyzer* Connection::FindAnalyzer(zeek::analyzer::ID id) { return root_analyzer ? root_analyzer->FindChild(id) : nullptr; } zeek::analyzer::Analyzer* Connection::FindAnalyzer(const zeek::analyzer::Tag& tag) { return root_analyzer ? root_analyzer->FindChild(tag) : nullptr; } zeek::analyzer::Analyzer* Connection::FindAnalyzer(const char* name) { return root_analyzer->FindChild(name); } void Connection::AppendAddl(const char* str) { const auto& cv = ConnVal(); const char* old = cv->GetField(6)->AsString()->CheckString(); const char* format = *old ? "%s %s" : "%s%s"; cv->Assign(6, zeek::make_intrusive(zeek::util::fmt(format, old, str))); } // Returns true if the character at s separates a version number. static inline bool is_version_sep(const char* s, const char* end) { return // foo-1.2.3 (s < end - 1 && ispunct(s[0]) && isdigit(s[1])) || // foo-v1.2.3 (s < end - 2 && ispunct(s[0]) && tolower(s[1]) == 'v' && isdigit(s[2])) || // foo 1.2.3 isspace(s[0]); } void Connection::Match(zeek::detail::Rule::PatternType type, const u_char* data, int len, bool is_orig, bool bol, bool eol, bool clear_state) { if ( primary_PIA ) primary_PIA->Match(type, data, len, is_orig, bol, eol, clear_state); } void Connection::RemovalEvent() { if ( connection_state_remove ) EnqueueEvent(connection_state_remove, nullptr, ConnVal()); if ( is_successful && successful_connection_remove ) EnqueueEvent(successful_connection_remove, nullptr, ConnVal()); } void Connection::Event(zeek::EventHandlerPtr f, zeek::analyzer::Analyzer* analyzer, const char* name) { if ( ! f ) return; if ( name ) EnqueueEvent(f, analyzer, zeek::make_intrusive(name), ConnVal()); else EnqueueEvent(f, analyzer, ConnVal()); } void Connection::Event(zeek::EventHandlerPtr f, zeek::analyzer::Analyzer* analyzer, zeek::Val* v1, zeek::Val* v2) { if ( ! f ) { Unref(v1); Unref(v2); return; } if ( v2 ) EnqueueEvent(f, analyzer, ConnVal(), zeek::IntrusivePtr{zeek::AdoptRef{}, v1}, zeek::IntrusivePtr{zeek::AdoptRef{}, v2}); else EnqueueEvent(f, analyzer, ConnVal(), zeek::IntrusivePtr{zeek::AdoptRef{}, v1}); } void Connection::ConnectionEvent(zeek::EventHandlerPtr f, zeek::analyzer::Analyzer* a, val_list vl) { auto args = zeek::val_list_to_args(vl); if ( ! f ) // This may actually happen if there is no local handler // and a previously existing remote handler went away. return; // "this" is passed as a cookie for the event zeek::event_mgr.Enqueue(f, std::move(args), zeek::util::detail::SOURCE_LOCAL, a ? a->GetID() : 0, this); } void Connection::ConnectionEventFast(zeek::EventHandlerPtr f, zeek::analyzer::Analyzer* a, val_list vl) { // "this" is passed as a cookie for the event zeek::event_mgr.Enqueue(f, zeek::val_list_to_args(vl), zeek::util::detail::SOURCE_LOCAL, a ? a->GetID() : 0, this); } void Connection::ConnectionEvent(zeek::EventHandlerPtr f, zeek::analyzer::Analyzer* a, val_list* vl) { auto args = zeek::val_list_to_args(*vl); delete vl; if ( f ) EnqueueEvent(f, a, std::move(args)); } void Connection::EnqueueEvent(zeek::EventHandlerPtr f, zeek::analyzer::Analyzer* a, zeek::Args args) { // "this" is passed as a cookie for the event zeek::event_mgr.Enqueue(f, std::move(args), zeek::util::detail::SOURCE_LOCAL, a ? a->GetID() : 0, this); } void Connection::Weird(const char* name, const char* addl) { weird = 1; zeek::reporter->Weird(this, name, addl ? addl : ""); } void Connection::AddTimer(timer_func timer, double t, bool do_expire, zeek::detail::TimerType type) { if ( timers_canceled ) return; // If the key is cleared, the connection isn't stored in the connection // table anymore and will soon be deleted. We're not installing new // timers anymore then. if ( ! key_valid ) return; zeek::detail::Timer* conn_timer = new detail::ConnectionTimer(this, timer, t, do_expire, type); zeek::detail::timer_mgr->Add(conn_timer); timers.push_back(conn_timer); } void Connection::RemoveTimer(zeek::detail::Timer* t) { timers.remove(t); } void Connection::CancelTimers() { // We are going to cancel our timers which, in turn, may cause them to // call RemoveTimer(), which would then modify the list we're just // traversing. Thus, we first make a copy of the list which we then // iterate through. timer_list tmp(timers.length()); std::copy(timers.begin(), timers.end(), std::back_inserter(tmp)); for ( const auto& timer : tmp ) zeek::detail::timer_mgr->Cancel(timer); timers_canceled = 1; timers.clear(); } void Connection::FlipRoles() { zeek::IPAddr tmp_addr = resp_addr; resp_addr = orig_addr; orig_addr = tmp_addr; uint32_t tmp_port = resp_port; resp_port = orig_port; orig_port = tmp_port; const int l2_len = sizeof(orig_l2_addr); u_char tmp_l2_addr[l2_len]; memcpy(tmp_l2_addr, resp_l2_addr, l2_len); memcpy(resp_l2_addr, orig_l2_addr, l2_len); memcpy(orig_l2_addr, tmp_l2_addr, l2_len); bool tmp_bool = saw_first_resp_packet; saw_first_resp_packet = saw_first_orig_packet; saw_first_orig_packet = tmp_bool; uint32_t tmp_flow = resp_flow_label; resp_flow_label = orig_flow_label; orig_flow_label = tmp_flow; conn_val = nullptr; if ( root_analyzer ) root_analyzer->FlipRoles(); zeek::analyzer_mgr->ApplyScheduledAnalyzers(this); AddHistory('^'); } unsigned int Connection::MemoryAllocation() const { return padded_sizeof(*this) + (timers.MemoryAllocation() - padded_sizeof(timers)) + (conn_val ? conn_val->MemoryAllocation() : 0) + (root_analyzer ? root_analyzer->MemoryAllocation(): 0) // login_conn is just a casted 'this'. // primary_PIA is already contained in the analyzer tree. ; } unsigned int Connection::MemoryAllocationConnVal() const { return conn_val ? conn_val->MemoryAllocation() : 0; } void Connection::Describe(zeek::ODesc* d) const { d->Add(start_time); d->Add("("); d->Add(last_time); d->AddSP(")"); switch ( proto ) { case TRANSPORT_TCP: d->Add("TCP"); break; case TRANSPORT_UDP: d->Add("UDP"); break; case TRANSPORT_ICMP: d->Add("ICMP"); break; case TRANSPORT_UNKNOWN: d->Add("unknown"); zeek::reporter->InternalWarning( "unknown transport in Connction::Describe()"); break; default: zeek::reporter->InternalError( "unhandled transport type in Connection::Describe"); } d->SP(); d->Add(orig_addr); d->Add(":"); d->Add(ntohs(orig_port)); d->SP(); d->AddSP("->"); d->Add(resp_addr); d->Add(":"); d->Add(ntohs(resp_port)); d->NL(); } void Connection::IDString(zeek::ODesc* d) const { d->Add(orig_addr); d->AddRaw(":", 1); d->Add(ntohs(orig_port)); d->AddRaw(" > ", 3); d->Add(resp_addr); d->AddRaw(":", 1); d->Add(ntohs(resp_port)); } void Connection::SetRootAnalyzer(zeek::analyzer::TransportLayerAnalyzer* analyzer, zeek::analyzer::pia::PIA* pia) { root_analyzer = analyzer; primary_PIA = pia; } void Connection::CheckFlowLabel(bool is_orig, uint32_t flow_label) { uint32_t& my_flow_label = is_orig ? orig_flow_label : resp_flow_label; if ( my_flow_label != flow_label ) { if ( conn_val ) { zeek::RecordVal* endp = conn_val->GetField(is_orig ? 1 : 2)->AsRecordVal(); endp->Assign(4, zeek::val_mgr->Count(flow_label)); } if ( connection_flow_label_changed && (is_orig ? saw_first_orig_packet : saw_first_resp_packet) ) { EnqueueEvent(connection_flow_label_changed, nullptr, ConnVal(), zeek::val_mgr->Bool(is_orig), zeek::val_mgr->Count(my_flow_label), zeek::val_mgr->Count(flow_label) ); } my_flow_label = flow_label; } if ( is_orig ) saw_first_orig_packet = 1; else saw_first_resp_packet = 1; } bool Connection::PermitWeird(const char* name, uint64_t threshold, uint64_t rate, double duration) { return zeek::detail::PermitWeird(weird_state, name, threshold, rate, duration); } } // namespace zeek