mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00

This also installs symlinks from "zeek" and "bro-config" to a wrapper script that prints a deprecation warning. The btests pass, but this is still WIP. broctl renaming is still missing. #239
1084 lines
25 KiB
C++
1084 lines
25 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
#include "zeek-config.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "Net.h"
|
|
#include "NetVar.h"
|
|
#include "Conn.h"
|
|
#include "Event.h"
|
|
#include "Sessions.h"
|
|
#include "Reporter.h"
|
|
#include "Timer.h"
|
|
#include "analyzer/protocol/pia/PIA.h"
|
|
#include "binpac.h"
|
|
#include "TunnelEncapsulation.h"
|
|
#include "analyzer/Analyzer.h"
|
|
#include "analyzer/Manager.h"
|
|
|
|
void ConnectionTimer::Init(Connection* arg_conn, timer_func arg_timer,
|
|
int arg_do_expire)
|
|
{
|
|
conn = arg_conn;
|
|
timer = arg_timer;
|
|
do_expire = arg_do_expire;
|
|
Ref(conn);
|
|
}
|
|
|
|
ConnectionTimer::~ConnectionTimer()
|
|
{
|
|
if ( conn->RefCnt() < 1 )
|
|
reporter->InternalError("reference count inconsistency in ~ConnectionTimer");
|
|
|
|
conn->RemoveTimer(this);
|
|
Unref(conn);
|
|
}
|
|
|
|
void ConnectionTimer::Dispatch(double t, int 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 )
|
|
reporter->InternalError("reference count inconsistency in ConnectionTimer::Dispatch");
|
|
}
|
|
|
|
IMPLEMENT_SERIAL(ConnectionTimer, SER_CONNECTION_TIMER);
|
|
|
|
bool ConnectionTimer::DoSerialize(SerialInfo* info) const
|
|
{
|
|
DO_SERIALIZE(SER_CONNECTION_TIMER, Timer);
|
|
|
|
// We enumerate all the possible timer functions here ... This
|
|
// has to match the list is DoUnserialize()!
|
|
char type = 0;
|
|
|
|
if ( timer == timer_func(&Connection::DeleteTimer) )
|
|
type = 1;
|
|
else if ( timer == timer_func(&Connection::InactivityTimer) )
|
|
type = 2;
|
|
else if ( timer == timer_func(&Connection::StatusUpdateTimer) )
|
|
type = 3;
|
|
else if ( timer == timer_func(&Connection::RemoveConnectionTimer) )
|
|
type = 4;
|
|
else
|
|
reporter->InternalError("unknown function in ConnectionTimer::DoSerialize()");
|
|
|
|
return conn->Serialize(info) && SERIALIZE(type) && SERIALIZE(do_expire);
|
|
}
|
|
|
|
bool ConnectionTimer::DoUnserialize(UnserialInfo* info)
|
|
{
|
|
DO_UNSERIALIZE(Timer);
|
|
|
|
conn = Connection::Unserialize(info);
|
|
if ( ! conn )
|
|
return false;
|
|
|
|
char type;
|
|
|
|
if ( ! UNSERIALIZE(&type) || ! UNSERIALIZE(&do_expire) )
|
|
return false;
|
|
|
|
switch ( type ) {
|
|
case 1:
|
|
timer = timer_func(&Connection::DeleteTimer);
|
|
break;
|
|
case 2:
|
|
timer = timer_func(&Connection::InactivityTimer);
|
|
break;
|
|
case 3:
|
|
timer = timer_func(&Connection::StatusUpdateTimer);
|
|
break;
|
|
case 4:
|
|
timer = timer_func(&Connection::RemoveConnectionTimer);
|
|
break;
|
|
default:
|
|
info->s->Error("unknown connection timer function");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint64 Connection::total_connections = 0;
|
|
uint64 Connection::current_connections = 0;
|
|
uint64 Connection::external_connections = 0;
|
|
|
|
IMPLEMENT_SERIAL(Connection, SER_CONNECTION);
|
|
|
|
Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id,
|
|
uint32 flow, const Packet* pkt,
|
|
const EncapsulationStack* arg_encap)
|
|
{
|
|
sessions = s;
|
|
key = k;
|
|
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;
|
|
|
|
if ( pkt->l2_src )
|
|
memcpy(orig_l2_addr, pkt->l2_src, sizeof(orig_l2_addr));
|
|
else
|
|
bzero(orig_l2_addr, sizeof(orig_l2_addr));
|
|
|
|
if ( pkt->l2_dst )
|
|
memcpy(resp_l2_addr, pkt->l2_dst, sizeof(resp_l2_addr));
|
|
else
|
|
bzero(resp_l2_addr, sizeof(resp_l2_addr));
|
|
|
|
vlan = pkt->vlan;
|
|
inner_vlan = pkt->inner_vlan;
|
|
|
|
conn_val = 0;
|
|
login_conn = 0;
|
|
|
|
is_active = 1;
|
|
skip = 0;
|
|
weird = 0;
|
|
persistent = 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 = 0;
|
|
primary_PIA = 0;
|
|
|
|
++current_connections;
|
|
++total_connections;
|
|
|
|
TimerMgr::Tag* tag = current_iosrc->GetCurrentTag();
|
|
conn_timer_mgr = tag ? new TimerMgr::Tag(*tag) : 0;
|
|
|
|
if ( arg_encap )
|
|
encapsulation = new EncapsulationStack(*arg_encap);
|
|
else
|
|
encapsulation = 0;
|
|
|
|
if ( conn_timer_mgr )
|
|
{
|
|
++external_connections;
|
|
// We schedule a timer which removes this connection from memory
|
|
// indefinitively into the future. Ii will expire when the timer
|
|
// mgr is drained but not before.
|
|
ADD_TIMER(&Connection::RemoveConnectionTimer, 1e20, 1,
|
|
TIMER_REMOVE_CONNECTION);
|
|
}
|
|
}
|
|
|
|
Connection::~Connection()
|
|
{
|
|
if ( ! finished )
|
|
reporter->InternalError("Done() not called before destruction of Connection");
|
|
|
|
CancelTimers();
|
|
|
|
if ( conn_val )
|
|
{
|
|
conn_val->SetOrigin(0);
|
|
Unref(conn_val);
|
|
}
|
|
|
|
delete key;
|
|
delete root_analyzer;
|
|
delete conn_timer_mgr;
|
|
delete encapsulation;
|
|
|
|
--current_connections;
|
|
if ( conn_timer_mgr )
|
|
--external_connections;
|
|
}
|
|
|
|
void Connection::CheckEncapsulation(const EncapsulationStack* arg_encap)
|
|
{
|
|
if ( encapsulation && arg_encap )
|
|
{
|
|
if ( *encapsulation != *arg_encap )
|
|
{
|
|
Event(tunnel_changed, 0, arg_encap->GetVectorVal());
|
|
delete encapsulation;
|
|
encapsulation = new EncapsulationStack(*arg_encap);
|
|
}
|
|
}
|
|
|
|
else if ( encapsulation )
|
|
{
|
|
EncapsulationStack empty;
|
|
Event(tunnel_changed, 0, empty.GetVectorVal());
|
|
delete encapsulation;
|
|
encapsulation = 0;
|
|
}
|
|
|
|
else if ( arg_encap )
|
|
{
|
|
Event(tunnel_changed, 0, arg_encap->GetVectorVal());
|
|
encapsulation = new EncapsulationStack(*arg_encap);
|
|
}
|
|
}
|
|
|
|
void Connection::Done()
|
|
{
|
|
finished = 1;
|
|
|
|
if ( root_analyzer && ! root_analyzer->IsFinished() )
|
|
root_analyzer->Done();
|
|
}
|
|
|
|
void Connection::NextPacket(double t, int is_orig,
|
|
const IP_Hdr* ip, int len, int caplen,
|
|
const u_char*& data,
|
|
int& record_packet, int& record_content,
|
|
// arguments for reproducing packets
|
|
const Packet *pkt)
|
|
{
|
|
current_timestamp = t;
|
|
current_pkt = pkt;
|
|
|
|
if ( Skipping() )
|
|
return;
|
|
|
|
if ( root_analyzer )
|
|
{
|
|
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;
|
|
}
|
|
else
|
|
last_time = t;
|
|
|
|
current_timestamp = 0;
|
|
current_pkt = 0;
|
|
}
|
|
|
|
void Connection::SetLifetime(double lifetime)
|
|
{
|
|
ADD_TIMER(&Connection::DeleteTimer, network_time + lifetime, 0,
|
|
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& counter,
|
|
uint32& scaling_threshold,
|
|
uint32 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(EventHandlerPtr e, bool is_orig,
|
|
uint32 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;
|
|
|
|
ConnectionEventFast(e, 0, {
|
|
BuildConnVal(),
|
|
val_mgr->GetBool(is_orig),
|
|
val_mgr->GetCount(threshold)
|
|
});
|
|
}
|
|
|
|
void Connection::DeleteTimer(double /* t */)
|
|
{
|
|
if ( is_active )
|
|
Event(connection_timeout, 0);
|
|
|
|
sessions->Remove(this);
|
|
}
|
|
|
|
void Connection::InactivityTimer(double t)
|
|
{
|
|
// If the inactivity_timeout is zero, there has been an active
|
|
// timeout once, but it's disabled now. We do nothing then.
|
|
if ( inactivity_timeout )
|
|
{
|
|
if ( last_time + inactivity_timeout <= t )
|
|
{
|
|
Event(connection_timeout, 0);
|
|
sessions->Remove(this);
|
|
++killed_by_inactivity;
|
|
}
|
|
else
|
|
ADD_TIMER(&Connection::InactivityTimer,
|
|
last_time + inactivity_timeout, 0,
|
|
TIMER_CONN_INACTIVITY);
|
|
}
|
|
}
|
|
|
|
void Connection::RemoveConnectionTimer(double t)
|
|
{
|
|
Event(connection_state_remove, 0);
|
|
sessions->Remove(this);
|
|
}
|
|
|
|
void Connection::SetInactivityTimeout(double timeout)
|
|
{
|
|
// We add a new inactivity timer even if there already is one. When
|
|
// it fires, we always use the current value to check for inactivity.
|
|
if ( timeout )
|
|
ADD_TIMER(&Connection::InactivityTimer,
|
|
last_time + timeout, 0, TIMER_CONN_INACTIVITY);
|
|
|
|
inactivity_timeout = timeout;
|
|
}
|
|
|
|
void Connection::EnableStatusUpdateTimer()
|
|
{
|
|
if ( connection_status_update && connection_status_update_interval )
|
|
{
|
|
ADD_TIMER(&Connection::StatusUpdateTimer,
|
|
network_time + connection_status_update_interval, 0,
|
|
TIMER_CONN_STATUS_UPDATE);
|
|
installed_status_timer = 1;
|
|
}
|
|
}
|
|
|
|
void Connection::StatusUpdateTimer(double t)
|
|
{
|
|
ConnectionEventFast(connection_status_update, 0, { BuildConnVal() });
|
|
ADD_TIMER(&Connection::StatusUpdateTimer,
|
|
network_time + connection_status_update_interval, 0,
|
|
TIMER_CONN_STATUS_UPDATE);
|
|
}
|
|
|
|
RecordVal* Connection::BuildConnVal()
|
|
{
|
|
if ( ! conn_val )
|
|
{
|
|
conn_val = new RecordVal(connection_type);
|
|
|
|
TransportProto prot_type = ConnTransport();
|
|
|
|
RecordVal* id_val = new RecordVal(conn_id);
|
|
id_val->Assign(0, new AddrVal(orig_addr));
|
|
id_val->Assign(1, val_mgr->GetPort(ntohs(orig_port), prot_type));
|
|
id_val->Assign(2, new AddrVal(resp_addr));
|
|
id_val->Assign(3, val_mgr->GetPort(ntohs(resp_port), prot_type));
|
|
|
|
RecordVal* orig_endp = new RecordVal(endpoint);
|
|
orig_endp->Assign(0, val_mgr->GetCount(0));
|
|
orig_endp->Assign(1, val_mgr->GetCount(0));
|
|
orig_endp->Assign(4, val_mgr->GetCount(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, new StringVal(fmt_mac(orig_l2_addr, l2_len)));
|
|
|
|
RecordVal* resp_endp = new RecordVal(endpoint);
|
|
resp_endp->Assign(0, val_mgr->GetCount(0));
|
|
resp_endp->Assign(1, val_mgr->GetCount(0));
|
|
resp_endp->Assign(4, val_mgr->GetCount(resp_flow_label));
|
|
|
|
if ( memcmp(&resp_l2_addr, &null, l2_len) != 0 )
|
|
resp_endp->Assign(5, new StringVal(fmt_mac(resp_l2_addr, l2_len)));
|
|
|
|
conn_val->Assign(0, id_val);
|
|
conn_val->Assign(1, orig_endp);
|
|
conn_val->Assign(2, resp_endp);
|
|
// 3 and 4 are set below.
|
|
conn_val->Assign(5, new TableVal(string_set)); // service
|
|
conn_val->Assign(6, val_mgr->GetEmptyString()); // history
|
|
|
|
if ( ! uid )
|
|
uid.Set(bits_per_uid);
|
|
|
|
conn_val->Assign(7, new StringVal(uid.Base62("C").c_str()));
|
|
|
|
if ( encapsulation && encapsulation->Depth() > 0 )
|
|
conn_val->Assign(8, encapsulation->GetVectorVal());
|
|
|
|
if ( vlan != 0 )
|
|
conn_val->Assign(9, val_mgr->GetInt(vlan));
|
|
|
|
if ( inner_vlan != 0 )
|
|
conn_val->Assign(10, val_mgr->GetInt(inner_vlan));
|
|
|
|
}
|
|
|
|
if ( root_analyzer )
|
|
root_analyzer->UpdateConnVal(conn_val);
|
|
|
|
conn_val->Assign(3, new Val(start_time, TYPE_TIME)); // ###
|
|
conn_val->Assign(4, new Val(last_time - start_time, TYPE_INTERVAL));
|
|
conn_val->Assign(6, new StringVal(history.c_str()));
|
|
|
|
conn_val->SetOrigin(this);
|
|
|
|
Ref(conn_val);
|
|
|
|
return conn_val;
|
|
}
|
|
|
|
analyzer::Analyzer* Connection::FindAnalyzer(analyzer::ID id)
|
|
{
|
|
return root_analyzer ? root_analyzer->FindChild(id) : 0;
|
|
}
|
|
|
|
analyzer::Analyzer* Connection::FindAnalyzer(analyzer::Tag tag)
|
|
{
|
|
return root_analyzer ? root_analyzer->FindChild(tag) : 0;
|
|
}
|
|
|
|
analyzer::Analyzer* Connection::FindAnalyzer(const char* name)
|
|
{
|
|
return root_analyzer->FindChild(name);
|
|
}
|
|
|
|
void Connection::AppendAddl(const char* str)
|
|
{
|
|
Unref(BuildConnVal());
|
|
|
|
const char* old = conn_val->Lookup(6)->AsString()->CheckString();
|
|
const char* format = *old ? "%s %s" : "%s%s";
|
|
|
|
conn_val->Assign(6, new StringVal(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(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);
|
|
}
|
|
|
|
Val* Connection::BuildVersionVal(const char* s, int len)
|
|
{
|
|
Val* name = 0;
|
|
Val* major = 0;
|
|
Val* minor = 0;
|
|
Val* minor2 = 0;
|
|
Val* addl = 0;
|
|
|
|
const char* last = s + len;
|
|
const char* e = s;
|
|
|
|
// This is all just a guess...
|
|
|
|
// Eat non-alpha-numerical chars.
|
|
for ( ; s < last && ! isalnum(*s); ++s )
|
|
;
|
|
|
|
// Leading characters are the program name.
|
|
// (first character must not be a digit)
|
|
if ( isalpha(*s) )
|
|
{
|
|
for ( e = s; e < last && ! is_version_sep(e, last); ++e )
|
|
;
|
|
|
|
if ( s != e )
|
|
name = new StringVal(e - s, s);
|
|
}
|
|
|
|
// Find first number - that's the major version.
|
|
for ( s = e; s < last && ! isdigit(*s); ++s )
|
|
;
|
|
for ( e = s; e < last && isdigit(*e); ++e )
|
|
;
|
|
|
|
if ( s != e )
|
|
major = val_mgr->GetInt(atoi(s));
|
|
|
|
// Find second number seperated only by punctuation chars -
|
|
// that's the minor version.
|
|
for ( s = e; s < last && ispunct(*s); ++s )
|
|
;
|
|
for ( e = s; e < last && isdigit(*e); ++e )
|
|
;
|
|
|
|
if ( s != e )
|
|
minor = val_mgr->GetInt(atoi(s));
|
|
|
|
// Find second number seperated only by punctuation chars; -
|
|
// that's the minor version.
|
|
for ( s = e; s < last && ispunct(*s); ++s )
|
|
;
|
|
for ( e = s; e < last && isdigit(*e); ++e )
|
|
;
|
|
|
|
if ( s != e )
|
|
minor2 = val_mgr->GetInt(atoi(s));
|
|
|
|
// Anything after following punctuation and until next white space is
|
|
// an additional version string.
|
|
for ( s = e; s < last && ispunct(*s); ++s )
|
|
;
|
|
for ( e = s; e < last && ! isspace(*e); ++e )
|
|
;
|
|
|
|
if ( s != e )
|
|
addl = new StringVal(e - s, s);
|
|
|
|
// If we do not have a name yet, the next alphanumerical string is it.
|
|
if ( ! name )
|
|
{ // eat non-alpha-numerical characters
|
|
for ( s = e; s < last && ! isalpha(*s); ++s )
|
|
;
|
|
|
|
// Get name.
|
|
for ( e = s; e < last && (isalnum(*e) || *e == '_'); ++e )
|
|
;
|
|
|
|
if ( s != e )
|
|
name = new StringVal(e - s, s);
|
|
}
|
|
|
|
// We need at least a name.
|
|
if ( ! name )
|
|
{
|
|
Unref(major);
|
|
Unref(minor);
|
|
Unref(minor2);
|
|
Unref(addl);
|
|
return 0;
|
|
}
|
|
|
|
RecordVal* version = new RecordVal(software_version);
|
|
version->Assign(0, major ? major : val_mgr->GetInt(-1));
|
|
version->Assign(1, minor ? minor : val_mgr->GetInt(-1));
|
|
version->Assign(2, minor2 ? minor2 : val_mgr->GetInt(-1));
|
|
version->Assign(3, addl ? addl : val_mgr->GetEmptyString());
|
|
|
|
RecordVal* sw = new RecordVal(software);
|
|
sw->Assign(0, name);
|
|
sw->Assign(1, version);
|
|
|
|
return sw;
|
|
}
|
|
|
|
int Connection::VersionFoundEvent(const IPAddr& addr, const char* s, int len,
|
|
analyzer::Analyzer* analyzer)
|
|
{
|
|
if ( ! software_version_found && ! software_parse_error )
|
|
return 1;
|
|
|
|
if ( ! is_printable(s, len) )
|
|
return 0;
|
|
|
|
Val* val = BuildVersionVal(s, len);
|
|
if ( ! val )
|
|
{
|
|
if ( software_parse_error )
|
|
{
|
|
ConnectionEventFast(software_parse_error, analyzer, {
|
|
BuildConnVal(),
|
|
new AddrVal(addr),
|
|
new StringVal(len, s),
|
|
});
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if ( software_version_found )
|
|
{
|
|
ConnectionEventFast(software_version_found, 0, {
|
|
BuildConnVal(),
|
|
new AddrVal(addr),
|
|
val,
|
|
new StringVal(len, s),
|
|
});
|
|
}
|
|
else
|
|
Unref(val);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int Connection::UnparsedVersionFoundEvent(const IPAddr& addr,
|
|
const char* full, int len, analyzer::Analyzer* analyzer)
|
|
{
|
|
// Skip leading white space.
|
|
while ( len && isspace(*full) )
|
|
{
|
|
--len;
|
|
++full;
|
|
}
|
|
|
|
if ( ! is_printable(full, len) )
|
|
return 0;
|
|
|
|
if ( software_unparsed_version_found )
|
|
{
|
|
ConnectionEventFast(software_unparsed_version_found, analyzer, {
|
|
BuildConnVal(),
|
|
new AddrVal(addr),
|
|
new StringVal(len, full),
|
|
});
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void Connection::Event(EventHandlerPtr f, analyzer::Analyzer* analyzer, const char* name)
|
|
{
|
|
if ( ! f )
|
|
return;
|
|
|
|
if ( name )
|
|
ConnectionEventFast(f, analyzer, {new StringVal(name), BuildConnVal()});
|
|
else
|
|
ConnectionEventFast(f, analyzer, {BuildConnVal()});
|
|
|
|
}
|
|
|
|
void Connection::Event(EventHandlerPtr f, analyzer::Analyzer* analyzer, Val* v1, Val* v2)
|
|
{
|
|
if ( ! f )
|
|
{
|
|
Unref(v1);
|
|
Unref(v2);
|
|
return;
|
|
}
|
|
|
|
if ( v2 )
|
|
ConnectionEventFast(f, analyzer, {BuildConnVal(), v1, v2});
|
|
else
|
|
ConnectionEventFast(f, analyzer, {BuildConnVal(), v1});
|
|
}
|
|
|
|
void Connection::ConnectionEvent(EventHandlerPtr f, analyzer::Analyzer* a, val_list vl)
|
|
{
|
|
if ( ! f )
|
|
{
|
|
// This may actually happen if there is no local handler
|
|
// and a previously existing remote handler went away.
|
|
loop_over_list(vl, i)
|
|
Unref(vl[i]);
|
|
|
|
return;
|
|
}
|
|
|
|
// "this" is passed as a cookie for the event
|
|
mgr.QueueEvent(f, std::move(vl), SOURCE_LOCAL,
|
|
a ? a->GetID() : 0, GetTimerMgr(), this);
|
|
}
|
|
|
|
void Connection::ConnectionEventFast(EventHandlerPtr f, analyzer::Analyzer* a, val_list vl)
|
|
{
|
|
// "this" is passed as a cookie for the event
|
|
mgr.QueueEventFast(f, std::move(vl), SOURCE_LOCAL,
|
|
a ? a->GetID() : 0, GetTimerMgr(), this);
|
|
}
|
|
|
|
void Connection::ConnectionEvent(EventHandlerPtr f, analyzer::Analyzer* a, val_list* vl)
|
|
{
|
|
ConnectionEvent(f, a, std::move(*vl));
|
|
delete vl;
|
|
}
|
|
|
|
void Connection::Weird(const char* name, const char* addl)
|
|
{
|
|
weird = 1;
|
|
reporter->Weird(this, name, addl ? addl : "");
|
|
}
|
|
|
|
void Connection::AddTimer(timer_func timer, double t, int do_expire,
|
|
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 )
|
|
return;
|
|
|
|
Timer* conn_timer = new ConnectionTimer(this, timer, t, do_expire, type);
|
|
GetTimerMgr()->Add(conn_timer);
|
|
timers.append(conn_timer);
|
|
}
|
|
|
|
void Connection::RemoveTimer(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());
|
|
loop_over_list(timers, j)
|
|
tmp.append(timers[j]);
|
|
|
|
loop_over_list(tmp, i)
|
|
GetTimerMgr()->Cancel(tmp[i]);
|
|
|
|
timers_canceled = 1;
|
|
timers.clear();
|
|
}
|
|
|
|
TimerMgr* Connection::GetTimerMgr() const
|
|
{
|
|
if ( ! conn_timer_mgr )
|
|
// Global manager.
|
|
return timer_mgr;
|
|
|
|
// We need to check whether the local timer manager still exists;
|
|
// it may have already been timed out, in which case we fall back
|
|
// to the global manager (though this should be rare).
|
|
TimerMgr* local_mgr = sessions->LookupTimerMgr(conn_timer_mgr, false);
|
|
return local_mgr ? local_mgr : timer_mgr;
|
|
}
|
|
|
|
void Connection::FlipRoles()
|
|
{
|
|
IPAddr tmp_addr = resp_addr;
|
|
resp_addr = orig_addr;
|
|
orig_addr = tmp_addr;
|
|
|
|
uint32 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 tmp_flow = resp_flow_label;
|
|
resp_flow_label = orig_flow_label;
|
|
orig_flow_label = tmp_flow;
|
|
|
|
Unref(conn_val);
|
|
conn_val = 0;
|
|
|
|
if ( root_analyzer )
|
|
root_analyzer->FlipRoles();
|
|
|
|
analyzer_mgr->ApplyScheduledAnalyzers(this);
|
|
|
|
AddHistory('^');
|
|
}
|
|
|
|
unsigned int Connection::MemoryAllocation() const
|
|
{
|
|
return padded_sizeof(*this)
|
|
+ (key ? key->MemoryAllocation() : 0)
|
|
+ (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(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");
|
|
reporter->InternalWarning(
|
|
"unknown transport in Connction::Describe()");
|
|
|
|
break;
|
|
|
|
default:
|
|
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(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));
|
|
}
|
|
|
|
bool Connection::Serialize(SerialInfo* info) const
|
|
{
|
|
return SerialObj::Serialize(info);
|
|
}
|
|
|
|
Connection* Connection::Unserialize(UnserialInfo* info)
|
|
{
|
|
return (Connection*) SerialObj::Unserialize(info, SER_CONNECTION);
|
|
}
|
|
|
|
bool Connection::DoSerialize(SerialInfo* info) const
|
|
{
|
|
DO_SERIALIZE(SER_CONNECTION, BroObj);
|
|
|
|
// First we write the members which are needed to
|
|
// create the HashKey.
|
|
if ( ! SERIALIZE(orig_addr) || ! SERIALIZE(resp_addr) )
|
|
return false;
|
|
|
|
if ( ! SERIALIZE(orig_port) || ! SERIALIZE(resp_port) )
|
|
return false;
|
|
|
|
if ( ! SERIALIZE(timers.length()) )
|
|
return false;
|
|
|
|
loop_over_list(timers, i)
|
|
if ( ! timers[i]->Serialize(info) )
|
|
return false;
|
|
|
|
SERIALIZE_OPTIONAL(conn_val);
|
|
|
|
// FIXME: RuleEndpointState not yet serializable.
|
|
// FIXME: Analyzers not yet serializable.
|
|
|
|
return
|
|
SERIALIZE(int(proto)) &&
|
|
SERIALIZE(history) &&
|
|
SERIALIZE(hist_seen) &&
|
|
SERIALIZE(start_time) &&
|
|
SERIALIZE(last_time) &&
|
|
SERIALIZE(inactivity_timeout) &&
|
|
SERIALIZE(suppress_event) &&
|
|
SERIALIZE(login_conn != 0) &&
|
|
SERIALIZE_BIT(installed_status_timer) &&
|
|
SERIALIZE_BIT(timers_canceled) &&
|
|
SERIALIZE_BIT(is_active) &&
|
|
SERIALIZE_BIT(skip) &&
|
|
SERIALIZE_BIT(weird) &&
|
|
SERIALIZE_BIT(finished) &&
|
|
SERIALIZE_BIT(record_packets) &&
|
|
SERIALIZE_BIT(record_contents) &&
|
|
SERIALIZE_BIT(persistent);
|
|
}
|
|
|
|
bool Connection::DoUnserialize(UnserialInfo* info)
|
|
{
|
|
// Make sure this is initialized for the condition in Unserialize().
|
|
persistent = 0;
|
|
|
|
DO_UNSERIALIZE(BroObj);
|
|
|
|
// Build the hash key first. Some of the recursive *::Unserialize()
|
|
// functions may need it.
|
|
ConnID id;
|
|
|
|
if ( ! UNSERIALIZE(&orig_addr) || ! UNSERIALIZE(&resp_addr) )
|
|
goto error;
|
|
|
|
if ( ! UNSERIALIZE(&orig_port) || ! UNSERIALIZE(&resp_port) )
|
|
goto error;
|
|
|
|
id.src_addr = orig_addr;
|
|
id.dst_addr = resp_addr;
|
|
// This doesn't work for ICMP. But I guess this is not really important.
|
|
id.src_port = orig_port;
|
|
id.dst_port = resp_port;
|
|
id.is_one_way = 0; // ### incorrect for ICMP
|
|
key = BuildConnIDHashKey(id);
|
|
|
|
int len;
|
|
if ( ! UNSERIALIZE(&len) )
|
|
goto error;
|
|
|
|
while ( len-- )
|
|
{
|
|
Timer* t = Timer::Unserialize(info);
|
|
if ( ! t )
|
|
goto error;
|
|
timers.append(t);
|
|
}
|
|
|
|
UNSERIALIZE_OPTIONAL(conn_val,
|
|
(RecordVal*) Val::Unserialize(info, connection_type));
|
|
|
|
int iproto;
|
|
|
|
if ( ! (UNSERIALIZE(&iproto) &&
|
|
UNSERIALIZE(&history) &&
|
|
UNSERIALIZE(&hist_seen) &&
|
|
UNSERIALIZE(&start_time) &&
|
|
UNSERIALIZE(&last_time) &&
|
|
UNSERIALIZE(&inactivity_timeout) &&
|
|
UNSERIALIZE(&suppress_event)) )
|
|
goto error;
|
|
|
|
proto = static_cast<TransportProto>(iproto);
|
|
|
|
bool has_login_conn;
|
|
if ( ! UNSERIALIZE(&has_login_conn) )
|
|
goto error;
|
|
|
|
login_conn = has_login_conn ? (LoginConn*) this : 0;
|
|
|
|
UNSERIALIZE_BIT(installed_status_timer);
|
|
UNSERIALIZE_BIT(timers_canceled);
|
|
UNSERIALIZE_BIT(is_active);
|
|
UNSERIALIZE_BIT(skip);
|
|
UNSERIALIZE_BIT(weird);
|
|
UNSERIALIZE_BIT(finished);
|
|
UNSERIALIZE_BIT(record_packets);
|
|
UNSERIALIZE_BIT(record_contents);
|
|
UNSERIALIZE_BIT(persistent);
|
|
|
|
// Hmm... Why does each connection store a sessions ptr?
|
|
sessions = ::sessions;
|
|
|
|
root_analyzer = 0;
|
|
primary_PIA = 0;
|
|
conn_timer_mgr = 0;
|
|
|
|
return true;
|
|
|
|
error:
|
|
abort();
|
|
CancelTimers();
|
|
return false;
|
|
}
|
|
|
|
void Connection::SetRootAnalyzer(analyzer::TransportLayerAnalyzer* analyzer, analyzer::pia::PIA* pia)
|
|
{
|
|
root_analyzer = analyzer;
|
|
primary_PIA = pia;
|
|
}
|
|
|
|
void Connection::CheckFlowLabel(bool is_orig, uint32 flow_label)
|
|
{
|
|
uint32& my_flow_label = is_orig ? orig_flow_label : resp_flow_label;
|
|
|
|
if ( my_flow_label != flow_label )
|
|
{
|
|
if ( conn_val )
|
|
{
|
|
RecordVal *endp = conn_val->Lookup(is_orig ? 1 : 2)->AsRecordVal();
|
|
endp->Assign(4, val_mgr->GetCount(flow_label));
|
|
}
|
|
|
|
if ( connection_flow_label_changed &&
|
|
(is_orig ? saw_first_orig_packet : saw_first_resp_packet) )
|
|
{
|
|
ConnectionEventFast(connection_flow_label_changed, 0, {
|
|
BuildConnVal(),
|
|
val_mgr->GetBool(is_orig),
|
|
val_mgr->GetCount(my_flow_label),
|
|
val_mgr->GetCount(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 threshold, uint64 rate,
|
|
double duration)
|
|
{
|
|
return ::PermitWeird(weird_state, name, threshold, rate, duration);
|
|
}
|