Merge branch 'master' into topic/jsiwek/supervisor

This commit is contained in:
Jon Siwek 2020-01-07 14:55:51 -08:00
commit 6046da9993
314 changed files with 3709 additions and 5389 deletions

@ -1 +1 @@
Subproject commit 0e1f951b0bcafea63e503957ae005220c24e4b20
Subproject commit 2b3206b7add3472ea0736f2841473e11d506a85e

View file

@ -1,5 +1,7 @@
#include <cstdio>
#include <cstring>
#include <sstream>
#include <fstream>
#include <utility>
#include <algorithm>
#include <sys/stat.h>
@ -13,6 +15,17 @@ Brofiler::Brofiler()
Brofiler::~Brofiler()
{
for ( auto& s : stmts )
Unref(s);
}
void Brofiler::AddStmt(Stmt* s)
{
if ( ignoring != 0 )
return;
::Ref(s);
stmts.push_back(s);
}
bool Brofiler::ReadStats()
@ -22,27 +35,46 @@ bool Brofiler::ReadStats()
if ( ! bf )
return false;
FILE* f = fopen(bf, "r");
if ( ! f )
std::ifstream ifs;
ifs.open(bf, std::ifstream::in);
if ( ! ifs )
return false;
char line[16384];
std::stringstream ss;
ss << ifs.rdbuf();
std::string file_contents = ss.str();
ss.clear();
std::vector<std::string> lines;
tokenize_string(file_contents, "\n", &lines);
string delimiter;
delimiter = delim;
while( fgets(line, sizeof(line), f) )
for ( const auto& line : lines )
{
line[strlen(line) - 1] = 0; //remove newline
string cnt(strtok(line, delimiter.c_str()));
string location(strtok(0, delimiter.c_str()));
string desc(strtok(0, delimiter.c_str()));
pair<string, string> location_desc(location, desc);
if ( line.empty() )
continue;
std::vector<std::string> line_components;
tokenize_string(line, delimiter, &line_components);
if ( line_components.size() != 3 )
{
fprintf(stderr, "invalid ZEEK_PROFILER_FILE line: %s\n", line.data());
continue;
}
std::string& cnt = line_components[0];
std::string& location = line_components[1];
std::string& desc = line_components[2];
pair<string, string> location_desc(std::move(location), std::move(desc));
uint64_t count;
atoi_n(cnt.size(), cnt.c_str(), 0, 10, count);
usage_map[location_desc] = count;
usage_map.emplace(std::move(location_desc), count);
}
fclose(f);
return true;
}
@ -88,7 +120,7 @@ bool Brofiler::WriteStats()
return false;
}
for ( list<const Stmt*>::const_iterator it = stmts.begin();
for ( list<Stmt*>::const_iterator it = stmts.begin();
it != stmts.end(); ++it )
{
ODesc location_info;
@ -96,7 +128,8 @@ bool Brofiler::WriteStats()
ODesc desc_info;
(*it)->Describe(&desc_info);
string desc(desc_info.Description());
for_each(desc.begin(), desc.end(), canonicalize_desc());
canonicalize_desc cd{delim};
for_each(desc.begin(), desc.end(), cd);
pair<string, string> location_desc(location_info.Description(), desc);
if ( usage_map.find(location_desc) != usage_map.end() )
usage_map[location_desc] += (*it)->GetAccessCount();

View file

@ -38,13 +38,13 @@ public:
void IncIgnoreDepth() { ignoring++; }
void DecIgnoreDepth() { ignoring--; }
void AddStmt(const Stmt* s) { if ( ignoring == 0 ) stmts.push_back(s); }
void AddStmt(Stmt* s);
private:
/**
* The current, global Brofiler instance creates this list at parse-time.
*/
list<const Stmt*> stmts;
list<Stmt*> stmts;
/**
* Indicates whether new statments will not be considered as part of
@ -70,9 +70,12 @@ private:
* that don't agree with the output format of Brofiler.
*/
struct canonicalize_desc {
char delim;
void operator() (char& c)
{
if ( c == '\n' ) c = ' ';
if ( c == delim ) c = ' ';
}
};
};

View file

@ -435,3 +435,25 @@ add_clang_tidy_files(${MAIN_SRCS})
# (*.pac.cc) files, and most of the generated code for BIFs (not including
# *.bif.register.cc)
create_clang_tidy_target()
########################################################################
## CTest setup.
# Scan all .cc files for TEST_CASE macros and generate CTest targets.
if (ENABLE_ZEEK_UNIT_TESTS)
file(GLOB_RECURSE all_cc_files "*.cc")
set(test_cases "")
foreach (cc_file ${all_cc_files})
file (STRINGS ${cc_file} test_case_lines REGEX "TEST_CASE")
foreach (line ${test_case_lines})
string(REGEX REPLACE "TEST_CASE\\(\"(.+)\"\\)" "\\1" test_case "${line}")
list(APPEND test_cases "${test_case}")
endforeach ()
endforeach ()
list(LENGTH test_cases num_test_cases)
MESSAGE(STATUS "-- Found ${num_test_cases} test cases for CTest")
foreach (test_case ${test_cases})
add_test(NAME "\"${test_case}\""
COMMAND zeek --test "--test-case=${test_case}")
endforeach ()
endif ()

View file

@ -146,6 +146,30 @@ char* CompositeHash::SingleValHash(int type_check, char* kp0,
break;
}
case TYPE_PATTERN:
{
const char* texts[2] = {
v->AsPattern()->PatternText(),
v->AsPattern()->AnywherePatternText()
};
size_t* kp;
for ( int i = 0; i < 2; i++ )
{
kp = AlignAndPadType<size_t>(kp0+i);
*kp = strlen(texts[i]) + 1;
}
kp1 = reinterpret_cast<char*>(kp+1);
for ( int i = 0; i < 2; i++ )
{
memcpy(kp1, texts[i], strlen(texts[i]) + 1);
kp1 += strlen(texts[i]) + 1;
}
break;
}
case TYPE_RECORD:
{
char* kp = kp0;
@ -401,6 +425,19 @@ HashKey* CompositeHash::ComputeSingletonHash(const Val* v, int type_check) const
if ( v->Type()->Tag() == TYPE_FUNC )
return new HashKey(v->AsFunc()->GetUniqueFuncID());
if ( v->Type()->Tag() == TYPE_PATTERN )
{
const char* texts[2] = {
v->AsPattern()->PatternText(),
v->AsPattern()->AnywherePatternText()
};
int n = strlen(texts[0]) + strlen(texts[1]) + 2; // 2 for null
char* key = new char[n];
std::memcpy(key, texts[0], strlen(texts[0]) + 1);
std::memcpy(key + strlen(texts[0]) + 1, texts[1], strlen(texts[1]) + 1);
return new HashKey(false, key, n);
}
reporter->InternalError("bad index type in CompositeHash::ComputeSingletonHash");
return 0;
@ -462,6 +499,17 @@ int CompositeHash::SingleTypeKeySize(BroType* bt, const Val* v,
break;
}
case TYPE_PATTERN:
{
if ( ! v )
return (optional && ! calc_static_size) ? sz : 0;
sz = SizeAlign(sz, 2 * sizeof(size_t));
sz += strlen(v->AsPattern()->PatternText())
+ strlen(v->AsPattern()->AnywherePatternText()) + 2; // 2 for null terminators
break;
}
case TYPE_RECORD:
{
const RecordVal* rv = v ? v->AsRecordVal() : 0;
@ -831,6 +879,28 @@ const char* CompositeHash::RecoverOneVal(const HashKey* k, const char* kp0,
}
break;
case TYPE_PATTERN:
{
RE_Matcher* re = nullptr;
if ( is_singleton )
{
kp1 = kp0;
int divider = strlen(kp0) + 1;
re = new RE_Matcher(kp1, kp1 + divider);
kp1 += k->Size();
}
else
{
const size_t* const len = AlignType<size_t>(kp0);
kp1 = reinterpret_cast<const char*>(len+2);
re = new RE_Matcher(kp1, kp1 + len[0]);
kp1 += len[0] + len[1];
}
pval = new PatternVal(re);
}
break;
case TYPE_RECORD:
{
const char* kp = kp0;

View file

@ -72,6 +72,7 @@ Connection::Connection(NetSessions* s, const ConnIDKey& k, double t, const ConnI
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));
@ -204,11 +205,18 @@ void Connection::NextPacket(double t, int is_orig,
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 )
ConnectionEventFast(connection_successful, nullptr, {BuildConnVal()});
}
else
last_time = t;
@ -300,7 +308,7 @@ void Connection::InactivityTimer(double t)
void Connection::RemoveConnectionTimer(double t)
{
Event(connection_state_remove, 0);
RemovalEvent();
sessions->Remove(this);
}
@ -396,6 +404,7 @@ RecordVal* Connection::BuildConnVal()
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->Assign(11, val_mgr->GetBool(is_successful));
conn_val->SetOrigin(this);
@ -448,6 +457,19 @@ void Connection::Match(Rule::PatternType type, const u_char* data, int len, bool
primary_PIA->Match(type, data, len, is_orig, bol, eol, clear_state);
}
void Connection::RemovalEvent()
{
auto cv = BuildConnVal();
if ( connection_state_remove )
ConnectionEventFast(connection_state_remove, nullptr, {cv->Ref()});
if ( is_successful && successful_connection_remove )
ConnectionEventFast(successful_connection_remove, nullptr, {cv->Ref()});
Unref(cv);
}
void Connection::Event(EventHandlerPtr f, analyzer::Analyzer* analyzer, const char* name)
{
if ( ! f )

View file

@ -114,6 +114,9 @@ public:
TransportProto ConnTransport() const { return proto; }
bool IsSuccessful() const { return is_successful; };
void SetSuccessful() { is_successful = true; }
// True if we should record subsequent packets (either headers or
// in their entirety, depending on record_contents). We still
// record subsequent SYN/FIN/RST, regardless of how this is set.
@ -162,6 +165,11 @@ public:
void Match(Rule::PatternType type, const u_char* data, int len,
bool is_orig, bool bol, bool eol, bool clear_state);
/**
* Generates connection removal event(s).
*/
void RemovalEvent();
// If a handler exists for 'f', an event will be generated. If 'name' is
// given that event's first argument will be it, and it's second will be
// the connection value. If 'name' is null, then the event's first
@ -339,6 +347,7 @@ protected:
unsigned int record_packets:1, record_contents:1;
unsigned int record_current_packet:1, record_current_content:1;
unsigned int saw_first_orig_packet:1, saw_first_resp_packet:1;
unsigned int is_successful:1;
// Count number of connections.
static uint64_t total_connections;

View file

@ -1404,7 +1404,7 @@ void DNS_Mgr::DoProcess()
{
AsyncRequest* req = asyncs_timeouts.top();
if ( req->time + DNS_TIMEOUT > current_time() )
if ( req->time + DNS_TIMEOUT > current_time() && ! terminating )
break;
if ( ! req->processed )

View file

@ -135,7 +135,7 @@ bool DbgBreakpoint::SetLocation(ParseLocationRec plr, string loc_str)
}
at_stmt = plr.stmt;
safe_snprintf(description, sizeof(description), "%s:%d",
snprintf(description, sizeof(description), "%s:%d",
source_filename, source_line);
debug_msg("Breakpoint %d set at %s\n", GetID(), Description());
@ -148,7 +148,7 @@ bool DbgBreakpoint::SetLocation(ParseLocationRec plr, string loc_str)
loc_str.c_str());
at_stmt = plr.stmt;
const Location* loc = at_stmt->GetLocationInfo();
safe_snprintf(description, sizeof(description), "%s at %s:%d",
snprintf(description, sizeof(description), "%s at %s:%d",
function_name.c_str(), loc->filename, loc->last_line);
debug_msg("Breakpoint %d set at %s\n", GetID(), Description());
@ -171,7 +171,7 @@ bool DbgBreakpoint::SetLocation(Stmt* stmt)
AddToGlobalMap();
const Location* loc = stmt->GetLocationInfo();
safe_snprintf(description, sizeof(description), "%s:%d",
snprintf(description, sizeof(description), "%s:%d",
loc->filename, loc->last_line);
debug_msg("Breakpoint %d set at %s\n", GetID(), Description());

View file

@ -717,7 +717,7 @@ static char* get_prompt(bool reset_counter = false)
if ( reset_counter )
counter = 0;
safe_snprintf(prompt, sizeof(prompt), "(Zeek [%d]) ", counter++);
snprintf(prompt, sizeof(prompt), "(Zeek [%d]) ", counter++);
return prompt;
}
@ -743,7 +743,7 @@ string get_context_description(const Stmt* stmt, const Frame* frame)
size_t buf_size = strlen(d.Description()) + strlen(loc.filename) + 1024;
char* buf = new char[buf_size];
safe_snprintf(buf, buf_size, "In %s at %s:%d",
snprintf(buf, buf_size, "In %s at %s:%d",
d.Description(), loc.filename, loc.last_line);
string retval(buf);

View file

@ -4339,6 +4339,7 @@ LambdaExpr::LambdaExpr(std::unique_ptr<function_ingredients> arg_ing,
// Install a dummy version of the function globally for use only
// when broker provides a closure.
::Ref(ingredients->body);
BroFunc* dummy_func = new BroFunc(
ingredients->id,
ingredients->body,
@ -4378,6 +4379,7 @@ LambdaExpr::LambdaExpr(std::unique_ptr<function_ingredients> arg_ing,
dummy_func->SetName(my_name.c_str());
Val* v = new Val(dummy_func);
Unref(dummy_func);
id->SetVal(v); // id will unref v when its done.
id->SetType(ingredients->id->Type()->Ref());
id->SetConst();
@ -4385,6 +4387,7 @@ LambdaExpr::LambdaExpr(std::unique_ptr<function_ingredients> arg_ing,
Val* LambdaExpr::Eval(Frame* f) const
{
::Ref(ingredients->body);
BroFunc* lamb = new BroFunc(
ingredients->id,
ingredients->body,
@ -4398,7 +4401,9 @@ Val* LambdaExpr::Eval(Frame* f) const
// Allows for lookups by the receiver.
lamb->SetName(my_name.c_str());
return new Val(lamb);
auto rval = new Val(lamb);
Unref(lamb);
return rval;
}
void LambdaExpr::ExprDescribe(ODesc* d) const

View file

@ -66,6 +66,9 @@ public:
bool IsRawOutput() const { return raw_output; }
protected:
friend class PrintStmt;
BroFile() { Init(); }
void Init();
@ -80,7 +83,8 @@ protected:
// Returns nil if the file is not active, was in error, etc.
// (Protected because we do not want anyone to write directly
// to the file.)
// to the file, but the PrintStmt friend uses this to check whether
// it's really stdout.)
FILE* File();
// Raises a file_opened event.

View file

@ -31,20 +31,54 @@ Frame::Frame(int arg_size, const BroFunc* func, const val_list* fn_args)
Frame::~Frame()
{
for ( auto& func : functions_with_closure_frame_reference )
{
func->StrengthenClosureReference(this);
Unref(func);
}
// Deleting a Frame that is a view is a no-op.
Unref(trigger);
Unref(closure);
if ( ! weak_closure_ref )
Unref(closure);
for ( auto& i : outer_ids )
Unref(i);
Release();
delete [] weak_refs;
}
void Frame::SetElement(int n, Val* v)
void Frame::AddFunctionWithClosureRef(BroFunc* func)
{
Unref(frame[n]);
::Ref(func);
functions_with_closure_frame_reference.emplace_back(func);
}
void Frame::SetElement(int n, Val* v, bool weak_ref)
{
UnrefElement(n);
frame[n] = v;
if ( weak_ref )
{
if ( ! weak_refs )
{
weak_refs = new bool[size];
for ( auto i = 0; i < size; ++i )
weak_refs[i] = false;
}
weak_refs[n] = true;
}
else
{
if ( weak_refs )
weak_refs[n] = false;
}
}
void Frame::SetElement(const ID* id, Val* v)
@ -62,8 +96,15 @@ void Frame::SetElement(const ID* id, Val* v)
if ( offset_map.size() )
{
auto where = offset_map.find(std::string(id->Name()));
if ( where != offset_map.end() )
SetElement(where->second, v);
{
// Need to add a Ref to 'v' since the SetElement() for
// id->Offset() below is otherwise responsible for keeping track
// of the implied reference count of the passed-in 'v' argument.
// i.e. if we end up storing it twice, we need an addition Ref.
SetElement(where->second, v->Ref());
}
}
SetElement(id->Offset(), v);
@ -92,7 +133,7 @@ void Frame::Reset(int startIdx)
{
for ( int i = startIdx; i < size; ++i )
{
Unref(frame[i]);
UnrefElement(i);
frame[i] = 0;
}
}
@ -100,7 +141,7 @@ void Frame::Reset(int startIdx)
void Frame::Release()
{
for ( int i = 0; i < size; ++i )
Unref(frame[i]);
UnrefElement(i);
delete [] frame;
}
@ -145,7 +186,34 @@ Frame* Frame::Clone() const
return other;
}
Frame* Frame::SelectiveClone(const id_list& selection) const
static bool val_is_func(Val* v, BroFunc* func)
{
if ( v->Type()->Tag() != TYPE_FUNC )
return false;
return v->AsFunc() == func;
}
static Val* clone_if_not_func(Val** frame, int offset, BroFunc* func,
Frame* other)
{
auto v = frame[offset];
if ( ! v )
return nullptr;
if ( val_is_func(v, func) )
{
other->SetElement(offset, v, true);
return v;
}
auto rval = v->Clone();
other->SetElement(offset, rval);
return rval;
}
Frame* Frame::SelectiveClone(const id_list& selection, BroFunc* func) const
{
if ( selection.length() == 0 )
return nullptr;
@ -171,7 +239,7 @@ Frame* Frame::SelectiveClone(const id_list& selection) const
auto where = offset_map.find(std::string(id->Name()));
if ( where != offset_map.end() )
{
other->frame[where->second] = frame[where->second]->Clone();
clone_if_not_func(frame, where->second, func, other);
continue;
}
}
@ -179,7 +247,7 @@ Frame* Frame::SelectiveClone(const id_list& selection) const
if ( ! frame[id->Offset()] )
reporter->InternalError("Attempted to clone an id ('%s') with no associated value.", id->Name());
other->frame[id->Offset()] = frame[id->Offset()]->Clone();
clone_if_not_func(frame, id->Offset(), func, other);
}
/**
@ -379,6 +447,7 @@ std::pair<bool, Frame*> Frame::Unserialize(const broker::vector& data)
// Frame takes ownership of unref'ing elements in outer_ids
rf->outer_ids = std::move(outer_ids);
rf->closure = closure;
rf->weak_closure_ref = false;
for ( int i = 0; i < frame_size; ++i )
{
@ -437,7 +506,7 @@ void Frame::CaptureClosure(Frame* c, id_list arg_outer_ids)
closure = c;
if ( closure )
Ref(closure);
weak_closure_ref = true;
/**
* Want to capture closures by copy?

View file

@ -44,8 +44,11 @@ public:
*
* @param n the index to set
* @param v the value to set it to
* @param weak_ref whether the frame owns the value and should unref
* it upon destruction. Used to break circular references between
* lambda functions and closure frames.
*/
void SetElement(int n, Val* v);
void SetElement(int n, Val* v, bool weak_ref = false);
/**
* Associates *id* and *v* in the frame. Future lookups of
@ -149,7 +152,7 @@ public:
* *selection* have been cloned. All other values are made to be
* null.
*/
Frame* SelectiveClone(const id_list& selection) const;
Frame* SelectiveClone(const id_list& selection, BroFunc* func) const;
/**
* Serializes the Frame into a Broker representation.
@ -215,8 +218,28 @@ public:
void SetDelayed() { delayed = true; }
bool HasDelayed() const { return delayed; }
/**
* Track a new function that refers to this frame for use as a closure.
* This frame's destructor will then upgrade that functions reference
* from weak to strong (by making a copy). The initial use of
* weak references prevents unbreakable circular references that
* otherwise cause memory leaks.
*/
void AddFunctionWithClosureRef(BroFunc* func);
private:
/**
* Unrefs the value at offset 'n' frame unless it's a weak reference.
*/
void UnrefElement(int n)
{
if ( weak_refs && weak_refs[n] )
return;
Unref(frame[n]);
}
/** Have we captured this id? */
bool IsOuterID(const ID* in) const;
@ -242,8 +265,13 @@ private:
/** Associates ID's offsets with values. */
Val** frame;
/** Values that are weakly referenced by the frame. Used to
* prevent circular reference memory leaks in lambda/closures */
bool* weak_refs = nullptr;
/** The enclosing frame of this frame. */
Frame* closure;
bool weak_closure_ref = false;
/** ID's used in this frame from the enclosing frame. */
id_list outer_ids;
@ -268,6 +296,8 @@ private:
Trigger* trigger;
const CallExpr* call;
bool delayed;
std::vector<BroFunc*> functions_with_closure_frame_reference;
};
/**

View file

@ -132,6 +132,7 @@ Func* Func::DoClone()
{
// By default, ok just to return a reference. Func does not have any state
// that is different across instances.
::Ref(this);
return this;
}
@ -286,7 +287,9 @@ BroFunc::~BroFunc()
{
std::for_each(bodies.begin(), bodies.end(),
[](Body& b) { Unref(b.stmts); });
Unref(closure);
if ( ! weak_closure_ref )
Unref(closure);
}
int BroFunc::IsPure() const
@ -490,14 +493,35 @@ void BroFunc::AddClosure(id_list ids, Frame* f)
SetClosureFrame(f);
}
bool BroFunc::StrengthenClosureReference(Frame* f)
{
if ( closure != f )
return false;
if ( ! weak_closure_ref )
return false;
closure = closure->SelectiveClone(outer_ids, this);
weak_closure_ref = false;
return true;
}
void BroFunc::SetClosureFrame(Frame* f)
{
if ( closure )
reporter->InternalError("Tried to override closure for BroFunc %s.",
Name());
// Have to use weak references initially because otherwise Ref'ing the
// original frame creates a circular reference: the function holds a
// reference to the frame and the frame contains a reference to this
// function value. And we can't just do a shallow clone of the frame
// up front because the closure semantics in Zeek allow mutating
// the outer frame.
closure = f;
Ref(closure);
weak_closure_ref = true;
f->AddFunctionWithClosureRef(this);
}
bool BroFunc::UpdateClosure(const broker::vector& data)
@ -510,9 +534,10 @@ bool BroFunc::UpdateClosure(const broker::vector& data)
if ( new_closure )
new_closure->SetFunction(this);
if ( closure )
if ( ! weak_closure_ref )
Unref(closure);
weak_closure_ref = false;
closure = new_closure;
return true;
@ -528,7 +553,8 @@ Func* BroFunc::DoClone()
CopyStateInto(other);
other->frame_size = frame_size;
other->closure = closure ? closure->SelectiveClone(outer_ids) : nullptr;
other->closure = closure ? closure->SelectiveClone(outer_ids, this) : nullptr;
other->weak_closure_ref = false;
other->outer_ids = outer_ids;
return other;
@ -814,3 +840,68 @@ bool check_built_in_call(BuiltinFunc* f, CallExpr* call)
return true;
}
// Gets a function's priority from its Scope's attributes. Errors if it sees any
// problems.
static int get_func_priority(const attr_list& attrs)
{
int priority = 0;
for ( const auto& a : attrs )
{
if ( a->Tag() == ATTR_DEPRECATED )
continue;
if ( a->Tag() != ATTR_PRIORITY )
{
a->Error("illegal attribute for function body");
continue;
}
Val* v = a->AttrExpr()->Eval(0);
if ( ! v )
{
a->Error("cannot evaluate attribute expression");
continue;
}
if ( ! IsIntegral(v->Type()->Tag()) )
{
a->Error("expression is not of integral type");
continue;
}
priority = v->InternalInt();
}
return priority;
}
function_ingredients::function_ingredients(Scope* scope, Stmt* body)
{
frame_size = scope->Length();
inits = scope->GetInits();
this->scope = scope;
::Ref(this->scope);
id = scope->ScopeID();
::Ref(id);
auto attrs = scope->Attrs();
priority = (attrs ? get_func_priority(*attrs) : 0);
this->body = body;
::Ref(this->body);
}
function_ingredients::~function_ingredients()
{
Unref(id);
Unref(body);
Unref(scope);
for ( const auto& i : *inits )
Unref(i);
delete inits;
}

View file

@ -114,6 +114,12 @@ public:
*/
bool UpdateClosure(const broker::vector& data);
/**
* If the function's closure is a weak reference to the given frame,
* upgrade to a strong reference of a shallow clone of that frame.
*/
bool StrengthenClosureReference(Frame* f);
/**
* Serializes this function's closure.
*
@ -154,6 +160,7 @@ private:
id_list outer_ids;
// The frame the BroFunc was initialized in.
Frame* closure = nullptr;
bool weak_closure_ref = false;
};
typedef Val* (*built_in_func)(Frame* frame, val_list* args);
@ -192,13 +199,20 @@ struct CallInfo {
// Struct that collects all the specifics defining a Func. Used for BroFuncs
// with closures.
struct function_ingredients {
// Gathers all of the information from a scope and a function body needed
// to build a function.
function_ingredients(Scope* scope, Stmt* body);
~function_ingredients();
ID* id;
Stmt* body;
id_list* inits;
int frame_size;
int priority;
Scope* scope;
};
};
extern vector<CallInfo> call_stack;

View file

@ -65,6 +65,17 @@ void notifier::Registry::Modified(Modifiable* m)
i->second->Modified(m);
}
void notifier::Registry::Terminate()
{
std::set<Receiver*> receivers;
for ( auto& r : registrations )
receivers.emplace(r.second);
for ( auto& r : receivers )
r->Terminate();
}
notifier::Modifiable::~Modifiable()
{
if ( num_receivers )

View file

@ -30,6 +30,12 @@ public:
* @param m object that was modified
*/
virtual void Modified(Modifiable* m) = 0;
/**
* Callback executed when notification registry is terminating and
* no further modifications can possibly occur.
*/
virtual void Terminate() { }
};
/** Singleton class tracking all notification requests globally. */
@ -69,6 +75,12 @@ public:
*/
void Unregister(Modifiable* m);
/**
* Notifies all receivers that no further modifications will occur
* as the registry is shutting down.
*/
void Terminate();
private:
friend class Modifiable;

View file

@ -1,11 +1,25 @@
#include "PacketFilter.h"
void PacketFilter::DeleteFilter(void* data)
{
auto f = static_cast<Filter*>(data);
delete f;
}
PacketFilter::PacketFilter(bool arg_default)
{
default_match = arg_default;
src_filter.SetDeleteFunction(PacketFilter::DeleteFilter);
dst_filter.SetDeleteFunction(PacketFilter::DeleteFilter);
}
void PacketFilter::AddSrc(const IPAddr& src, uint32_t tcp_flags, double probability)
{
Filter* f = new Filter;
f->tcp_flags = tcp_flags;
f->probability = uint32_t(probability * RAND_MAX);
src_filter.Insert(src, 128, f);
auto prev = static_cast<Filter*>(src_filter.Insert(src, 128, f));
delete prev;
}
void PacketFilter::AddSrc(Val* src, uint32_t tcp_flags, double probability)
@ -13,7 +27,8 @@ void PacketFilter::AddSrc(Val* src, uint32_t tcp_flags, double probability)
Filter* f = new Filter;
f->tcp_flags = tcp_flags;
f->probability = uint32_t(probability * RAND_MAX);
src_filter.Insert(src, f);
auto prev = static_cast<Filter*>(src_filter.Insert(src, f));
delete prev;
}
void PacketFilter::AddDst(const IPAddr& dst, uint32_t tcp_flags, double probability)
@ -21,7 +36,8 @@ void PacketFilter::AddDst(const IPAddr& dst, uint32_t tcp_flags, double probabil
Filter* f = new Filter;
f->tcp_flags = tcp_flags;
f->probability = uint32_t(probability * RAND_MAX);
dst_filter.Insert(dst, 128, f);
auto prev = static_cast<Filter*>(dst_filter.Insert(dst, 128, f));
delete prev;
}
void PacketFilter::AddDst(Val* dst, uint32_t tcp_flags, double probability)
@ -29,27 +45,36 @@ void PacketFilter::AddDst(Val* dst, uint32_t tcp_flags, double probability)
Filter* f = new Filter;
f->tcp_flags = tcp_flags;
f->probability = uint32_t(probability * RAND_MAX);
dst_filter.Insert(dst, f);
auto prev = static_cast<Filter*>(dst_filter.Insert(dst, f));
delete prev;
}
bool PacketFilter::RemoveSrc(const IPAddr& src)
{
return src_filter.Remove(src, 128) != 0;
auto f = static_cast<Filter*>(src_filter.Remove(src, 128));
delete f;
return f != nullptr;
}
bool PacketFilter::RemoveSrc(Val* src)
{
return src_filter.Remove(src) != NULL;
auto f = static_cast<Filter*>(src_filter.Remove(src));
delete f;
return f != nullptr;
}
bool PacketFilter::RemoveDst(const IPAddr& dst)
{
return dst_filter.Remove(dst, 128) != NULL;
auto f = static_cast<Filter*>(dst_filter.Remove(dst, 128));
delete f;
return f != nullptr;
}
bool PacketFilter::RemoveDst(Val* dst)
{
return dst_filter.Remove(dst) != NULL;
auto f = static_cast<Filter*>(dst_filter.Remove(dst));
delete f;
return f != nullptr;
}
bool PacketFilter::Match(const IP_Hdr* ip, int len, int caplen)

View file

@ -7,7 +7,7 @@
class PacketFilter {
public:
explicit PacketFilter(bool arg_default) { default_match = arg_default; }
explicit PacketFilter(bool arg_default);
~PacketFilter() {}
// Drops all packets from a particular source (which may be given
@ -34,6 +34,8 @@ private:
uint32_t probability;
};
static void DeleteFilter(void* data);
bool MatchFilter(const Filter& f, const IP_Hdr& ip, int len, int caplen);
bool default_match;

View file

@ -18,8 +18,8 @@ private:
};
public:
PrefixTable() { tree = New_Patricia(128); }
~PrefixTable() { Destroy_Patricia(tree, 0); }
PrefixTable() { tree = New_Patricia(128); delete_function = nullptr; }
~PrefixTable() { Destroy_Patricia(tree, delete_function); }
// Addr in network byte order. If data is zero, acts like a set.
// Returns ptr to old data if already existing.
@ -43,7 +43,10 @@ public:
void* Remove(const IPAddr& addr, int width);
void* Remove(const Val* value);
void Clear() { Clear_Patricia(tree, 0); }
void Clear() { Clear_Patricia(tree, delete_function); }
// Sets a function to call for each node when table is cleared/destroyed.
void SetDeleteFunction(data_fn_t del_fn) { delete_function = del_fn; }
iterator InitIterator();
void* GetNext(iterator* i);
@ -53,4 +56,5 @@ private:
static IPPrefix PrefixToIPPrefix(prefix_t* p);
patricia_tree_t* tree;
data_fn_t delete_function;
};

View file

@ -109,7 +109,7 @@ void Specific_RE_Matcher::MakeCaseInsensitive()
char* s = new char[n + 5 /* slop */];
safe_snprintf(s, n + 5, fmt, pattern_text);
snprintf(s, n + 5, fmt, pattern_text);
delete [] pattern_text;
pattern_text = s;
@ -493,7 +493,7 @@ static RE_Matcher* matcher_merge(const RE_Matcher* re1, const RE_Matcher* re2,
int n = strlen(text1) + strlen(text2) + strlen(merge_op) + 32 /* slop */ ;
char* merge_text = new char[n];
safe_snprintf(merge_text, n, "(%s)%s(%s)", text1, merge_op, text2);
snprintf(merge_text, n, "(%s)%s(%s)", text1, merge_op, text2);
RE_Matcher* merge = new RE_Matcher(merge_text);
delete [] merge_text;

View file

@ -430,7 +430,7 @@ void Reporter::DoLog(const char* prefix, EventHandlerPtr event, FILE* out,
{
va_list aq;
va_copy(aq, ap);
int n = safe_vsnprintf(buffer, size, fmt, aq);
int n = vsnprintf(buffer, size, fmt, aq);
va_end(aq);
if ( postfix )
@ -451,7 +451,7 @@ void Reporter::DoLog(const char* prefix, EventHandlerPtr event, FILE* out,
if ( postfix && *postfix )
// Note, if you change this fmt string, adjust the additional
// buffer size above.
safe_snprintf(buffer + strlen(buffer), size - strlen(buffer), " (%s)", postfix);
snprintf(buffer + strlen(buffer), size - strlen(buffer), " (%s)", postfix);
bool raise_event = true;

View file

@ -3,6 +3,7 @@
#include "zeek-config.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
@ -1046,9 +1047,7 @@ void NetSessions::Remove(Connection* c)
}
c->Done();
if ( connection_state_remove )
c->Event(connection_state_remove, 0);
c->RemovalEvent();
// Zero out c's copy of the key, so that if c has been Ref()'d
// up, we know on a future call to Remove() that it's no
@ -1141,21 +1140,21 @@ void NetSessions::Drain()
{
Connection* tc = entry.second;
tc->Done();
tc->Event(connection_state_remove, 0);
tc->RemovalEvent();
}
for ( const auto& entry : udp_conns )
{
Connection* uc = entry.second;
uc->Done();
uc->Event(connection_state_remove, 0);
uc->RemovalEvent();
}
for ( const auto& entry : icmp_conns )
{
Connection* ic = entry.second;
ic->Done();
ic->Event(connection_state_remove, 0);
ic->RemovalEvent();
}
ExpireTimerMgrs();

View file

@ -14,6 +14,9 @@
#include "Debug.h"
#include "Traverse.h"
#include "Trigger.h"
#include "IntrusivePtr.h"
#include "logging/Manager.h"
#include "logging/logging.bif.h"
const char* stmt_name(BroStmtTag t)
{
@ -184,6 +187,40 @@ TraversalCode ExprListStmt::Traverse(TraversalCallback* cb) const
static BroFile* print_stdout = 0;
static IntrusivePtr<EnumVal> lookup_enum_val(const char* module_name, const char* name)
{
ID* id = lookup_ID(name, module_name);
assert(id);
assert(id->IsEnumConst());
EnumType* et = id->Type()->AsEnumType();
int index = et->Lookup(module_name, name);
assert(index >= 0);
IntrusivePtr<EnumVal> rval{et->GetVal(index), false};
return rval;
}
static Val* print_log(val_list* vals)
{
auto plval = lookup_enum_val("Log", "PRINTLOG");
auto record = make_intrusive<RecordVal>(internal_type("Log::PrintLogInfo")->AsRecordType());
auto vec = make_intrusive<VectorVal>(internal_type("string_vec")->AsVectorType());
for ( const auto& val : *vals )
{
ODesc d(DESC_READABLE);
val->Describe(&d);
vec->Assign(vec->Size(), new StringVal(d.Description()));
}
record->Assign(0, new Val(current_time(), TYPE_TIME));
record->Assign(1, vec.detach());
log_mgr->Write(plval.get(), record.get());
return nullptr;
}
Val* PrintStmt::DoExec(val_list* vals, stmt_flow_type& /* flow */) const
{
RegisterAccess();
@ -203,6 +240,26 @@ Val* PrintStmt::DoExec(val_list* vals, stmt_flow_type& /* flow */) const
++offset;
}
static auto print_log_type = static_cast<BifEnum::Log::PrintLogType>(
internal_val("Log::print_to_log")->AsEnum());
switch ( print_log_type ) {
case BifEnum::Log::REDIRECT_NONE:
break;
case BifEnum::Log::REDIRECT_ALL:
return print_log(vals);
case BifEnum::Log::REDIRECT_STDOUT:
if ( f->File() == stdout )
// Should catch even printing to a "manually opened" stdout file,
// like "/dev/stdout" or "-".
return print_log(vals);
break;
default:
reporter->InternalError("unknown Log::PrintLogType value: %d",
print_log_type);
break;
}
desc_style style = f->IsRawOutput() ? RAW_STYLE : STANDARD_STYLE;
if ( f->IsRawOutput() )

View file

@ -171,6 +171,27 @@ Trigger::Trigger(Expr* arg_cond, Stmt* arg_body, Stmt* arg_timeout_stmts,
Unref(this);
}
void Trigger::Terminate()
{
if ( is_return )
{
auto parent = frame->GetTrigger();
if ( ! parent->Disabled() )
{
// If the trigger was already disabled due to interpreter
// exception, an Unref already happened at that point.
parent->Disable();
Unref(parent);
}
frame->ClearTrigger();
}
Disable();
Unref(this);
}
Trigger::~Trigger()
{
DBG_LOG(DBG_NOTIFIERS, "%s: deleting", Name());

View file

@ -62,6 +62,10 @@ public:
// later to avoid race conditions.
void Modified(notifier::Modifiable* m) override
{ QueueTrigger(this); }
// Overridden from notifer::Receiver. If we're still waiting
// on an ID/Val to be modified at termination time, we can't hope
// for any further progress to be made, so just Unref ourselves.
void Terminate() override;
const char* Name() const;

View file

@ -391,7 +391,7 @@ TableType::TableType(TypeList* ind, BroType* yield)
// Allow functions, since they can be compared
// for Func* pointer equality.
if ( t == TYPE_INTERNAL_OTHER && tli->Tag() != TYPE_FUNC &&
tli->Tag() != TYPE_RECORD )
tli->Tag() != TYPE_RECORD && tli->Tag() != TYPE_PATTERN )
{
tli->Error("bad index type");
SetError();
@ -824,6 +824,71 @@ void RecordType::DescribeReST(ODesc* d, bool roles_only) const
d->PopType(this);
}
static string container_type_name(const BroType* ft)
{
string s;
if ( ft->Tag() == TYPE_RECORD )
s = "record " + ft->GetName();
else if ( ft->Tag() == TYPE_VECTOR )
s = "vector of " + container_type_name(ft->YieldType());
else if ( ft->Tag() == TYPE_TABLE )
{
if ( ft->IsSet() )
s = "set[";
else
s = "table[";
const type_list* tl = ((const IndexType*) ft)->IndexTypes();
loop_over_list(*tl, i)
{
if ( i > 0 )
s += ",";
s += container_type_name((*tl)[i]);
}
s += "]";
if ( ft->YieldType() )
{
s += " of ";
s += container_type_name(ft->YieldType());
}
}
else
s = type_name(ft->Tag());
return s;
}
TableVal* RecordType::GetRecordFieldsVal(const RecordVal* rv) const
{
auto rval = new TableVal(internal_type("record_field_table")->AsTableType());
for ( int i = 0; i < NumFields(); ++i )
{
const BroType* ft = FieldType(i);
const TypeDecl* fd = FieldDecl(i);
Val* fv = nullptr;
if ( rv )
fv = rv->Lookup(i);
if ( fv )
::Ref(fv);
bool logged = (fd->attrs && fd->FindAttr(ATTR_LOG) != 0);
RecordVal* nr = new RecordVal(internal_type("record_field")->AsRecordType());
string s = container_type_name(ft);
nr->Assign(0, new StringVal(s));
nr->Assign(1, val_mgr->GetBool(logged));
nr->Assign(2, fv);
nr->Assign(3, FieldDefault(i));
Val* field_name = new StringVal(FieldName(i));
rval->Assign(field_name, nr);
Unref(field_name);
}
return rval;
}
const char* RecordType::AddFields(type_decl_list* others, attr_list* attr)
{
assert(types);

View file

@ -75,6 +75,7 @@ class VectorType;
class TypeType;
class OpaqueType;
class EnumVal;
class TableVal;
const int DOES_NOT_MATCH_INDEX = 0;
const int MATCHES_INDEX_SCALAR = 1;
@ -483,6 +484,13 @@ public:
int NumFields() const { return num_fields; }
/**
* Returns a "record_field_table" value for introspection purposes.
* @param rv an optional record value, if given the values of
* all fields will be provided in the returned table.
*/
TableVal* GetRecordFieldsVal(const RecordVal* rv = nullptr) const;
// Returns 0 if all is ok, otherwise a pointer to an error message.
// Takes ownership of list.
const char* AddFields(type_decl_list* types, attr_list* attr);

View file

@ -120,7 +120,12 @@ Val* Val::DoClone(CloneState* state)
// Derived classes are responsible for this. Exception:
// Functions and files. There aren't any derived classes.
if ( type->Tag() == TYPE_FUNC )
return new Val(AsFunc()->DoClone());
{
auto c = AsFunc()->DoClone();
auto rval = new Val(c);
Unref(c);
return rval;
}
if ( type->Tag() == TYPE_FILE )
{
@ -396,14 +401,12 @@ bool Val::WouldOverflow(const BroType* from_type, const BroType* to_type, const
TableVal* Val::GetRecordFields()
{
TableVal* fields = new TableVal(internal_type("record_field_table")->AsTableType());
auto t = Type();
if ( t->Tag() != TYPE_RECORD && t->Tag() != TYPE_TYPE )
{
reporter->Error("non-record value/type passed to record_fields");
return fields;
return new TableVal(internal_type("record_field_table")->AsTableType());
}
RecordType* rt = nullptr;
@ -421,47 +424,17 @@ TableVal* Val::GetRecordFields()
if ( t->Tag() != TYPE_RECORD )
{
reporter->Error("non-record value/type passed to record_fields");
return fields;
return new TableVal(internal_type("record_field_table")->AsTableType());
}
rt = t->AsRecordType();
}
for ( int i = 0; i < rt->NumFields(); ++i )
{
BroType* ft = rt->FieldType(i);
TypeDecl* fd = rt->FieldDecl(i);
Val* fv = nullptr;
if ( rv )
fv = rv->Lookup(i);
if ( fv )
::Ref(fv);
bool logged = (fd->attrs && fd->FindAttr(ATTR_LOG) != 0);
RecordVal* nr = new RecordVal(internal_type("record_field")->AsRecordType());
if ( ft->Tag() == TYPE_RECORD )
nr->Assign(0, new StringVal("record " + ft->GetName()));
else
nr->Assign(0, new StringVal(type_name(ft->Tag())));
nr->Assign(1, val_mgr->GetBool(logged));
nr->Assign(2, fv);
nr->Assign(3, rt->FieldDefault(i));
Val* field_name = new StringVal(rt->FieldName(i));
fields->Assign(field_name, nr);
Unref(field_name);
}
return fields;
return rt->GetRecordFieldsVal(rv);
}
// This is a static method in this file to avoid including json.hpp in Val.h since it's huge.
static ZeekJson BuildJSON(Val* val, bool only_loggable=false, RE_Matcher* re=new RE_Matcher("^_"))
static ZeekJson BuildJSON(Val* val, bool only_loggable=false, RE_Matcher* re=nullptr)
{
// If the value wasn't set, return a nullptr. This will get turned into a 'null' in the json output.
if ( ! val )
@ -511,11 +484,7 @@ static ZeekJson BuildJSON(Val* val, bool only_loggable=false, RE_Matcher* re=new
ODesc d;
d.SetStyle(RAW_STYLE);
val->Describe(&d);
auto* bs = new BroString(1, d.TakeBytes(), d.Len());
j = string((char*)bs->Bytes(), bs->Len());
delete bs;
j = string(reinterpret_cast<const char*>(d.Bytes()), d.Len());
break;
}
@ -527,11 +496,7 @@ static ZeekJson BuildJSON(Val* val, bool only_loggable=false, RE_Matcher* re=new
ODesc d;
d.SetStyle(RAW_STYLE);
val->Describe(&d);
auto* bs = new BroString(1, d.TakeBytes(), d.Len());
j = json_escape_utf8(string((char*)bs->Bytes(), bs->Len()));
delete bs;
j = json_escape_utf8(string(reinterpret_cast<const char*>(d.Bytes()), d.Len()));
break;
}
@ -594,7 +559,7 @@ static ZeekJson BuildJSON(Val* val, bool only_loggable=false, RE_Matcher* re=new
auto field_name = rt->FieldName(i);
std::string key_string;
if ( re->MatchAnywhere(field_name) != 0 )
if ( re && re->MatchAnywhere(field_name) != 0 )
{
StringVal blank("");
StringVal fn_val(field_name);
@ -2616,7 +2581,7 @@ RecordVal* RecordVal::CoerceTo(const RecordType* t, Val* aggr, bool allow_orphan
continue;
char buf[512];
safe_snprintf(buf, sizeof(buf),
snprintf(buf, sizeof(buf),
"orphan field \"%s\" in initialization",
rv_t->FieldName(i));
Error(buf);
@ -2646,7 +2611,7 @@ RecordVal* RecordVal::CoerceTo(const RecordType* t, Val* aggr, bool allow_orphan
! ar_t->FieldDecl(i)->FindAttr(ATTR_OPTIONAL) )
{
char buf[512];
safe_snprintf(buf, sizeof(buf),
snprintf(buf, sizeof(buf),
"non-optional field \"%s\" missing in initialization", ar_t->FieldName(i));
Error(buf);
}
@ -2665,6 +2630,11 @@ RecordVal* RecordVal::CoerceTo(RecordType* t, bool allow_orphaning)
return CoerceTo(t, 0, allow_orphaning);
}
TableVal* RecordVal::GetRecordFieldsVal() const
{
return Type()->AsRecordType()->GetRecordFieldsVal(this);
}
void RecordVal::Describe(ODesc* d) const
{
const val_list* vl = AsRecord();

View file

@ -348,7 +348,7 @@ public:
TableVal* GetRecordFields();
StringVal* ToJSON(bool only_loggable=false, RE_Matcher* re=new RE_Matcher("^_"));
StringVal* ToJSON(bool only_loggable=false, RE_Matcher* re=nullptr);
protected:
@ -960,6 +960,11 @@ public:
void Describe(ODesc* d) const override;
/**
* Returns a "record_field_table" value for introspection purposes.
*/
TableVal* GetRecordFieldsVal() const;
// This is an experiment to associate a BroObj within the
// event engine to a record value in bro script.
void SetOrigin(BroObj* o) { origin = o; }

View file

@ -416,13 +416,30 @@ public:
: scope(s) { }
virtual TraversalCode PreExpr(const Expr*);
virtual TraversalCode PostExpr(const Expr*);
Scope* scope;
vector<const NameExpr*> outer_id_references;
int lambda_depth = 0;
// Note: think we really ought to toggle this to false to prevent
// considering locals within inner-lambdas as "outer", but other logic
// for "selective cloning" and locating IDs in the closure chain may
// depend on current behavior and also needs to be changed.
bool search_inner_lambdas = true;
};
TraversalCode OuterIDBindingFinder::PreExpr(const Expr* expr)
{
if ( expr->Tag() == EXPR_LAMBDA )
++lambda_depth;
if ( lambda_depth > 0 && ! search_inner_lambdas )
// Don't inspect the bodies of inner lambdas as they will have their
// own traversal to find outer IDs and we don't want to detect
// references to local IDs inside and accidentally treat them as
// "outer" since they can't be found in current scope.
return TC_CONTINUE;
if ( expr->Tag() != EXPR_NAME )
return TC_CONTINUE;
@ -438,45 +455,20 @@ TraversalCode OuterIDBindingFinder::PreExpr(const Expr* expr)
return TC_CONTINUE;
}
// Gets a function's priority from its Scope's attributes. Errors if it sees any
// problems.
static int get_func_priotity(const attr_list& attrs)
TraversalCode OuterIDBindingFinder::PostExpr(const Expr* expr)
{
int priority = 0;
for ( const auto& a : attrs )
if ( expr->Tag() == EXPR_LAMBDA )
{
if ( a->Tag() == ATTR_DEPRECATED )
continue;
if ( a->Tag() != ATTR_PRIORITY )
{
a->Error("illegal attribute for function body");
continue;
}
Val* v = a->AttrExpr()->Eval(0);
if ( ! v )
{
a->Error("cannot evaluate attribute expression");
continue;
}
if ( ! IsIntegral(v->Type()->Tag()) )
{
a->Error("expression is not of integral type");
continue;
}
priority = v->InternalInt();
--lambda_depth;
assert(lambda_depth >= 0);
}
return priority;
return TC_CONTINUE;
}
void end_func(Stmt* body)
{
std::unique_ptr<function_ingredients> ingredients = gather_function_ingredients(pop_scope(), body);
auto ingredients = std::make_unique<function_ingredients>(pop_scope(), body);
if ( streq(ingredients->id->Name(), "anonymous-function") )
{
@ -508,24 +500,10 @@ void end_func(Stmt* body)
}
ingredients->id->ID_Val()->AsFunc()->SetScope(ingredients->scope);
}
std::unique_ptr<function_ingredients> gather_function_ingredients(Scope* scope, Stmt* body)
{
auto ingredients = std::make_unique<function_ingredients>();
ingredients->frame_size = scope->Length();
ingredients->inits = scope->GetInits();
ingredients->scope = scope;
ingredients->id = scope->ScopeID();
auto attrs = scope->Attrs();
ingredients->priority = (attrs ? get_func_priotity(*attrs) : 0);
ingredients->body = body;
return ingredients;
// Note: ideally, something would take ownership of this memory until the
// end of script execution, but that's essentially the same as the
// lifetime of the process at the moment, so ok to "leak" it.
ingredients.release();
}
Val* internal_val(const char* name)
@ -548,7 +526,14 @@ id_list gather_outer_ids(Scope* scope, Stmt* body)
id_list idl ( cb.outer_id_references.size() );
for ( size_t i = 0; i < cb.outer_id_references.size(); ++i )
idl.append(cb.outer_id_references[i]->Id());
{
auto id = cb.outer_id_references[i]->Id();
if ( idl.is_member(id) )
continue;
idl.append(id);
}
return idl;
}

View file

@ -26,11 +26,6 @@ extern void begin_func(ID* id, const char* module_name, function_flavor flavor,
int is_redef, FuncType* t, attr_list* attrs = nullptr);
extern void end_func(Stmt* body);
// Gathers all of the information from a scope and a function body needed to
// build a function and collects it into a function_ingredients struct.
// Gathered elements are not refeed.
extern std::unique_ptr<function_ingredients> gather_function_ingredients(Scope* scope, Stmt* body);
// Gather all IDs referenced inside a body that aren't part of a given scope.
extern id_list gather_outer_ids(Scope* scope, Stmt* body);

View file

@ -5,6 +5,7 @@
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "NetVar.h"

View file

@ -25,6 +25,13 @@ KRB_Analyzer::KRB_Analyzer(Connection* conn)
}
#ifdef USE_KRB5
static void warn_krb(const char* msg, krb5_context ctx, krb5_error_code code)
{
auto err = krb5_get_error_message(ctx, code);
reporter->Warning("%s (%s)", msg, err);
krb5_free_error_message(ctx, err);
}
void KRB_Analyzer::Initialize_Krb()
{
if ( BifConst::KRB::keytab->Len() == 0 )
@ -40,14 +47,14 @@ void KRB_Analyzer::Initialize_Krb()
krb5_error_code retval = krb5_init_context(&krb_context);
if ( retval )
{
reporter->Warning("KRB: Couldn't initialize the context (%s)", krb5_get_error_message(krb_context, retval));
warn_krb("KRB: Couldn't initialize the context", krb_context, retval);
return;
}
retval = krb5_kt_resolve(krb_context, keytab_filename, &krb_keytab);
if ( retval )
{
reporter->Warning("KRB: Couldn't resolve keytab (%s)", krb5_get_error_message(krb_context, retval));
warn_krb("KRB: Couldn't resolve keytab", krb_context, retval);
return;
}
krb_available = true;
@ -103,33 +110,44 @@ StringVal* KRB_Analyzer::GetAuthenticationInfo(const BroString* principal, const
krb5_error_code retval = krb5_sname_to_principal(krb_context, hostname->CheckString(), service->CheckString(), KRB5_NT_SRV_HST, &sprinc);
if ( retval )
{
reporter->Warning("KRB: Couldn't generate principal name (%s)", krb5_get_error_message(krb_context, retval));
warn_krb("KRB: Couldn't generate principal name", krb_context, retval);
return nullptr;
}
krb5_ticket tkt;
tkt.server = sprinc;
tkt.enc_part.enctype = enctype;
tkt.enc_part.ciphertext.data = reinterpret_cast<char*>(ciphertext->Bytes());
tkt.enc_part.ciphertext.length = ciphertext->Len();
auto tkt = static_cast<krb5_ticket*>(safe_malloc(sizeof(krb5_ticket)));
memset(tkt, 0, sizeof(krb5_ticket));
tkt->server = sprinc;
tkt->enc_part.enctype = enctype;
auto ctd = static_cast<char*>(safe_malloc(ciphertext->Len()));
memcpy(ctd, ciphertext->Bytes(), ciphertext->Len());
tkt->enc_part.ciphertext.data = ctd;
tkt->enc_part.ciphertext.length = ciphertext->Len();
retval = krb5_server_decrypt_ticket_keytab(krb_context, krb_keytab, tkt);
retval = krb5_server_decrypt_ticket_keytab(krb_context, krb_keytab, &tkt);
if ( retval )
{
reporter->Warning("KRB: Couldn't decrypt ticket (%s)", krb5_get_error_message(krb_context, retval));
krb5_free_ticket(krb_context, tkt);
warn_krb("KRB: Couldn't decrypt ticket", krb_context, retval);
return nullptr;
}
char* cp;
retval = krb5_unparse_name(krb_context, tkt.enc_part2->client, &cp);
retval = krb5_unparse_name(krb_context, tkt->enc_part2->client, &cp);
if ( retval )
{
reporter->Warning("KRB: Couldn't unparse name (%s)", krb5_get_error_message(krb_context, retval));
krb5_free_ticket(krb_context, tkt);
warn_krb("KRB: Couldn't unparse name", krb_context, retval);
return nullptr;
}
StringVal* ret = new StringVal(cp);
krb5_free_unparsed_name(krb_context, cp);
krb5_free_ticket(krb_context, tkt);
return ret;
#else

View file

@ -29,6 +29,9 @@ RPC_CallInfo::RPC_CallInfo(uint32_t arg_xid, const u_char*& buf, int& n, double
{
v = nullptr;
xid = arg_xid;
stamp = 0;
uid = 0;
gid = 0;
start_time = arg_start_time;
last_time = arg_last_time;
@ -42,7 +45,8 @@ RPC_CallInfo::RPC_CallInfo(uint32_t arg_xid, const u_char*& buf, int& n, double
vers = extract_XDR_uint32(buf, n);
proc = extract_XDR_uint32(buf, n);
cred_flavor = extract_XDR_uint32(buf, n);
int cred_opaque_n, machinename_n;
int cred_opaque_n;
const u_char* cred_opaque = extract_XDR_opaque(buf, n, cred_opaque_n);
if ( ! cred_opaque )
@ -51,33 +55,40 @@ RPC_CallInfo::RPC_CallInfo(uint32_t arg_xid, const u_char*& buf, int& n, double
return;
}
stamp = extract_XDR_uint32(cred_opaque, cred_opaque_n);
const u_char* tmp = extract_XDR_opaque(cred_opaque, cred_opaque_n, machinename_n);
if ( ! tmp )
{
buf = nullptr;
return;
}
machinename = std::string(reinterpret_cast<const char*>(tmp), machinename_n);
uid = extract_XDR_uint32(cred_opaque, cred_opaque_n);
gid = extract_XDR_uint32(cred_opaque, cred_opaque_n);
size_t number_of_gids = extract_XDR_uint32(cred_opaque, cred_opaque_n);
if ( number_of_gids > 64 )
{
buf = nullptr;
return;
}
for ( auto i = 0u; i < number_of_gids; ++i )
auxgids.push_back(extract_XDR_uint32(cred_opaque, cred_opaque_n));
verf_flavor = skip_XDR_opaque_auth(buf, n);
if ( ! buf )
return;
if ( cred_flavor == RPC_AUTH_UNIX )
{
stamp = extract_XDR_uint32(cred_opaque, cred_opaque_n);
int machinename_n;
constexpr auto max_machinename_len = 255;
auto mnp = extract_XDR_opaque(cred_opaque, cred_opaque_n, machinename_n, max_machinename_len);
if ( ! mnp )
{
buf = nullptr;
return;
}
machinename = std::string(reinterpret_cast<const char*>(mnp), machinename_n);
uid = extract_XDR_uint32(cred_opaque, cred_opaque_n);
gid = extract_XDR_uint32(cred_opaque, cred_opaque_n);
size_t number_of_gids = extract_XDR_uint32(cred_opaque, cred_opaque_n);
if ( number_of_gids > 64 )
{
buf = nullptr;
return;
}
for ( auto i = 0u; i < number_of_gids; ++i )
auxgids.push_back(extract_XDR_uint32(cred_opaque, cred_opaque_n));
}
header_len = call_n - n;
valid_call = false;

View file

@ -889,7 +889,7 @@ void SMTP_Analyzer::UnexpectedCommand(const int cmd_code, const int reply_code)
// If this happens, please fix the SMTP state machine!
// ### Eventually, these should be turned into "weird" events.
static char buf[512];
int len = safe_snprintf(buf, sizeof(buf),
int len = snprintf(buf, sizeof(buf),
"%s reply = %d state = %d",
SMTP_CMD_WORD(cmd_code), reply_code, state);
if ( len > (int) sizeof(buf) )
@ -902,7 +902,7 @@ void SMTP_Analyzer::UnexpectedReply(const int cmd_code, const int reply_code)
// If this happens, please fix the SMTP state machine!
// ### Eventually, these should be turned into "weird" events.
static char buf[512];
int len = safe_snprintf(buf, sizeof(buf),
int len = snprintf(buf, sizeof(buf),
"%d state = %d, last command = %s",
reply_code, state, SMTP_CMD_WORD(cmd_code));
Unexpected (1, "unexpected reply", len, buf);

View file

@ -71,7 +71,7 @@ void TCPStateStats::PrintStats(BroFile* file, const char* prefix)
if ( n > 0 )
{
char buf[32];
safe_snprintf(buf, sizeof(buf), "%-8d", state_cnt[i][j]);
snprintf(buf, sizeof(buf), "%-8d", state_cnt[i][j]);
file->Write(buf);
}
else

View file

@ -1197,6 +1197,12 @@ void TCP_Analyzer::DeliverPacket(int len, const u_char* data, bool is_orig,
// TCP Fast Open).
CheckPIA_FirstPacket(is_orig, ip);
// Note the similar/inverse logic to connection_attempt.
if ( resp->state != TCP_ENDPOINT_INACTIVE ||
(orig->state != TCP_ENDPOINT_SYN_SENT &&
orig->state != TCP_ENDPOINT_SYN_ACK_SENT))
Conn()->SetSuccessful();
if ( DEBUG_tcp_data_sent )
{
DEBUG_MSG("%.6f before DataSent: len=%d caplen=%d skip=%d\n",

View file

@ -27,6 +27,7 @@ event new_connection_contents%(c: connection%);
## connection_rejected connection_reset connection_reused connection_state_remove
## connection_status_update connection_timeout scheduled_analyzer_applied
## new_connection new_connection_contents partial_connection
## connection_successful successful_connection_remove
event connection_attempt%(c: connection%);
## Generated when seeing a SYN-ACK packet from the responder in a TCP
@ -45,6 +46,7 @@ event connection_attempt%(c: connection%);
## connection_rejected connection_reset connection_reused connection_state_remove
## connection_status_update connection_timeout scheduled_analyzer_applied
## new_connection new_connection_contents partial_connection
## connection_successful successful_connection_remove
event connection_established%(c: connection%);
## Generated for a new active TCP connection if Zeek did not see the initial

View file

@ -1,5 +1,6 @@
#include "Data.h"
#include "File.h"
#include "3rdparty/doctest.h"
#include "broker/data.bif.h"
#include <broker/error.hh>
@ -35,6 +36,16 @@ static broker::port::protocol to_broker_port_proto(TransportProto tp)
}
}
TEST_CASE("converting Zeek to Broker protocol constants")
{
CHECK_EQ(to_broker_port_proto(TRANSPORT_TCP), broker::port::protocol::tcp);
CHECK_EQ(to_broker_port_proto(TRANSPORT_UDP), broker::port::protocol::udp);
CHECK_EQ(to_broker_port_proto(TRANSPORT_ICMP),
broker::port::protocol::icmp);
CHECK_EQ(to_broker_port_proto(TRANSPORT_UNKNOWN),
broker::port::protocol::unknown);
}
TransportProto bro_broker::to_bro_port_proto(broker::port::protocol tp)
{
switch ( tp ) {
@ -50,6 +61,16 @@ TransportProto bro_broker::to_bro_port_proto(broker::port::protocol tp)
}
}
TEST_CASE("converting Broker to Zeek protocol constants")
{
using bro_broker::to_bro_port_proto;
CHECK_EQ(to_bro_port_proto(broker::port::protocol::tcp), TRANSPORT_TCP);
CHECK_EQ(to_bro_port_proto(broker::port::protocol::udp), TRANSPORT_UDP);
CHECK_EQ(to_bro_port_proto(broker::port::protocol::icmp), TRANSPORT_ICMP);
CHECK_EQ(to_bro_port_proto(broker::port::protocol::unknown),
TRANSPORT_UNKNOWN);
}
struct val_converter {
using result_type = Val*;

View file

@ -143,8 +143,49 @@ event connection_timeout%(c: connection%);
## connection_status_update connection_timeout scheduled_analyzer_applied
## new_connection new_connection_contents partial_connection udp_inactivity_timeout
## tcp_inactivity_timeout icmp_inactivity_timeout conn_stats
## connection_successful successful_connection_remove
event connection_state_remove%(c: connection%);
## Generated for every new connection that is deemed "successful" according to
## transport-layer-dependent criteria. Zeek uses a flow-based definition of
## "connection" here that includes not only TCP sessions but also UDP and ICMP
## flows. For anything except TCP, this event is raised with the first packet
## of a previously unknown connection. For TCP, this event is raised if the
## responder host ever sends a packet or if the originator host ever sends a
## packet that is not a SYN (i.e. the "success" status of a connection can be
## useful to help weed out SYN scans).
##
## c: The new connection.
##
## .. zeek:see:: connection_EOF connection_SYN_packet connection_attempt
## connection_established connection_external connection_finished
## connection_first_ACK connection_half_finished connection_partial_close
## connection_pending connection_rejected connection_reset connection_reused
## connection_status_update connection_timeout scheduled_analyzer_applied
## new_connection new_connection_contents partial_connection udp_inactivity_timeout
## tcp_inactivity_timeout icmp_inactivity_timeout conn_stats connection_state_remove
## successful_connection_remove
event connection_successful%(c: connection%);
## Like :zeek:see:`connection_state_remove`, but raised only for "successful"
## connections, as defined by :zeek:see:`connection_successful`. This in particular
## excludes TCP connections that were never established (removal of such
## "unsuccessful" connections is implied by the :zeek:see:`connection_attempt`
## event instead.) Handlers for this event will run after handlers for
## :zeek:see:`connection_state_remove`
##
## c: The connection being removed
##
## .. zeek:see:: connection_EOF connection_SYN_packet connection_attempt
## connection_established connection_external connection_finished
## connection_first_ACK connection_half_finished connection_partial_close
## connection_pending connection_rejected connection_reset connection_reused
## connection_status_update connection_timeout scheduled_analyzer_applied
## new_connection new_connection_contents partial_connection udp_inactivity_timeout
## tcp_inactivity_timeout icmp_inactivity_timeout conn_stats connection_state_remove
## connection_successful
event successful_connection_remove%(c: connection%);
## Generated when a connection 4-tuple is reused. This event is raised when Zeek
## sees a new TCP session or UDP flow using a 4-tuple matching that of an
## earlier connection it still considers active.

View file

@ -183,6 +183,23 @@ bool file_analysis::OCSP::EndOfFile()
}
#if ( OPENSSL_VERSION_NUMBER >= 0x10100000L )
struct ASN1Seq {
ASN1Seq(const unsigned char** der_in, long length)
{ decoded = d2i_ASN1_SEQUENCE_ANY(nullptr, der_in, length); }
~ASN1Seq()
{ sk_ASN1_TYPE_pop_free(decoded, ASN1_TYPE_free); }
explicit operator bool() const
{ return decoded; }
operator ASN1_SEQUENCE_ANY*() const
{ return decoded; }
ASN1_SEQUENCE_ANY* decoded;
};
// Re-encode and then parse out ASN1 structures to get at what we need...
/*- BasicOCSPResponse ::= SEQUENCE {
* tbsResponseData ResponseData,
@ -209,8 +226,7 @@ static StringVal* parse_basic_resp_sig_alg(OCSP_BASICRESP* basic_resp,
const unsigned char* const_der_basic_resp_dat = der_basic_resp_dat;
auto bseq = d2i_ASN1_SEQUENCE_ANY(nullptr, &const_der_basic_resp_dat,
der_basic_resp_len);
ASN1Seq bseq{&const_der_basic_resp_dat, der_basic_resp_len};
if ( ! bseq )
{
@ -220,7 +236,6 @@ static StringVal* parse_basic_resp_sig_alg(OCSP_BASICRESP* basic_resp,
if ( sk_ASN1_TYPE_num(bseq) < 3 )
{
sk_ASN1_TYPE_free(bseq);
OPENSSL_free(der_basic_resp_dat);
return val_mgr->GetEmptyString();
}
@ -230,7 +245,6 @@ static StringVal* parse_basic_resp_sig_alg(OCSP_BASICRESP* basic_resp,
if ( ASN1_TYPE_get(aseq_type) != V_ASN1_SEQUENCE )
{
sk_ASN1_TYPE_free(bseq);
OPENSSL_free(der_basic_resp_dat);
return val_mgr->GetEmptyString();
}
@ -239,19 +253,16 @@ static StringVal* parse_basic_resp_sig_alg(OCSP_BASICRESP* basic_resp,
auto aseq_len = ASN1_STRING_length(aseq_str);
auto aseq_dat = ASN1_STRING_get0_data(aseq_str);
auto aseq = d2i_ASN1_SEQUENCE_ANY(nullptr, &aseq_dat, aseq_len);
ASN1Seq aseq{&aseq_dat, aseq_len};
if ( ! aseq )
{
sk_ASN1_TYPE_free(bseq);
OPENSSL_free(der_basic_resp_dat);
return val_mgr->GetEmptyString();
}
if ( sk_ASN1_TYPE_num(aseq) < 1 )
{
sk_ASN1_TYPE_free(aseq);
sk_ASN1_TYPE_free(bseq);
OPENSSL_free(der_basic_resp_dat);
return val_mgr->GetEmptyString();
}
@ -261,8 +272,6 @@ static StringVal* parse_basic_resp_sig_alg(OCSP_BASICRESP* basic_resp,
if ( ASN1_TYPE_get(alg_obj_type) != V_ASN1_OBJECT )
{
sk_ASN1_TYPE_free(aseq);
sk_ASN1_TYPE_free(bseq);
OPENSSL_free(der_basic_resp_dat);
return val_mgr->GetEmptyString();
}
@ -273,8 +282,6 @@ static StringVal* parse_basic_resp_sig_alg(OCSP_BASICRESP* basic_resp,
auto rval = new StringVal(alg_len, buf);
BIO_reset(bio);
sk_ASN1_TYPE_free(aseq);
sk_ASN1_TYPE_free(bseq);
OPENSSL_free(der_basic_resp_dat);
return rval;
}
@ -291,8 +298,7 @@ static Val* parse_basic_resp_data_version(OCSP_BASICRESP* basic_resp)
const unsigned char* const_der_basic_resp_dat = der_basic_resp_dat;
auto bseq = d2i_ASN1_SEQUENCE_ANY(nullptr, &const_der_basic_resp_dat,
der_basic_resp_len);
ASN1Seq bseq{&const_der_basic_resp_dat, der_basic_resp_len};
if ( ! bseq )
{
@ -302,7 +308,6 @@ static Val* parse_basic_resp_data_version(OCSP_BASICRESP* basic_resp)
if ( sk_ASN1_TYPE_num(bseq) < 3 )
{
sk_ASN1_TYPE_free(bseq);
OPENSSL_free(der_basic_resp_dat);
return val_mgr->GetCount(-1);
}
@ -312,7 +317,6 @@ static Val* parse_basic_resp_data_version(OCSP_BASICRESP* basic_resp)
if ( ASN1_TYPE_get(dseq_type) != V_ASN1_SEQUENCE )
{
sk_ASN1_TYPE_free(bseq);
OPENSSL_free(der_basic_resp_dat);
return val_mgr->GetCount(-1);
}
@ -321,19 +325,16 @@ static Val* parse_basic_resp_data_version(OCSP_BASICRESP* basic_resp)
auto dseq_len = ASN1_STRING_length(dseq_str);
auto dseq_dat = ASN1_STRING_get0_data(dseq_str);
auto dseq = d2i_ASN1_SEQUENCE_ANY(nullptr, &dseq_dat, dseq_len);
ASN1Seq dseq{&dseq_dat, dseq_len};
if ( ! dseq )
{
sk_ASN1_TYPE_free(bseq);
OPENSSL_free(der_basic_resp_dat);
return val_mgr->GetEmptyString();
}
if ( sk_ASN1_TYPE_num(dseq) < 1 )
{
sk_ASN1_TYPE_free(dseq);
sk_ASN1_TYPE_free(bseq);
OPENSSL_free(der_basic_resp_dat);
return val_mgr->GetEmptyString();
}
@ -351,16 +352,12 @@ static Val* parse_basic_resp_data_version(OCSP_BASICRESP* basic_resp)
if ( ASN1_TYPE_get(version_type) != V_ASN1_INTEGER )
{
sk_ASN1_TYPE_free(dseq);
sk_ASN1_TYPE_free(bseq);
OPENSSL_free(der_basic_resp_dat);
// Not present, use default value.
return val_mgr->GetCount(0);
}
uint64_t asn1_int = ASN1_INTEGER_get(version_type->value.integer);
sk_ASN1_TYPE_free(dseq);
sk_ASN1_TYPE_free(bseq);
OPENSSL_free(der_basic_resp_dat);
return val_mgr->GetCount(asn1_int);
}
@ -375,8 +372,7 @@ static uint64_t parse_request_version(OCSP_REQUEST* req)
if ( ! der_req_dat )
return -1;
auto rseq = d2i_ASN1_SEQUENCE_ANY(nullptr, &const_der_req_dat,
der_req_len);
ASN1Seq rseq{&const_der_req_dat, der_req_len};
if ( ! rseq )
{
@ -386,7 +382,6 @@ static uint64_t parse_request_version(OCSP_REQUEST* req)
if ( sk_ASN1_TYPE_num(rseq) < 1 )
{
sk_ASN1_TYPE_free(rseq);
OPENSSL_free(der_req_dat);
return -1;
}
@ -396,14 +391,12 @@ static uint64_t parse_request_version(OCSP_REQUEST* req)
if ( ASN1_TYPE_get(version_type) != V_ASN1_INTEGER )
{
sk_ASN1_TYPE_free(rseq);
OPENSSL_free(der_req_dat);
// Not present, use default value.
return 0;
}
uint64_t asn1_int = ASN1_INTEGER_get(version_type->value.integer);
sk_ASN1_TYPE_free(rseq);
OPENSSL_free(der_req_dat);
return asn1_int;
}

View file

@ -11,7 +11,7 @@ namespace Zeek_X509 {
class Plugin : public plugin::Plugin {
public:
plugin::Configuration Configure()
plugin::Configuration Configure() override
{
AddComponent(new ::file_analysis::Component("X509", ::file_analysis::X509::Instantiate));
AddComponent(new ::file_analysis::Component("OCSP_REQUEST", ::file_analysis::OCSP::InstantiateRequest));
@ -22,6 +22,12 @@ public:
config.description = "X509 and OCSP analyzer";
return config;
}
void Done() override
{
plugin::Plugin::Done();
::file_analysis::X509::FreeRootStore();
}
} plugin;
}

View file

@ -18,6 +18,10 @@
#include <openssl/opensslconf.h>
#include <openssl/err.h>
namespace file_analysis {
std::map<Val*, X509_STORE*> X509::x509_stores;
}
using namespace file_analysis;
file_analysis::X509::X509(RecordVal* args, file_analysis::File* file)
@ -213,6 +217,47 @@ RecordVal* file_analysis::X509::ParseCertificate(X509Val* cert_val, File* f)
return pX509Cert;
}
X509_STORE* file_analysis::X509::GetRootStore(TableVal* root_certs)
{
// If this certificate store was built previously, just reuse the old one.
if ( x509_stores.count(root_certs) > 0 )
return x509_stores[root_certs];
X509_STORE* ctx = X509_STORE_new();
ListVal* idxs = root_certs->ConvertToPureList();
// Build the validation store
for ( int i = 0; i < idxs->Length(); ++i )
{
Val* key = idxs->Index(i);
StringVal *sv = root_certs->Lookup(key)->AsStringVal();
assert(sv);
const uint8_t* data = sv->Bytes();
::X509* x = d2i_X509(NULL, &data, sv->Len());
if ( ! x )
{
builtin_error(fmt("Root CA error: %s", ERR_error_string(ERR_get_error(),NULL)));
return 0;
}
X509_STORE_add_cert(ctx, x);
X509_free(x);
}
delete idxs;
// Save the newly constructed certificate store into the cacheing map.
x509_stores[root_certs] = ctx;
return ctx;
}
void file_analysis::X509::FreeRootStore()
{
for ( const auto& e : x509_stores )
X509_STORE_free(e.second);
}
void file_analysis::X509::ParseBasicConstraints(X509_EXTENSION* ex)
{
assert(OBJ_obj2nid(X509_EXTENSION_get_object(ex)) == NID_basic_constraints);

View file

@ -3,6 +3,7 @@
#pragma once
#include <string>
#include <map>
#include "OpaqueVal.h"
#include "X509Common.h"
@ -89,6 +90,28 @@ public:
static file_analysis::Analyzer* Instantiate(RecordVal* args, File* file)
{ return new X509(args, file); }
/**
* Retrieves OpenSSL's representation of an X509 certificate store
* associated with a script-layer certificate root table variable/value.
* The underlying X509 store will be created if it has not been already,
* else the previously allocated one for the same table will be returned.
*
* @param root_certs The script-layer certificate root table value.
*
* @return OpenSSL's X509 store associated with the table value.
*/
static X509_STORE* GetRootStore(TableVal* root_certs);
/**
* Frees memory obtained from OpenSSL that is associated with the global
* X509 certificate store used by the Zeek scripting-layer. This primarily
* exists so leak checkers like LeakSanitizer don't count the
* globally-allocated mapping as a leak. Would be easy to suppress/ignore
* it, but that could accidentally silence cases where some new code
* mistakenly overwrites a table element without freeing it.
*/
static void FreeRootStore();
protected:
X509(RecordVal* args, File* file);
@ -102,6 +125,8 @@ private:
// Helpers for ParseCertificate.
static StringVal* KeyCurve(EVP_PKEY *key);
static unsigned int KeyLength(EVP_PKEY *key);
/** X509 stores associated with global script-layer values */
static std::map<Val*, X509_STORE*> x509_stores;
};
/**

View file

@ -10,9 +10,6 @@
#include <openssl/pem.h>
#include <openssl/err.h>
// This is the indexed map of X509 certificate stores.
static map<Val*, X509_STORE*> x509_stores;
// construct an error record
RecordVal* x509_result_record(uint64_t num, const char* reason, Val* chainVector = 0)
{
@ -26,41 +23,6 @@ RecordVal* x509_result_record(uint64_t num, const char* reason, Val* chainVector
return rrecord;
}
X509_STORE* x509_get_root_store(TableVal* root_certs)
{
// If this certificate store was built previously, just reuse the old one.
if ( x509_stores.count(root_certs) > 0 )
return x509_stores[root_certs];
X509_STORE* ctx = X509_STORE_new();
ListVal* idxs = root_certs->ConvertToPureList();
// Build the validation store
for ( int i = 0; i < idxs->Length(); ++i )
{
Val* key = idxs->Index(i);
StringVal *sv = root_certs->Lookup(key)->AsStringVal();
assert(sv);
const uint8_t* data = sv->Bytes();
X509* x = d2i_X509(NULL, &data, sv->Len());
if ( ! x )
{
builtin_error(fmt("Root CA error: %s", ERR_error_string(ERR_get_error(),NULL)));
return 0;
}
X509_STORE_add_cert(ctx, x);
X509_free(x);
}
delete idxs;
// Save the newly constructed certificate store into the cacheing map.
x509_stores[root_certs] = ctx;
return ctx;
}
// get all cretificates starting at the second one (assuming the first one is the host certificate)
STACK_OF(X509)* x509_get_untrusted_stack(VectorVal* certs_vec)
{
@ -254,7 +216,7 @@ function x509_get_certificate_string%(cert: opaque of x509, pem: bool &default=F
function x509_ocsp_verify%(certs: x509_opaque_vector, ocsp_reply: string, root_certs: table_string_of_string, verify_time: time &default=network_time()%): X509::Result
%{
RecordVal* rval = 0;
X509_STORE* ctx = x509_get_root_store(root_certs->AsTableVal());
X509_STORE* ctx = ::file_analysis::X509::GetRootStore(root_certs->AsTableVal());
if ( ! ctx )
return x509_result_record(-1, "Problem initializing root store");
@ -540,7 +502,7 @@ x509_ocsp_cleanup:
## x509_get_certificate_string x509_ocsp_verify sct_verify
function x509_verify%(certs: x509_opaque_vector, root_certs: table_string_of_string, verify_time: time &default=network_time()%): X509::Result
%{
X509_STORE* ctx = x509_get_root_store(root_certs->AsTableVal());
X509_STORE* ctx = ::file_analysis::X509::GetRootStore(root_certs->AsTableVal());
if ( ! ctx )
return x509_result_record(-1, "Problem initializing root store");

View file

@ -444,6 +444,7 @@ bool Manager::CreateEventStream(RecordVal* fval)
if ( status )
{
reporter->Error("Input stream %s: Problem unrolling", stream_name.c_str());
for ( auto& f : fieldsV ) delete f;
return false;
}
@ -453,6 +454,7 @@ bool Manager::CreateEventStream(RecordVal* fval)
if ( ! res )
{
delete stream;
for ( auto& f : fieldsV ) delete f;
return false;
}
@ -671,6 +673,7 @@ bool Manager::CreateTableStream(RecordVal* fval)
if ( (valfields > 1) && (want_record->InternalInt() != 1) )
{
reporter->Error("Input stream %s: Stream does not want a record (want_record=F), but has more then one value field.", stream_name.c_str());
for ( auto& f : fieldsV ) delete f;
return false;
}
@ -680,6 +683,7 @@ bool Manager::CreateTableStream(RecordVal* fval)
if ( status )
{
reporter->Error("Input stream %s: Problem unrolling", stream_name.c_str());
for ( auto& f : fieldsV ) delete f;
return false;
}
@ -689,6 +693,7 @@ bool Manager::CreateTableStream(RecordVal* fval)
if ( ! res )
{
delete stream;
for ( auto& f : fieldsV ) delete f;
return false;
}
}
@ -1289,7 +1294,8 @@ int Manager::SendEntryTable(Stream* i, const Value* const *vals)
if ( predidx != 0 )
Unref(predidx);
stream->currDict->Insert(idxhash, ih);
auto prev = stream->currDict->Insert(idxhash, ih);
delete prev;
delete idxhash;
if ( stream->event )

View file

@ -49,6 +49,9 @@ SQLite::~SQLite()
void SQLite::DoClose()
{
sqlite3_finalize(st);
st = nullptr;
if ( db != 0 )
{
sqlite3_close(db);

View file

@ -85,7 +85,7 @@ bool BPF_Program::Compile(pcap_t* pcap, const char* filter, uint32_t netmask,
if ( pcap_compile(pcap, &m_program, (char *) filter, optimize, netmask) < 0 )
{
if ( errbuf )
safe_snprintf(errbuf, errbuf_len,
snprintf(errbuf, errbuf_len,
"pcap_compile(%s): %s", filter,
pcap_geterr(pcap));

View file

@ -93,7 +93,7 @@ void PktSrc::Opened(const Properties& arg_props)
if ( Packet::GetLinkHeaderSize(arg_props.link_type) < 0 )
{
char buf[512];
safe_snprintf(buf, sizeof(buf),
snprintf(buf, sizeof(buf),
"unknown data link type 0x%x", arg_props.link_type);
Error(buf);
Close();

View file

@ -62,7 +62,7 @@ void PcapSource::OpenLive()
if ( pcap_findalldevs(&devs, tmp_errbuf) < 0 )
{
safe_snprintf(errbuf, sizeof(errbuf),
snprintf(errbuf, sizeof(errbuf),
"pcap_findalldevs: %s", tmp_errbuf);
Error(errbuf);
return;
@ -75,7 +75,7 @@ void PcapSource::OpenLive()
if ( props.path.empty() )
{
safe_snprintf(errbuf, sizeof(errbuf),
snprintf(errbuf, sizeof(errbuf),
"pcap_findalldevs: empty device name");
Error(errbuf);
return;
@ -83,7 +83,7 @@ void PcapSource::OpenLive()
}
else
{
safe_snprintf(errbuf, sizeof(errbuf),
snprintf(errbuf, sizeof(errbuf),
"pcap_findalldevs: no devices found");
Error(errbuf);
return;
@ -263,7 +263,7 @@ bool PcapSource::SetFilter(int index)
if ( ! code )
{
safe_snprintf(errbuf, sizeof(errbuf),
snprintf(errbuf, sizeof(errbuf),
"No precompiled pcap filter for index %d",
index);
Error(errbuf);

View file

@ -98,6 +98,7 @@ Manager::Filter::~Filter()
free(fields);
Unref(path_val);
Unref(config);
}
Manager::Stream::~Stream()
@ -919,6 +920,7 @@ bool Manager::Write(EnumVal* id, RecordVal* columns)
filter->fields, vals),
true) )
{
Unref(columns);
DeleteVals(filter->num_fields, vals);
#ifdef DEBUG

View file

@ -133,6 +133,11 @@ WriterFrontend::WriterFrontend(const WriterBackend::WriterInfo& arg_info, EnumVa
WriterFrontend::~WriterFrontend()
{
for ( auto i = 0; i < num_fields; ++i )
delete fields[i];
delete [] fields;
Unref(stream);
Unref(writer);
delete info;
@ -165,7 +170,14 @@ void WriterFrontend::Init(int arg_num_fields, const Field* const * arg_fields)
initialized = true;
if ( backend )
backend->SendIn(new InitMessage(backend, arg_num_fields, arg_fields));
{
auto fs = new Field*[num_fields];
for ( auto i = 0; i < num_fields; ++i )
fs[i] = new Field(*fields[i]);
backend->SendIn(new InitMessage(backend, arg_num_fields, fs));
}
if ( remote )
{

View file

@ -10,6 +10,12 @@ type Filter: record;
type Stream: record;
type RotationInfo: record;
enum PrintLogType %{
REDIRECT_NONE,
REDIRECT_STDOUT,
REDIRECT_ALL,
%}
function Log::__create_stream%(id: Log::ID, stream: Log::Stream%) : bool
%{
bool result = log_mgr->CreateStream(id->AsEnumVal(), stream->AsRecordVal());

View file

@ -63,6 +63,9 @@ extern "C" {
#include "3rdparty/sqlite3.h"
#define DOCTEST_CONFIG_IMPLEMENT
#include "3rdparty/doctest.h"
Brofiler brofiler;
#ifndef HAVE_STRSEP
@ -153,7 +156,9 @@ static bool zeek_dns_fake()
static void usage(const char* prog, int code = 1)
{
fprintf(stderr, "zeek version %s\n", zeek_version());
fprintf(stderr, "usage: %s [options] [file ...]\n", prog);
fprintf(stderr, "usage: %s --test [doctest-options] -- [options] [file ...]\n", prog);
fprintf(stderr, " <file> | Zeek script file, or read stdin\n");
fprintf(stderr, " -a|--parse-only | exit immediately after parsing scripts\n");
fprintf(stderr, " -b|--bare-mode | don't load scripts from the base/ directory\n");
@ -196,6 +201,7 @@ static void usage(const char* prog, int code = 1)
fprintf(stderr, " -n|--idmef-dtd <idmef-msg.dtd> | specify path to IDMEF DTD file\n");
#endif
fprintf(stderr, " --test | run unit tests ('--test -h' for help, only when compiling with ENABLE_ZEEK_UNIT_TESTS)\n");
fprintf(stderr, " $ZEEKPATH | file search path (%s)\n", bro_path().c_str());
fprintf(stderr, " $ZEEK_PLUGIN_PATH | plugin search path (%s)\n", bro_plugin_path());
fprintf(stderr, " $ZEEK_PLUGIN_ACTIVATE | plugins to always activate (%s)\n", bro_plugin_activate());
@ -240,6 +246,9 @@ struct zeek_options {
bool perftools_check_leaks = false;
bool perftools_profile = false;
bool run_unit_tests = false;
std::vector<std::string> doctest_args;
std::optional<std::string> pcap_filter;
std::vector<std::string> interfaces;
std::vector<std::string> pcap_files;
@ -257,10 +266,67 @@ struct zeek_options {
std::vector<std::string> script_options_to_set;
};
static std::vector<const char*> to_cargs(const std::vector<std::string>& args)
{
std::vector<const char*> rval;
rval.reserve(args.size());
for ( const auto& arg : args )
rval.emplace_back(arg.data());
return rval;
}
static zeek_options parse_cmdline(int argc, char** argv)
{
zeek_options rval = {};
// When running unit tests, the first argument on the command line must be
// --test, followed by doctest options. Optionally, users can use "--" as
// separator to pass Zeek options afterwards:
//
// zeek --test [doctest-options] -- [zeek-options]
// Just locally filtering out the args for Zeek usage from doctest args.
std::vector<std::string> zeek_args;
if ( argc > 1 && strcmp(argv[1], "--test") == 0 )
{
#ifdef DOCTEST_CONFIG_DISABLE
fprintf(stderr, "ERROR: C++ unit tests are disabled for this build.\n"
" Please re-compile with ENABLE_ZEEK_UNIT_TESTS "
"to run the C++ unit tests.\n");
usage(argv[0], 1);
#endif
auto is_separator = [](const char* cstr)
{
return strcmp(cstr, "--") == 0;
};
auto first = argv;
auto last = argv + argc;
auto separator = std::find_if(first, last, is_separator);
zeek_args.emplace_back(argv[0]);
if ( separator != last )
{
auto first_zeek_arg = std::next(separator);
for ( auto i = first_zeek_arg; i != last; ++i )
zeek_args.emplace_back(*i);
}
rval.run_unit_tests = true;
for ( auto i = 0; i < std::distance(first, separator); ++i )
rval.doctest_args.emplace_back(argv[i]);
}
else
{
for ( auto i = 0; i < argc; ++i )
zeek_args.emplace_back(argv[i]);
}
constexpr struct option long_opts[] = {
{"parse-only", no_argument, 0, 'a'},
{"bare-mode", no_argument, 0, 'b'},
@ -302,6 +368,7 @@ static zeek_options parse_cmdline(int argc, char** argv)
{"pseudo-realtime", optional_argument, 0, 'E'},
{"jobs", optional_argument, 0, 'j'},
{"test", no_argument, 0, '#'},
{0, 0, 0, 0},
};
@ -318,7 +385,13 @@ static zeek_options parse_cmdline(int argc, char** argv)
int long_optsind;
opterr = 0;
while ( (op = getopt_long(argc, argv, opts, long_opts, &long_optsind)) != EOF )
// getopt may permute the array, so need yet another array
auto zargs = std::make_unique<char*[]>(zeek_args.size());
for ( auto i = 0; i < zeek_args.size(); ++i )
zargs[i] = zeek_args[i].data();
while ( (op = getopt_long(zeek_args.size(), zargs.get(), opts, long_opts, &long_optsind)) != EOF )
switch ( op ) {
case 'a':
rval.parse_only = true;
@ -387,7 +460,7 @@ static zeek_options parse_cmdline(int argc, char** argv)
break;
case 'F':
if ( rval.dns_mode != DNS_DEFAULT )
usage(argv[0], 1);
usage(zargs[0], 1);
rval.dns_mode = DNS_FORCE;
break;
case 'G':
@ -404,7 +477,7 @@ static zeek_options parse_cmdline(int argc, char** argv)
break;
case 'P':
if ( rval.dns_mode != DNS_DEFAULT )
usage(argv[0], 1);
usage(zargs[0], 1);
rval.dns_mode = DNS_PRIME;
break;
case 'Q':
@ -441,6 +514,11 @@ static zeek_options parse_cmdline(int argc, char** argv)
break;
#endif
case '#':
fprintf(stderr, "ERROR: --test only allowed as first argument.\n");
usage(zargs[0], 1);
break;
case 0:
// This happens for long options that don't have
// a short-option equivalent.
@ -448,21 +526,21 @@ static zeek_options parse_cmdline(int argc, char** argv)
case '?':
default:
usage(argv[0], 1);
usage(zargs[0], 1);
break;
}
// Process remaining arguments. X=Y arguments indicate script
// variable/parameter assignments. X::Y arguments indicate plugins to
// activate/query. The remainder are treated as scripts to load.
while ( optind < argc )
while ( optind < zeek_args.size() )
{
if ( strchr(argv[optind], '=') )
rval.script_options_to_set.emplace_back(argv[optind++]);
else if ( strstr(argv[optind], "::") )
rval.plugins_to_load.emplace(argv[optind++]);
if ( strchr(zargs[optind], '=') )
rval.script_options_to_set.emplace_back(zargs[optind++]);
else if ( strstr(zargs[optind], "::") )
rval.plugins_to_load.emplace(zargs[optind++]);
else
rval.scripts_to_load.emplace_back(argv[optind++]);
rval.scripts_to_load.emplace_back(zargs[optind++]);
}
return rval;
@ -561,6 +639,8 @@ void done_with_network()
abort();
}
#endif
ZEEK_LSAN_DISABLE();
}
void terminate_bro()
@ -594,6 +674,7 @@ void terminate_bro()
mgr.Drain();
notifier::registry.Terminate();
log_mgr->Terminate();
input_mgr->Terminate();
thread_mgr->Terminate();
@ -711,6 +792,9 @@ static std::string get_exe_path(const std::string& invocation)
int main(int argc, char** argv)
{
ZEEK_LSAN_DISABLE();
std::set_new_handler(bro_new_handler);
auto zeek_exe_path = get_exe_path(argv[0]);
if ( zeek_exe_path.empty() )
@ -736,6 +820,14 @@ int main(int argc, char** argv)
exit(0);
}
if ( options.run_unit_tests )
{
doctest::Context context;
auto dargs = to_cargs(options.doctest_args);
context.applyCommandLine(dargs.size(), dargs.data());
return context.run();
}
auto use_supervisor = [&]() -> bool { return options.supervised_workers > 0; };
pid_t stem_pid = 0;
std::unique_ptr<bro::PipePair> supervisor_pipe;
@ -814,8 +906,6 @@ int main(int argc, char** argv)
}
}
std::set_new_handler(bro_new_handler);
double time_start = current_time(true);
brofiler.ReadStats();
@ -1214,6 +1304,13 @@ int main(int argc, char** argv)
reporter->Warning("event handler never invoked: %s", handler.c_str());
}
// Enable LeakSanitizer before zeek_init() and even before executing
// top-level statements. Even though it's not bad if a leak happens only
// once at initialization, we have to assume that script-layer code causing
// such a leak can be placed in any arbitrary event handler and potentially
// cause more severe problems.
ZEEK_LSAN_ENABLE();
if ( stmts )
{
stmt_flow_type flow;

View file

@ -1,10 +1,14 @@
//
// See the file "COPYING" in the main distribution directory for copyright.
#include <string>
#include <string.h>
#include "module_util.h"
#include <string.h>
#include <iostream>
#include <string>
#include "3rdparty/doctest.h"
using namespace std;
static int streq(const char* s1, const char* s2)
@ -12,7 +16,20 @@ static int streq(const char* s1, const char* s2)
return ! strcmp(s1, s2);
}
// Returns it without trailing "::".
TEST_CASE("module_util streq")
{
CHECK(streq("abcd", "abcd") == true);
CHECK(streq("abcd", "efgh") == false);
}
TEST_CASE("module_util extract_module_name")
{
CHECK(extract_module_name("mod") == GLOBAL_MODULE_NAME);
CHECK(extract_module_name("mod::") == "mod");
CHECK(extract_module_name("mod::var") == "mod");
}
// Returns it without trailing "::" var section.
string extract_module_name(const char* name)
{
string module_name = name;
@ -26,6 +43,14 @@ string extract_module_name(const char* name)
return module_name;
}
TEST_CASE("module_util extract_var_name")
{
CHECK(extract_var_name("mod") == "mod");
CHECK(extract_var_name("mod::") == "");
CHECK(extract_var_name("mod::var") == "var");
CHECK(extract_var_name("::var") == "var");
}
string extract_var_name(const char *name)
{
string var_name = name;
@ -40,6 +65,13 @@ string extract_var_name(const char *name)
return var_name.substr(pos+2);
}
TEST_CASE("module_util normalized_module_name")
{
CHECK(normalized_module_name("a") == "a");
CHECK(normalized_module_name("module") == "module");
CHECK(normalized_module_name("module::") == "module");
}
string normalized_module_name(const char* module_name)
{
int mod_len;
@ -50,6 +82,18 @@ string normalized_module_name(const char* module_name)
return string(module_name, mod_len);
}
TEST_CASE("module_util make_full_var_name")
{
CHECK(make_full_var_name(nullptr, "GLOBAL::var") == "var");
CHECK(make_full_var_name(GLOBAL_MODULE_NAME, "var") == "var");
CHECK(make_full_var_name(nullptr, "notglobal::var") == "notglobal::var");
CHECK(make_full_var_name(nullptr, "::var") == "::var");
CHECK(make_full_var_name("module", "var") == "module::var");
CHECK(make_full_var_name("module::", "var") == "module::var");
CHECK(make_full_var_name("", "var") == "::var");
}
string make_full_var_name(const char* module_name, const char* var_name)
{
if ( ! module_name || streq(module_name, GLOBAL_MODULE_NAME) ||

View file

@ -136,7 +136,7 @@ const char* fmt_conn_id(const IPAddr& src_addr, uint32_t src_port,
{
static char buffer[512];
safe_snprintf(buffer, sizeof(buffer), "%s:%d > %s:%d",
snprintf(buffer, sizeof(buffer), "%s:%d > %s:%d",
string(src_addr).c_str(), src_port,
string(dst_addr).c_str(), dst_port);

View file

@ -1234,7 +1234,7 @@ anonymous_function:
// a lambda expression.
// Gather the ingredients for a BroFunc from the current scope
std::unique_ptr<function_ingredients> ingredients = gather_function_ingredients(current_scope(), $5);
auto ingredients = std::make_unique<function_ingredients>(current_scope(), $5);
id_list outer_ids = gather_outer_ids(pop_scope(), $5);
$$ = new LambdaExpr(std::move(ingredients), std::move(outer_ids));

View file

@ -432,7 +432,7 @@ New_Patricia (int maxbits)
*/
void
Clear_Patricia (patricia_tree_t *patricia, void_fn_t func)
Clear_Patricia (patricia_tree_t *patricia, data_fn_t func)
{
assert (patricia);
if (patricia->head) {
@ -476,7 +476,7 @@ Clear_Patricia (patricia_tree_t *patricia, void_fn_t func)
void
Destroy_Patricia (patricia_tree_t *patricia, void_fn_t func)
Destroy_Patricia (patricia_tree_t *patricia, data_fn_t func)
{
Clear_Patricia (patricia, func);
Delete (patricia);
@ -489,7 +489,7 @@ Destroy_Patricia (patricia_tree_t *patricia, void_fn_t func)
*/
void
patricia_process (patricia_tree_t *patricia, void_fn_t func)
patricia_process (patricia_tree_t *patricia, prefix_data_fn_t func)
{
patricia_node_t *node;
assert (func);

View file

@ -51,8 +51,6 @@
#include <sys/types.h>
/* typedef unsigned int u_int; */
typedef void (*void_fn_t)();
/* { from defs.h */
#define prefix_touchar(prefix) ((u_char *)&(prefix)->add.sin)
#define MAXLINE 1024
@ -84,6 +82,9 @@ typedef struct _prefix_t {
} add;
} prefix_t;
typedef void (*data_fn_t)(void*);
typedef void (*prefix_data_fn_t)(prefix_t*, void*);
/* } */
typedef struct _patricia_node_t {
@ -110,9 +111,9 @@ patricia_node_t * patricia_search_best2 (patricia_tree_t *patricia, prefix_t *pr
patricia_node_t *patricia_lookup (patricia_tree_t *patricia, prefix_t *prefix);
void patricia_remove (patricia_tree_t *patricia, patricia_node_t *node);
patricia_tree_t *New_Patricia (int maxbits);
void Clear_Patricia (patricia_tree_t *patricia, void_fn_t func);
void Destroy_Patricia (patricia_tree_t *patricia, void_fn_t func);
void patricia_process (patricia_tree_t *patricia, void_fn_t func);
void Clear_Patricia (patricia_tree_t *patricia, data_fn_t func);
void Destroy_Patricia (patricia_tree_t *patricia, data_fn_t func);
void patricia_process (patricia_tree_t *patricia, prefix_data_fn_t func);
void Deref_Prefix (prefix_t * prefix);

View file

@ -156,7 +156,8 @@ function Reporter::get_weird_sampling_whitelist%(%): string_set
TableVal* set = new TableVal(string_set);
for ( auto el : reporter->GetWeirdSamplingWhitelist() )
{
set->Assign(new StringVal(el), nullptr);
auto idx = make_intrusive<StringVal>(el);
set->Assign(idx.get(), nullptr);
}
return set;
%}

View file

@ -197,7 +197,7 @@ finger { rules_lval.val = Rule::FINGER; return TOK_PATTERN_TYPE; }
const char fmt[] = "(?i:%s)";
int n = len + strlen(fmt);
char* s = new char[n + 5 /* slop */];
safe_snprintf(s, n + 5, fmt, yytext + 1);
snprintf(s, n + 5, fmt, yytext + 1);
rules_lval.str = s;
}
else

View file

@ -1096,15 +1096,15 @@ function hexdump%(data_str: string%) : string
if ( x == 0 )
{
char offset[5];
safe_snprintf(offset, sizeof(offset),
"%.4x", data_ptr - data);
snprintf(offset, sizeof(offset),
"%.4tx", data_ptr - data);
memcpy(hex_data_ptr, offset, 4);
hex_data_ptr += 6;
ascii_ptr = hex_data_ptr + 50;
}
char hex_byte[3];
safe_snprintf(hex_byte, sizeof(hex_byte),
snprintf(hex_byte, sizeof(hex_byte),
"%.2x", (u_char) *data_ptr);
int val = (u_char) *data_ptr;

View file

@ -61,7 +61,7 @@ const char* BasicThread::Fmt(const char* format, ...)
va_list al;
va_start(al, format);
int n = safe_vsnprintf(buf, buf_len, format, al);
int n = vsnprintf(buf, buf_len, format, al);
va_end(al);
if ( (unsigned int) n >= buf_len )
@ -71,7 +71,7 @@ const char* BasicThread::Fmt(const char* format, ...)
// Is it portable to restart?
va_start(al, format);
n = safe_vsnprintf(buf, buf_len, format, al);
n = vsnprintf(buf, buf_len, format, al);
va_end(al);
}

View file

@ -176,6 +176,7 @@ MsgThread::MsgThread() : BasicThread(), queue_in(this, 0), queue_out(0, this)
cnt_sent_in = cnt_sent_out = 0;
main_finished = false;
child_finished = false;
child_sent_finish = false;
failed = false;
thread_mgr->AddMsgThread(this);
}
@ -185,9 +186,10 @@ extern int signal_val;
void MsgThread::OnSignalStop()
{
if ( main_finished || Killed() )
if ( main_finished || Killed() || child_sent_finish )
return;
child_sent_finish = true;
// Signal thread to terminate.
SendIn(new FinishMessage(this, network_time), true);
}
@ -258,6 +260,9 @@ void MsgThread::OnKill()
void MsgThread::Heartbeat()
{
if ( child_sent_finish )
return;
SendIn(new HeartbeatMessage(this, network_time, current_time()));
}

View file

@ -306,6 +306,7 @@ private:
bool main_finished; // Main thread is finished, meaning child_finished propagated back through message queue.
bool child_finished; // Child thread is finished.
bool child_sent_finish; // Child thread asked to be finished.
bool failed; // Set to true when a command failed.
};

View file

@ -53,6 +53,15 @@
#include "iosource/Manager.h"
#include "ConvertUTF.h"
#include "3rdparty/doctest.h"
TEST_CASE("util extract_ip")
{
CHECK(extract_ip("[1.2.3.4]") == "1.2.3.4");
CHECK(extract_ip("0x1.2.3.4") == "1.2.3.4");
CHECK(extract_ip("[]") == "");
}
/**
* Return IP address without enclosing brackets and any leading 0x. Also
* trims leading/trailing whitespace.
@ -74,6 +83,25 @@ std::string extract_ip(const std::string& i)
return s;
}
TEST_CASE("util extract_ip_and_len")
{
int len;
std::string out = extract_ip_and_len("[1.2.3.4/24]", &len);
CHECK(out == "1.2.3.4");
CHECK(len == 24);
out = extract_ip_and_len("0x1.2.3.4/32", &len);
CHECK(out == "1.2.3.4");
CHECK(len == 32);
out = extract_ip_and_len("[]/abcd", &len);
CHECK(out == "");
CHECK(len == 0);
out = extract_ip_and_len("[]/16", nullptr);
CHECK(out == "");
}
/**
* Given a subnet string, return IP address and subnet length separately.
*/
@ -89,6 +117,12 @@ std::string extract_ip_and_len(const std::string& i, int* len)
return extract_ip(i.substr(0, pos));
}
TEST_CASE("util get_unescaped_string")
{
CHECK(get_unescaped_string("abcde") == "abcde");
CHECK(get_unescaped_string("\\x41BCD\\x45") == "ABCDE");
}
/**
* Takes a string, unescapes all characters that are escaped as hex codes
* (\x##) and turns them into the equivalent ascii-codes. Returns a string
@ -127,6 +161,31 @@ std::string get_unescaped_string(const std::string& arg_str)
return outstring;
}
TEST_CASE("util get_escaped_string")
{
SUBCASE("returned ODesc")
{
ODesc* d = get_escaped_string(nullptr, "a bcd\n", 6, false);
CHECK(strcmp(d->Description(), "a\\x20bcd\\x0a") == 0);
}
SUBCASE("provided ODesc")
{
ODesc d2;
get_escaped_string(&d2, "ab\\e", 4, true);
CHECK(strcmp(d2.Description(), "\\x61\\x62\\\\\\x65") == 0);
}
SUBCASE("std::string versions")
{
std::string s = get_escaped_string("a b c", 5, false);
CHECK(s == "a\\x20b\\x20c");
s = get_escaped_string("d e", false);
CHECK(s == "d\\x20e");
}
}
/**
* Takes a string, escapes characters into equivalent hex codes (\x##), and
* returns a string containing all escaped values.
@ -184,6 +243,12 @@ char* copy_string(const char* s)
return c;
}
TEST_CASE("util streq")
{
CHECK(streq("abcd", "abcd") == true);
CHECK(streq("abcd", "efgh") == false);
}
int streq(const char* s1, const char* s2)
{
return ! strcmp(s1, s2);
@ -287,6 +352,30 @@ char* skip_digits(char* s)
return s;
}
TEST_CASE("util get_word")
{
char orig[10];
strcpy(orig, "two words");
SUBCASE("get first word")
{
char* a = (char*)orig;
char* b = get_word(a);
CHECK(strcmp(a, "words") == 0);
CHECK(strcmp(b, "two") == 0);
}
SUBCASE("get length of first word")
{
int len = strlen(orig);
int len2;
const char* b = nullptr;
get_word(len, orig, len2, b);
CHECK(len2 == 3);
}
}
char* get_word(char*& s)
{
char* w = s;
@ -316,6 +405,17 @@ void get_word(int length, const char* s, int& pwlen, const char*& pw)
pwlen = len;
}
TEST_CASE("util to_upper")
{
char a[10];
strcpy(a, "aBcD");
to_upper(a);
CHECK(strcmp(a, "ABCD") == 0);
std::string b = "aBcD";
CHECK(to_upper(b) == "ABCD");
}
void to_upper(char* s)
{
while ( *s )
@ -363,6 +463,16 @@ unsigned char encode_hex(int h)
return hex[h];
}
TEST_CASE("util strpbrk_n")
{
const char* s = "abcdef";
const char* o = strpbrk_n(5, s, "gc");
CHECK(strcmp(o, "cdef") == 0);
const char* f = strpbrk_n(5, s, "xyz");
CHECK(f == nullptr);
}
// Same as strpbrk except that s is not NUL-terminated, but limited by
// len. Note that '\0' is always implicitly contained in charset.
const char* strpbrk_n(size_t len, const char* s, const char* charset)
@ -375,6 +485,20 @@ const char* strpbrk_n(size_t len, const char* s, const char* charset)
}
#ifndef HAVE_STRCASESTR
TEST_CASE("util strcasestr")
{
const char* s = "this is a string";
const char* out = strcasestr(s, "is");
CHECK(strcmp(out, "is a string") == 0);
const char* out2 = strcasestr(s, "IS");
CHECK(strcmp(out2, "is a string") == 0);
const char* out3 = strcasestr(s, "not there");
CHECK(strcmp(out2, s) == 0);
}
// This code is derived from software contributed to BSD by Chris Torek.
char* strcasestr(const char* s, const char* find)
{
@ -401,6 +525,22 @@ char* strcasestr(const char* s, const char* find)
}
#endif
TEST_CASE("util atoi_n")
{
const char* dec = "12345";
int val;
CHECK(atoi_n(strlen(dec), dec, nullptr, 10, val) == 1);
CHECK(val == 12345);
const char* hex = "12AB";
CHECK(atoi_n(strlen(hex), hex, nullptr, 16, val) == 1);
CHECK(val == 0x12AB);
const char* fail = "XYZ";
CHECK(atoi_n(strlen(fail), fail, nullptr, 10, val) == 0);
}
template<class T> int atoi_n(int len, const char* s, const char** end, int base, T& result)
{
T n = 0;
@ -453,6 +593,15 @@ template int atoi_n<uint32_t>(int len, const char* s, const char** end, int base
template int atoi_n<int64_t>(int len, const char* s, const char** end, int base, int64_t& result);
template int atoi_n<uint64_t>(int len, const char* s, const char** end, int base, uint64_t& result);
TEST_CASE("util uitoa_n")
{
int val = 12345;
char str[20];
const char* result = uitoa_n(val, str, 20, 10, "pref: ");
// TODO: i'm not sure this is the correct output. was it supposed to reverse the digits?
CHECK(strcmp(str, "pref: 54321") == 0);
}
char* uitoa_n(uint64_t value, char* str, int n, int base, const char* prefix)
{
static char dig[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
@ -486,6 +635,21 @@ char* uitoa_n(uint64_t value, char* str, int n, int base, const char* prefix)
return str;
}
TEST_CASE("util strstr_n")
{
const u_char* s = reinterpret_cast<const u_char*>("this is a string");
int out = strstr_n(16, s, 3, reinterpret_cast<const u_char*>("str"));
CHECK(out == 10);
out = strstr_n(16, s, 17, reinterpret_cast<const u_char*>("is"));
CHECK(out == -1);
out = strstr_n(16, s, 2, reinterpret_cast<const u_char*>("IS"));
CHECK(out == -1);
out = strstr_n(16, s, 9, reinterpret_cast<const u_char*>("not there"));
CHECK(out == -1);
}
int strstr_n(const int big_len, const u_char* big,
const int little_len, const u_char* little)
@ -510,6 +674,12 @@ int fputs(int len, const char* s, FILE* fp)
return 0;
}
TEST_CASE("util is_printable")
{
CHECK(is_printable("abcd", 4) == true);
CHECK(is_printable("ab\0d", 4) == false);
}
bool is_printable(const char* s, int len)
{
while ( --len >= 0 )
@ -518,6 +688,15 @@ bool is_printable(const char* s, int len)
return true;
}
TEST_CASE("util strtolower")
{
const char* a = "aBcD";
CHECK(strtolower(a) == "abcd");
std::string b = "aBcD";
CHECK(strtolower(b) == "abcd");
}
std::string strtolower(const std::string& s)
{
std::string t = s;
@ -525,6 +704,20 @@ std::string strtolower(const std::string& s)
return t;
}
TEST_CASE("util fmt_bytes")
{
const char* a = "abcd";
const char* af = fmt_bytes(a, 4);
CHECK(strcmp(a, af) == 0);
const char* b = "abc\0abc";
const char* bf = fmt_bytes(b, 7);
CHECK(strcmp(bf, "abc\\x00abc") == 0);
const char* cf = fmt_bytes(a, 3);
CHECK(strcmp(cf, "abc") == 0);
}
const char* fmt_bytes(const char* data, int len)
{
static char buf[1024];
@ -557,14 +750,14 @@ const char* fmt(const char* format, va_list al)
va_list alc;
va_copy(alc, al);
int n = safe_vsnprintf(buf, buf_len, format, al);
int n = vsnprintf(buf, buf_len, format, al);
if ( (unsigned int) n >= buf_len )
{ // Not enough room, grow the buffer.
buf_len = n + 32;
buf = (char*) safe_realloc(buf, buf_len);
n = safe_vsnprintf(buf, buf_len, format, alc);
n = vsnprintf(buf, buf_len, format, alc);
if ( (unsigned int) n >= buf_len )
reporter->InternalError("confusion reformatting in fmt()");
@ -682,6 +875,13 @@ bool is_file(const std::string& path)
return S_ISREG(st.st_mode);
}
TEST_CASE("util strreplace")
{
string s = "this is not a string";
CHECK(strreplace(s, "not", "really") == "this is really a string");
CHECK(strreplace(s, "not ", "") == "this is a string");
}
string strreplace(const string& s, const string& o, const string& n)
{
string r = s;
@ -699,6 +899,18 @@ string strreplace(const string& s, const string& o, const string& n)
return r;
}
TEST_CASE("util strstrip")
{
string s = " abcd";
CHECK(strstrip(s) == "abcd");
s = "abcd ";
CHECK(strstrip(s) == "abcd");
s = " abcd ";
CHECK(strstrip(s) == "abcd");
}
std::string strstrip(std::string s)
{
auto notspace = [](unsigned char c) { return ! std::isspace(c); };
@ -1013,6 +1225,12 @@ string bro_prefixes()
return rval;
}
TEST_CASE("util is_package_loader")
{
CHECK(is_package_loader("/some/path/__load__.zeek") == true);
CHECK(is_package_loader("/some/path/notload.zeek") == false);
}
const array<string, 2> script_extensions = {".zeek", ".bro"};
bool is_package_loader(const string& path)
@ -1072,6 +1290,32 @@ FILE* open_package(string& path, const string& mode)
return 0;
}
TEST_CASE("util path ops")
{
SUBCASE("SafeDirname")
{
SafeDirname d("/this/is/a/path", false);
CHECK(d.result == "/this/is/a");
SafeDirname d2("invalid", false);
CHECK(d2.result == ".");
SafeDirname d3("./filename", false);
CHECK(d2.result == ".");
}
SUBCASE("SafeBasename")
{
SafeBasename b("/this/is/a/path", false);
CHECK(b.result == "path");
CHECK(! b.error);
SafeBasename b2("justafile", false);
CHECK(b2.result == "justafile");
CHECK(! b2.error);
}
}
void SafePathOp::CheckValid(const char* op_result, const char* path,
bool error_aborts)
{
@ -1128,6 +1372,16 @@ void SafeBasename::DoFunc(const string& path, bool error_aborts)
delete [] tmp;
}
TEST_CASE("util implode_string_vector")
{
std::vector<std::string> v = { "a", "b", "c" };
CHECK(implode_string_vector(v, ",") == "a,b,c");
CHECK(implode_string_vector(v, "") == "abc");
v.clear();
CHECK(implode_string_vector(v, ",") == "");
}
string implode_string_vector(const std::vector<std::string>& v,
const std::string& delim)
{
@ -1144,6 +1398,13 @@ string implode_string_vector(const std::vector<std::string>& v,
return rval;
}
TEST_CASE("util flatten_script_name")
{
CHECK(flatten_script_name("script", "some/path") == "some.path.script");
CHECK(flatten_script_name("other/path/__load__.zeek", "some/path") == "some.path.other.path");
CHECK(flatten_script_name("path/to/script", "") == "path.to.script");
}
string flatten_script_name(const string& name, const string& prefix)
{
string rval = prefix;
@ -1164,6 +1425,23 @@ string flatten_script_name(const string& name, const string& prefix)
return rval;
}
TEST_CASE("util tokenize_string")
{
auto v = tokenize_string("/this/is/a/path", "/", nullptr);
CHECK(v->size() == 5);
CHECK(*v == vector<string>({ "", "this", "is", "a", "path" }));
delete v;
std::vector<std::string> v2;
tokenize_string("/this/is/path/2", "/", &v2);
CHECK(v2.size() == 5);
CHECK(v2 == vector<string>({ "", "this", "is", "path", "2" }));
v2.clear();
tokenize_string("/wrong/delim", ",", &v2);
CHECK(v2.size() == 1);
}
vector<string>* tokenize_string(string input, const string& delim,
vector<string>* rval, int limit)
{
@ -1187,6 +1465,13 @@ vector<string>* tokenize_string(string input, const string& delim,
return rval;
}
TEST_CASE("util normalize_path")
{
CHECK(normalize_path("/1/2/3") == "/1/2/3");
CHECK(normalize_path("/1/./2/3") == "/1/2/3");
CHECK(normalize_path("/1/2/../3") == "/1/3");
CHECK(normalize_path("1/2/3/") == "1/2/3");
}
string normalize_path(const string& path)
{
@ -1316,6 +1601,13 @@ static bool ends_with(const std::string& s, const std::string& ending)
return std::equal(ending.rbegin(), ending.rend(), s.rbegin());
}
TEST_CASE("util ends_with")
{
CHECK(ends_with("abcde", "de") == true);
CHECK(ends_with("abcde", "fg") == false);
CHECK(ends_with("abcde", "abcedf") == false);
}
string find_script_file(const string& filename, const string& path_set)
{
vector<string> paths;
@ -1349,7 +1641,7 @@ FILE* rotate_file(const char* name, RecordVal* rotate_info)
char newname[buflen], tmpname[buflen+4];
safe_snprintf(newname, buflen, "%s.%d.%.06f.tmp",
snprintf(newname, buflen, "%s.%d.%.06f.tmp",
name, getpid(), network_time);
newname[buflen-1] = '\0';
strcpy(tmpname, newname);
@ -1822,6 +2114,11 @@ void operator delete[](void* v)
#endif
TEST_CASE("util canonify_name")
{
CHECK(canonify_name("file name") == "FILE_NAME");
}
std::string canonify_name(const std::string& name)
{
unsigned int len = name.size();
@ -1899,6 +2196,13 @@ static string json_escape_byte(char c)
return result;
}
TEST_CASE("util json_escape_utf8")
{
CHECK(json_escape_utf8("string") == "string");
CHECK(json_escape_utf8("string\n") == "string\n");
CHECK(json_escape_utf8("string\x82") == "string\\x82");
}
string json_escape_utf8(const string& val)
{
string result;

View file

@ -517,6 +517,7 @@ inline char* safe_strncpy(char* dest, const char* src, size_t n)
return result;
}
ZEEK_DEPRECATED("Remove in v4.1: Use system snprintf instead")
inline int safe_snprintf(char* str, size_t size, const char* format, ...)
{
va_list al;
@ -528,6 +529,7 @@ inline int safe_snprintf(char* str, size_t size, const char* format, ...)
return result;
}
ZEEK_DEPRECATED("Remove in v4.1: Use system vsnprintf instead")
inline int safe_vsnprintf(char* str, size_t size, const char* format, va_list al)
{
int result = vsnprintf(str, size, format, al);

View file

@ -23,6 +23,7 @@
#include "file_analysis/Manager.h"
#include "iosource/Manager.h"
#include "iosource/Packet.h"
#include "IntrusivePtr.h"
using namespace std;
@ -396,7 +397,7 @@ function terminate%(%): bool
// is false).
static bool prepare_environment(TableVal* tbl, bool set)
{
ListVal* idxs = tbl->ConvertToPureList();
IntrusivePtr<ListVal> idxs{tbl->ConvertToPureList(), false};
for ( int i = 0; i < idxs->Length(); ++i )
{
@ -1877,6 +1878,18 @@ function type_name%(t: any%): string
return new StringVal(s);
%}
## Returns: list of command-line arguments (``argv``) used to run Zeek.
function zeek_args%(%): string_vec
%{
auto sv = internal_type("string_vec")->AsVectorType();
auto rval = make_intrusive<VectorVal>(sv);
for ( auto i = 0; i < bro_argc; ++i )
rval->Assign(rval->Size(), new StringVal(bro_argv[i]));
return rval.detach();
%}
## Checks whether Zeek reads traffic from one or more network interfaces (as
## opposed to from a network trace in a file). Note that this function returns
## true even after Zeek has stopped reading network traffic, for example due to
@ -1884,7 +1897,7 @@ function type_name%(t: any%): string
##
## Returns: True if reading traffic from a network interface.
##
## .. zeek:see:: reading_traces
## .. zeek:see:: reading_traces packet_sources
function reading_live_traffic%(%): bool
%{
return val_mgr->GetBool(reading_live);
@ -1895,12 +1908,33 @@ function reading_live_traffic%(%): bool
##
## Returns: True if reading traffic from a network trace.
##
## .. zeek:see:: reading_live_traffic
## .. zeek:see:: reading_live_traffic packet_sources
function reading_traces%(%): bool
%{
return val_mgr->GetBool(reading_traces);
%}
## Returns: a list of packet sources being read by Zeek.
##
## .. zeek:see:: reading_live_traffic reading_traces
function packet_sources%(%): PacketSourceList
%{
auto ps_type = internal_type("PacketSource")->AsRecordType();
auto psl_type = internal_type("PacketSourceList")->AsVectorType();
auto rval = make_intrusive<VectorVal>(psl_type);
for ( const auto& ps : iosource_mgr->GetPktSrcs() )
{
auto r = make_intrusive<RecordVal>(ps_type);
r->Assign(0, val_mgr->GetBool(ps->IsLive()));
r->Assign(1, new StringVal(ps->Path()));
r->Assign(2, val_mgr->GetInt(ps->LinkType()));
r->Assign(3, val_mgr->GetCount(ps->Netmask()));
rval->Assign(rval->Size(), r.detach());
}
return rval.detach();
%}
## Generates a table of the size of all global variables. The table index is
## the variable name and the value is the variable size in bytes.
@ -1994,6 +2028,19 @@ function lookup_ID%(id: string%) : any
## Returns: A table that describes the fields of a record.
function record_fields%(rec: any%): record_field_table
%{
if ( rec->Type()->Tag() == TYPE_STRING )
{
auto id = global_scope()->Lookup(rec->AsStringVal()->ToStdString());
if ( ! id || ! id->AsType() || id->AsType()->Tag() != TYPE_RECORD )
{
reporter->Error("record_fields string argument does not name a record type");
return new TableVal(internal_type("record_field_table")->AsTableType());
}
return id->AsType()->AsRecordType()->GetRecordFieldsVal();
}
return rec->GetRecordFields();
%}