mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
The logging systems now supports fields of type set[<atomic_type>].
This commit is contained in:
parent
52c54859b6
commit
c6e3174bc8
7 changed files with 364 additions and 109 deletions
171
src/LogMgr.cc
171
src/LogMgr.cc
|
@ -63,6 +63,21 @@ struct LogMgr::Stream {
|
|||
~Stream();
|
||||
};
|
||||
|
||||
LogVal::~LogVal()
|
||||
{
|
||||
if ( type == TYPE_STRING && present )
|
||||
delete val.string_val;
|
||||
|
||||
if ( type == TYPE_TABLE && present )
|
||||
{
|
||||
for ( int i = 0; i < val.set_val.size; i++ )
|
||||
delete val.set_val.vals[i];
|
||||
|
||||
delete [] val.set_val.vals;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool LogVal::Read(SerializationFormat* fmt)
|
||||
{
|
||||
int ty;
|
||||
|
@ -139,6 +154,23 @@ bool LogVal::Read(SerializationFormat* fmt)
|
|||
return fmt->Read(val.string_val, "string");
|
||||
}
|
||||
|
||||
case TYPE_TABLE:
|
||||
{
|
||||
if ( ! fmt->Read(&val.set_val.size, "set_size") )
|
||||
return false;
|
||||
|
||||
val.set_val.vals = new LogVal* [val.set_val.size];
|
||||
|
||||
for ( int i = 0; i < val.set_val.size; ++i )
|
||||
{
|
||||
val.set_val.vals[i] = new LogVal;
|
||||
if ( ! val.set_val.vals[i]->Read(fmt) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
internal_error(::fmt("unsupported type %s in LogVal::Write", type_name(type)));
|
||||
}
|
||||
|
@ -205,6 +237,20 @@ bool LogVal::Write(SerializationFormat* fmt) const
|
|||
case TYPE_STRING:
|
||||
return fmt->Write(*val.string_val, "string");
|
||||
|
||||
case TYPE_TABLE:
|
||||
{
|
||||
if ( ! fmt->Write(val.set_val.size, "set_size") )
|
||||
return false;
|
||||
|
||||
for ( int i = 0; i < val.set_val.size; ++i )
|
||||
{
|
||||
if ( ! val.set_val.vals[i]->Write(fmt) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
internal_error(::fmt("unsupported type %s in LogVal::REad", type_name(type)));
|
||||
}
|
||||
|
@ -402,14 +448,18 @@ bool LogMgr::TraverseRecord(Filter* filter, RecordType* rt, TableVal* include, T
|
|||
// Recurse.
|
||||
if ( ! TraverseRecord(filter, t->AsRecordType(), include, exclude, new_path, new_indices) )
|
||||
return false;
|
||||
|
||||
continue;
|
||||
}
|
||||
else
|
||||
else if ( t->Tag() == TYPE_TABLE && t->AsTableType()->IsSet() )
|
||||
{
|
||||
// That's ok, handle it with all the other types below.
|
||||
}
|
||||
|
||||
else {
|
||||
run_time("unsupported field type for log column");
|
||||
return false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// If include fields are specified, only include if explicitly listed.
|
||||
|
@ -701,6 +751,70 @@ bool LogMgr::Write(EnumVal* id, RecordVal* columns)
|
|||
return true;
|
||||
}
|
||||
|
||||
LogVal* LogMgr::ValToLogVal(Val* val)
|
||||
{
|
||||
LogVal* lval = new LogVal(val->Type()->Tag());
|
||||
|
||||
switch ( lval->type ) {
|
||||
case TYPE_BOOL:
|
||||
case TYPE_INT:
|
||||
case TYPE_ENUM:
|
||||
lval->val.int_val = val->InternalInt();
|
||||
break;
|
||||
|
||||
case TYPE_COUNT:
|
||||
case TYPE_COUNTER:
|
||||
lval->val.uint_val = val->InternalUnsigned();
|
||||
break;
|
||||
|
||||
case TYPE_PORT:
|
||||
lval->val.uint_val = val->AsPortVal()->Port();
|
||||
break;
|
||||
|
||||
case TYPE_SUBNET:
|
||||
lval->val.subnet_val = *val->AsSubNet();
|
||||
break;
|
||||
|
||||
case TYPE_NET:
|
||||
case TYPE_ADDR:
|
||||
{
|
||||
addr_type t = val->AsAddr();
|
||||
copy_addr(&t, &lval->val.addr_val);
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_DOUBLE:
|
||||
case TYPE_TIME:
|
||||
case TYPE_INTERVAL:
|
||||
lval->val.double_val = val->InternalDouble();
|
||||
break;
|
||||
|
||||
case TYPE_STRING:
|
||||
{
|
||||
const BroString* s = val->AsString();
|
||||
lval->val.string_val = new string((const char*) s->Bytes(), s->Len());
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_TABLE:
|
||||
{
|
||||
ListVal* set = val->AsTableVal()->ConvertToPureList();
|
||||
lval->val.set_val.size = set->Length();
|
||||
lval->val.set_val.vals = new LogVal* [lval->val.set_val.size];
|
||||
|
||||
for ( int i = 0; i < lval->val.set_val.size; i++ )
|
||||
lval->val.set_val.vals[i] = ValToLogVal(set->Index(i));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
internal_error("unsupported type for log_write");
|
||||
}
|
||||
|
||||
return lval;
|
||||
}
|
||||
|
||||
LogVal** LogMgr::RecordToFilterVals(Filter* filter, RecordVal* columns)
|
||||
{
|
||||
LogVal** vals = new LogVal*[filter->num_fields];
|
||||
|
@ -727,55 +841,8 @@ LogVal** LogMgr::RecordToFilterVals(Filter* filter, RecordVal* columns)
|
|||
}
|
||||
}
|
||||
|
||||
if ( ! val )
|
||||
continue;
|
||||
|
||||
vals[i] = new LogVal(type);
|
||||
|
||||
switch ( val->Type()->Tag() ) {
|
||||
case TYPE_BOOL:
|
||||
case TYPE_INT:
|
||||
case TYPE_ENUM:
|
||||
vals[i]->val.int_val = val->InternalInt();
|
||||
break;
|
||||
|
||||
case TYPE_COUNT:
|
||||
case TYPE_COUNTER:
|
||||
vals[i]->val.uint_val = val->InternalUnsigned();
|
||||
break;
|
||||
|
||||
case TYPE_PORT:
|
||||
vals[i]->val.uint_val = val->AsPortVal()->Port();
|
||||
break;
|
||||
|
||||
case TYPE_SUBNET:
|
||||
vals[i]->val.subnet_val = *val->AsSubNet();
|
||||
break;
|
||||
|
||||
case TYPE_NET:
|
||||
case TYPE_ADDR:
|
||||
{
|
||||
addr_type t = val->AsAddr();
|
||||
copy_addr(&t, &vals[i]->val.addr_val);
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_DOUBLE:
|
||||
case TYPE_TIME:
|
||||
case TYPE_INTERVAL:
|
||||
vals[i]->val.double_val = val->InternalDouble();
|
||||
break;
|
||||
|
||||
case TYPE_STRING:
|
||||
{
|
||||
const BroString* s = val->AsString();
|
||||
vals[i]->val.string_val = new string((const char*) s->Bytes(), s->Len());
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
internal_error("unsupported type for log_write");
|
||||
}
|
||||
if ( val )
|
||||
vals[i] = ValToLogVal(val);
|
||||
}
|
||||
|
||||
return vals;
|
||||
|
|
|
@ -36,17 +36,20 @@ struct LogVal {
|
|||
|
||||
// The following union is a subset of BroValUnion, including only the
|
||||
// atomic types.
|
||||
union {
|
||||
struct set_t { bro_int_t size; LogVal** vals; };
|
||||
|
||||
union _val {
|
||||
bro_int_t int_val;
|
||||
bro_uint_t uint_val;
|
||||
addr_type addr_val;
|
||||
subnet_type subnet_val;
|
||||
double double_val;
|
||||
string* string_val;
|
||||
set_t set_val;
|
||||
} val;
|
||||
|
||||
LogVal(TypeTag arg_type = TYPE_ERROR, bool arg_present = true) : type(arg_type), present(arg_present) {}
|
||||
~LogVal() { if ( type == TYPE_STRING && present ) delete val.string_val; }
|
||||
~LogVal();
|
||||
|
||||
bool Read(SerializationFormat* fmt);
|
||||
bool Write(SerializationFormat* fmt) const;
|
||||
|
@ -97,6 +100,7 @@ private:
|
|||
struct WriterInfo;
|
||||
|
||||
bool TraverseRecord(Filter* filter, RecordType* rt, TableVal* include, TableVal* exclude, string path, list<int> indices);
|
||||
LogVal* ValToLogVal(Val* val);
|
||||
LogVal** RecordToFilterVals(Filter* filter, RecordVal* columns);
|
||||
Stream* FindStream(EnumVal* id);
|
||||
void RemoveDisabledWriters(Stream* stream);
|
||||
|
|
|
@ -16,6 +16,10 @@ LogWriterAscii::LogWriterAscii()
|
|||
separator = new char[separator_len];
|
||||
memcpy(separator, BifConst::LogAscii::separator->Bytes(), separator_len);
|
||||
|
||||
set_separator_len = BifConst::LogAscii::set_separator->Len();
|
||||
set_separator = new char[set_separator_len];
|
||||
memcpy(set_separator, BifConst::LogAscii::set_separator->Bytes(), set_separator_len);
|
||||
|
||||
empty_field_len = BifConst::LogAscii::empty_field->Len();
|
||||
empty_field = new char[empty_field_len];
|
||||
memcpy(empty_field, BifConst::LogAscii::empty_field->Bytes(), empty_field_len);
|
||||
|
@ -27,6 +31,7 @@ LogWriterAscii::LogWriterAscii()
|
|||
header_prefix_len = BifConst::LogAscii::header_prefix->Len();
|
||||
header_prefix = new char[header_prefix_len];
|
||||
memcpy(header_prefix, BifConst::LogAscii::header_prefix->Bytes(), header_prefix_len);
|
||||
|
||||
}
|
||||
|
||||
LogWriterAscii::~LogWriterAscii()
|
||||
|
@ -35,6 +40,7 @@ LogWriterAscii::~LogWriterAscii()
|
|||
fclose(file);
|
||||
|
||||
delete [] separator;
|
||||
delete [] set_separator;
|
||||
delete [] empty_field;
|
||||
delete [] unset_field;
|
||||
delete [] header_prefix;
|
||||
|
@ -89,6 +95,86 @@ void LogWriterAscii::DoFinish()
|
|||
{
|
||||
}
|
||||
|
||||
bool LogWriterAscii::DoWriteOne(ODesc* desc, LogVal* val, const LogField* field)
|
||||
{
|
||||
if ( ! val->present )
|
||||
{
|
||||
desc->AddN(unset_field, unset_field_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch ( val->type ) {
|
||||
|
||||
case TYPE_BOOL:
|
||||
desc->Add(val->val.int_val ? "T" : "F");
|
||||
break;
|
||||
|
||||
case TYPE_INT:
|
||||
case TYPE_ENUM:
|
||||
desc->Add(val->val.int_val);
|
||||
break;
|
||||
|
||||
case TYPE_COUNT:
|
||||
case TYPE_COUNTER:
|
||||
case TYPE_PORT:
|
||||
desc->Add(val->val.uint_val);
|
||||
break;
|
||||
|
||||
case TYPE_SUBNET:
|
||||
desc->Add(dotted_addr(val->val.subnet_val.net));
|
||||
desc->Add("/");
|
||||
desc->Add(val->val.subnet_val.width);
|
||||
break;
|
||||
|
||||
case TYPE_NET:
|
||||
case TYPE_ADDR:
|
||||
desc->Add(dotted_addr(val->val.addr_val));
|
||||
break;
|
||||
|
||||
case TYPE_DOUBLE:
|
||||
case TYPE_TIME:
|
||||
case TYPE_INTERVAL:
|
||||
desc->Add(val->val.double_val);
|
||||
break;
|
||||
|
||||
case TYPE_STRING:
|
||||
{
|
||||
int size = val->val.string_val->size();
|
||||
if ( size )
|
||||
desc->AddN(val->val.string_val->data(), val->val.string_val->size());
|
||||
else
|
||||
desc->AddN(empty_field, empty_field_len);
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_TABLE:
|
||||
{
|
||||
if ( ! val->val.set_val.size )
|
||||
{
|
||||
desc->AddN(empty_field, empty_field_len);
|
||||
break;
|
||||
}
|
||||
|
||||
for ( int j = 0; j < val->val.set_val.size; j++ )
|
||||
{
|
||||
if ( j > 0 )
|
||||
desc->AddN(set_separator, set_separator_len);
|
||||
|
||||
if ( ! DoWriteOne(desc, val->val.set_val.vals[j], field) )
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Error(Fmt("unsupported field format %d for %s", val->type, field->name.c_str()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LogWriterAscii::DoWrite(int num_fields, const LogField* const * fields, LogVal** vals)
|
||||
{
|
||||
ODesc desc(DESC_READABLE);
|
||||
|
@ -99,63 +185,9 @@ bool LogWriterAscii::DoWrite(int num_fields, const LogField* const * fields, Log
|
|||
if ( i > 0 )
|
||||
desc.AddRaw(separator, separator_len);
|
||||
|
||||
LogVal* val = vals[i];
|
||||
const LogField* field = fields[i];
|
||||
|
||||
if ( ! val->present )
|
||||
{
|
||||
desc.AddN(unset_field, unset_field_len);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ( field->type ) {
|
||||
case TYPE_BOOL:
|
||||
desc.Add(val->val.int_val ? "T" : "F");
|
||||
break;
|
||||
|
||||
case TYPE_INT:
|
||||
case TYPE_ENUM:
|
||||
desc.Add(val->val.int_val);
|
||||
break;
|
||||
|
||||
case TYPE_COUNT:
|
||||
case TYPE_COUNTER:
|
||||
case TYPE_PORT:
|
||||
desc.Add(val->val.uint_val);
|
||||
break;
|
||||
|
||||
case TYPE_SUBNET:
|
||||
desc.Add(dotted_addr(val->val.subnet_val.net));
|
||||
desc.Add("/");
|
||||
desc.Add(val->val.subnet_val.width);
|
||||
break;
|
||||
|
||||
case TYPE_NET:
|
||||
case TYPE_ADDR:
|
||||
desc.Add(dotted_addr(val->val.addr_val));
|
||||
break;
|
||||
|
||||
case TYPE_DOUBLE:
|
||||
case TYPE_TIME:
|
||||
case TYPE_INTERVAL:
|
||||
desc.Add(val->val.double_val);
|
||||
break;
|
||||
|
||||
case TYPE_STRING:
|
||||
{
|
||||
int size = val->val.string_val->size();
|
||||
if ( size )
|
||||
desc.AddN(val->val.string_val->data(), val->val.string_val->size());
|
||||
else
|
||||
desc.AddN(empty_field, empty_field_len);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Error(Fmt("unsupported field format %d for %s", field->type, field->name.c_str()));
|
||||
if ( ! DoWriteOne(&desc, vals[i], fields[i]) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
desc.Add("\n");
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ protected:
|
|||
|
||||
private:
|
||||
bool IsSpecial(string path) { return path.find("/dev/") == 0; }
|
||||
bool DoWriteOne(ODesc* desc, LogVal* val, const LogField* field);
|
||||
|
||||
FILE* file;
|
||||
string fname;
|
||||
|
@ -35,6 +36,9 @@ private:
|
|||
char* separator;
|
||||
int separator_len;
|
||||
|
||||
char* set_separator;
|
||||
int set_separator_len;
|
||||
|
||||
char* empty_field;
|
||||
int empty_field_len;
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ const output_to_stdout: bool;
|
|||
const include_header: bool;
|
||||
const header_prefix: string;
|
||||
const separator: string;
|
||||
const set_separator: string;
|
||||
const empty_field: string;
|
||||
const unset_field: string;
|
||||
|
||||
|
|
90
testing/btest/logging/remote-types.bro
Normal file
90
testing/btest/logging/remote-types.bro
Normal file
|
@ -0,0 +1,90 @@
|
|||
#
|
||||
# @TEST-EXEC: btest-bg-run sender bro --pseudo-realtime %INPUT ../sender.bro
|
||||
# @TEST-EXEC: sleep 1
|
||||
# @TEST-EXEC: btest-bg-run receiver bro --pseudo-realtime %INPUT ../receiver.bro
|
||||
# @TEST-EXEC: sleep 1
|
||||
# @TEST-EXEC: btest-bg-wait -k 1
|
||||
# @TEST-EXEC: btest-diff receiver/ssh.log
|
||||
# @TEST-EXEC: cmp receiver/ssh.log sender/ssh.log
|
||||
|
||||
# Remote version testing all types.
|
||||
|
||||
# This is the common part loaded by both sender and receiver.
|
||||
|
||||
redef LogAscii::empty_field = "EMPTY";
|
||||
|
||||
module SSH;
|
||||
|
||||
export {
|
||||
# Create a new ID for our log stream
|
||||
redef enum Log::ID += { SSH };
|
||||
|
||||
type Log: record {
|
||||
b: bool;
|
||||
i: int;
|
||||
e: Log::ID;
|
||||
c: count;
|
||||
p: port;
|
||||
sn: subnet;
|
||||
n: net;
|
||||
a: addr;
|
||||
d: double;
|
||||
t: time;
|
||||
iv: interval;
|
||||
s: string;
|
||||
sc: set[count];
|
||||
ss: set[string];
|
||||
se: set[string];
|
||||
};
|
||||
}
|
||||
|
||||
global log_ssh: event(rec: Log);
|
||||
|
||||
event bro_init()
|
||||
{
|
||||
Log::create_stream(SSH, [$columns=Log, $ev=log_ssh]);
|
||||
}
|
||||
|
||||
#####
|
||||
|
||||
@TEST-START-FILE sender.bro
|
||||
|
||||
module SSH;
|
||||
|
||||
@load listen-clear
|
||||
|
||||
event remote_connection_handshake_done(p: event_peer)
|
||||
{
|
||||
local empty_set: set[string];
|
||||
|
||||
Log::write(SSH, [
|
||||
$b=T,
|
||||
$i=-42,
|
||||
$e=SSH,
|
||||
$c=21,
|
||||
$p=123/tcp,
|
||||
$sn=10.0.0.1/24,
|
||||
$n=10.0.,
|
||||
$a=1.2.3.4,
|
||||
$d=3.14,
|
||||
$t=network_time(),
|
||||
$iv=100secs,
|
||||
$s="hurz",
|
||||
$sc=set(1,2,3,4),
|
||||
$ss=set("AA", "BB", "CC"),
|
||||
$se=empty_set
|
||||
]);
|
||||
}
|
||||
@TEST-END-FILE
|
||||
|
||||
@TEST-START-FILE receiver.bro
|
||||
|
||||
#####
|
||||
|
||||
@load remote
|
||||
|
||||
redef Remote::destinations += {
|
||||
["foo"] = [$host = 127.0.0.1, $connect=T, $request_logs=T]
|
||||
};
|
||||
|
||||
@TEST-END-FILE
|
57
testing/btest/logging/types.bro
Normal file
57
testing/btest/logging/types.bro
Normal file
|
@ -0,0 +1,57 @@
|
|||
#
|
||||
# @TEST-EXEC: bro %INPUT
|
||||
# @TEST-EXEC: btest-diff ssh.log
|
||||
#
|
||||
# Testing all possible types.
|
||||
|
||||
redef LogAscii::empty_field = "EMPTY";
|
||||
|
||||
module SSH;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { SSH };
|
||||
|
||||
type Log: record {
|
||||
b: bool;
|
||||
i: int;
|
||||
e: Log::ID;
|
||||
c: count;
|
||||
p: port;
|
||||
sn: subnet;
|
||||
n: net;
|
||||
a: addr;
|
||||
d: double;
|
||||
t: time;
|
||||
iv: interval;
|
||||
s: string;
|
||||
sc: set[count];
|
||||
ss: set[string];
|
||||
se: set[string];
|
||||
};
|
||||
}
|
||||
|
||||
event bro_init()
|
||||
{
|
||||
Log::create_stream(SSH, [$columns=Log]);
|
||||
|
||||
local empty_set: set[string];
|
||||
|
||||
Log::write(SSH, [
|
||||
$b=T,
|
||||
$i=-42,
|
||||
$e=SSH,
|
||||
$c=21,
|
||||
$p=123/tcp,
|
||||
$sn=10.0.0.1/24,
|
||||
$n=10.0.,
|
||||
$a=1.2.3.4,
|
||||
$d=3.14,
|
||||
$t=network_time(),
|
||||
$iv=100secs,
|
||||
$s="hurz",
|
||||
$sc=set(1,2,3,4),
|
||||
$ss=set("AA", "BB", "CC"),
|
||||
$se=empty_set
|
||||
]);
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue