mirror of
https://github.com/zeek/zeek.git
synced 2025-10-13 03:58:20 +00:00
Merge branch 'master' into topic/jsiwek/supervisor
This commit is contained in:
commit
6046da9993
314 changed files with 3709 additions and 5389 deletions
|
@ -1 +1 @@
|
|||
Subproject commit 0e1f951b0bcafea63e503957ae005220c24e4b20
|
||||
Subproject commit 2b3206b7add3472ea0736f2841473e11d506a85e
|
|
@ -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();
|
||||
|
|
|
@ -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 = ' ';
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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 ()
|
||||
|
|
|
@ -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;
|
||||
|
|
24
src/Conn.cc
24
src/Conn.cc
|
@ -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 )
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
89
src/Frame.cc
89
src/Frame.cc
|
@ -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?
|
||||
|
|
34
src/Frame.h
34
src/Frame.h
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
99
src/Func.cc
99
src/Func.cc
|
@ -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;
|
||||
}
|
||||
|
|
16
src/Func.h
16
src/Func.h
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
57
src/Stmt.cc
57
src/Stmt.cc
|
@ -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() )
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
67
src/Type.cc
67
src/Type.cc
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
70
src/Val.cc
70
src/Val.cc
|
@ -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();
|
||||
|
|
|
@ -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; }
|
||||
|
|
85
src/Var.cc
85
src/Var.cc
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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*;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -49,6 +49,9 @@ SQLite::~SQLite()
|
|||
|
||||
void SQLite::DoClose()
|
||||
{
|
||||
sqlite3_finalize(st);
|
||||
st = nullptr;
|
||||
|
||||
if ( db != 0 )
|
||||
{
|
||||
sqlite3_close(db);
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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());
|
||||
|
|
121
src/main.cc
121
src/main.cc
|
@ -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;
|
||||
|
|
|
@ -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) ||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
%}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
};
|
||||
|
||||
|
|
310
src/util.cc
310
src/util.cc
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
53
src/zeek.bif
53
src/zeek.bif
|
@ -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();
|
||||
%}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue