zeek/src/Func.cc
Robin Sommer 6fa03abdbc Merge remote-tracking branch 'origin/topic/gilbert/plugin-api-tweak'
* origin/topic/gilbert/plugin-api-tweak:
  Updating plugin.hooks baseline so that test succeeds
  Revert spacing change that shouldn't have been included with the previous changeset ... should fix all of the plugin tests save hooks, which needs to be updated.
  More small fixes
  Small fixes
  Incremental
  Re-updating plugin.hooks test to include new argument output (after merge).
  Fixing logic errors in HandlePluginResult
  Updating tests and tweaking HookArgument to include Frame support.
  Incremental commit: implementing a wrapper for the Val class.
  Reverting change to const status of network_time.  Also, see FIXME: in Func.cc / HandlePluginResult ...
  Tweaks to result handling to make things a little more sane.
  Plugin API: minor change (adding parent frame) to support calling methods from hook.  Also declare network time update argument to be const because good practice.

BIT-1270 #merged

Conflicts:
	testing/btest/Baseline/plugins.hooks/output
2015-03-02 18:17:52 -08:00

712 lines
15 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#include <sys/resource.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <sys/param.h>
#include <netdb.h>
#include <unistd.h>
#include <signal.h>
#include <algorithm>
#include "Base64.h"
#include "Stmt.h"
#include "Scope.h"
#include "Net.h"
#include "NetVar.h"
#include "File.h"
#include "Func.h"
#include "Frame.h"
#include "Var.h"
#include "analyzer/protocol/login/Login.h"
#include "Sessions.h"
#include "RE.h"
#include "Serializer.h"
#include "RemoteSerializer.h"
#include "Event.h"
#include "Traverse.h"
#include "Reporter.h"
#include "plugin/Manager.h"
extern RETSIGTYPE sig_handler(int signo);
const Expr* calling_expr = 0;
bool did_builtin_init = false;
vector<Func*> Func::unique_ids;
static const std::pair<bool, Val*> empty_hook_result(false, NULL);
Func::Func() : scope(0), type(0)
{
unique_id = unique_ids.size();
unique_ids.push_back(this);
}
Func::Func(Kind arg_kind) : scope(0), kind(arg_kind), type(0)
{
unique_id = unique_ids.size();
unique_ids.push_back(this);
}
Func::~Func()
{
Unref(type);
}
void Func::AddBody(Stmt* /* new_body */, id_list* /* new_inits */,
int /* new_frame_size */, int /* priority */)
{
Internal("Func::AddBody called");
}
bool Func::Serialize(SerialInfo* info) const
{
return SerialObj::Serialize(info);
}
Func* Func::Unserialize(UnserialInfo* info)
{
Func* f = (Func*) SerialObj::Unserialize(info, SER_FUNC);
// For builtins, we return a reference to the (hopefully) already
// existing function.
if ( f && f->kind == BUILTIN_FUNC )
{
const char* name = ((BuiltinFunc*) f)->Name();
ID* id = global_scope()->Lookup(name);
if ( ! id )
{
info->s->Error(fmt("can't find built-in %s", name));
return 0;
}
if ( ! (id->HasVal() && id->ID_Val()->Type()->Tag() == TYPE_FUNC) )
{
info->s->Error(fmt("ID %s is not a built-in", name));
return 0;
}
Unref(f);
f = id->ID_Val()->AsFunc();
Ref(f);
}
return f;
}
bool Func::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_FUNC, BroObj);
if ( ! SERIALIZE(int(bodies.size())) )
return false;
for ( unsigned int i = 0; i < bodies.size(); ++i )
{
if ( ! bodies[i].stmts->Serialize(info) )
return false;
if ( ! SERIALIZE(bodies[i].priority) )
return false;
}
if ( ! SERIALIZE(char(kind) ) )
return false;
if ( ! type->Serialize(info) )
return false;
if ( ! SERIALIZE(Name()) )
return false;
// We don't serialize scope as only global functions are considered here
// anyway.
return true;
}
bool Func::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroObj);
int len;
if ( ! UNSERIALIZE(&len) )
return false;
while ( len-- )
{
Body b;
b.stmts = Stmt::Unserialize(info);
if ( ! b.stmts )
return false;
if ( ! UNSERIALIZE(&b.priority) )
return false;
bodies.push_back(b);
}
char c;
if ( ! UNSERIALIZE(&c) )
return false;
kind = (Kind) c;
type = BroType::Unserialize(info);
if ( ! type )
return false;
const char* n;
if ( ! UNSERIALIZE_STR(&n, 0) )
return false;
name = n;
delete [] n;
return true;
}
void Func::DescribeDebug(ODesc* d, const val_list* args) const
{
d->Add(Name());
RecordType* func_args = FType()->Args();
if ( args )
{
d->Add("(");
for ( int i = 0; i < args->length(); ++i )
{
// Handle varargs case (more args than formals).
if ( i >= func_args->NumFields() )
{
d->Add("vararg");
d->Add(i - func_args->NumFields());
}
else
d->Add(func_args->FieldName(i));
d->Add(" = '");
(*args)[i]->Describe(d);
if ( i < args->length() - 1 )
d->Add("', ");
else
d->Add("'");
}
d->Add(")");
}
}
TraversalCode Func::Traverse(TraversalCallback* cb) const
{
// FIXME: Make a fake scope for builtins?
Scope* old_scope = cb->current_scope;
cb->current_scope = scope;
TraversalCode tc = cb->PreFunction(this);
HANDLE_TC_STMT_PRE(tc);
// FIXME: Traverse arguments to builtin functions, too.
if ( kind == BRO_FUNC && scope )
{
tc = scope->Traverse(cb);
HANDLE_TC_STMT_PRE(tc);
for ( unsigned int i = 0; i < bodies.size(); ++i )
{
tc = bodies[i].stmts->Traverse(cb);
HANDLE_TC_STMT_PRE(tc);
}
}
tc = cb->PostFunction(this);
cb->current_scope = old_scope;
HANDLE_TC_STMT_POST(tc);
}
std::pair<bool, Val*> Func::HandlePluginResult(std::pair<bool, Val*> plugin_result, val_list* args, function_flavor flavor) const
{
// Helper function factoring out this code from BroFunc:Call() for
// better readability.
if( ! plugin_result.first )
{
if( plugin_result.second )
reporter->InternalError("plugin set processed flag to false but actually returned a value");
// The plugin result hasn't been processed yet (read: fall
// into ::Call method).
return plugin_result;
}
switch ( flavor ) {
case FUNC_FLAVOR_EVENT:
if( plugin_result.second )
reporter->InternalError("plugin returned non-void result for event %s", this->Name());
break;
case FUNC_FLAVOR_HOOK:
if ( plugin_result.second->Type()->Tag() != TYPE_BOOL )
reporter->InternalError("plugin returned non-bool for hook %s", this->Name());
break;
case FUNC_FLAVOR_FUNCTION:
{
BroType* yt = FType()->YieldType();
if ( (! yt) || yt->Tag() == TYPE_VOID )
{
if( plugin_result.second )
reporter->InternalError("plugin returned non-void result for void method %s", this->Name());
}
else if ( plugin_result.second && plugin_result.second->Type()->Tag() != yt->Tag() && yt->Tag() != TYPE_ANY)
{
reporter->InternalError("plugin returned wrong type (got %d, expecting %d) for %s",
plugin_result.second->Type()->Tag(), yt->Tag(), this->Name());
}
break;
}
}
loop_over_list(*args, i)
Unref((*args)[i]);
return plugin_result;
}
BroFunc::BroFunc(ID* arg_id, Stmt* arg_body, id_list* aggr_inits,
int arg_frame_size, int priority)
: Func(BRO_FUNC)
{
name = arg_id->Name();
type = arg_id->Type()->Ref();
frame_size = arg_frame_size;
if ( arg_body )
{
Body b;
b.stmts = AddInits(arg_body, aggr_inits);
b.priority = priority;
bodies.push_back(b);
}
}
BroFunc::~BroFunc()
{
for ( unsigned int i = 0; i < bodies.size(); ++i )
Unref(bodies[i].stmts);
}
int BroFunc::IsPure() const
{
for ( unsigned int i = 0; i < bodies.size(); ++i )
if ( ! bodies[i].stmts->IsPure() )
return 0;
return 1;
}
Val* BroFunc::Call(val_list* args, Frame* parent) const
{
#ifdef PROFILE_BRO_FUNCTIONS
DEBUG_MSG("Function: %s\n", Name());
#endif
SegmentProfiler(segment_logger, location);
if ( sample_logger )
sample_logger->FunctionSeen(this);
std::pair<bool, Val*> plugin_result = PLUGIN_HOOK_WITH_RESULT(HOOK_CALL_FUNCTION, HookCallFunction(this, parent, args), empty_hook_result);
plugin_result = HandlePluginResult(plugin_result, args, Flavor());
if( plugin_result.first )
{
Val *result = plugin_result.second;
return result;
}
if ( bodies.empty() )
{
// Can only happen for events and hooks.
assert(Flavor() == FUNC_FLAVOR_EVENT || Flavor() == FUNC_FLAVOR_HOOK);
loop_over_list(*args, i)
Unref((*args)[i]);
return Flavor() == FUNC_FLAVOR_HOOK ? new Val(true, TYPE_BOOL) : 0;
}
Frame* f = new Frame(frame_size, this, args);
// Hand down any trigger.
if ( parent )
{
f->SetTrigger(parent->GetTrigger());
f->SetCall(parent->GetCall());
}
g_frame_stack.push_back(f); // used for backtracing
if ( g_trace_state.DoTrace() )
{
ODesc d;
DescribeDebug(&d, args);
g_trace_state.LogTrace("%s called: %s\n",
FType()->FlavorString().c_str(), d.Description());
}
loop_over_list(*args, i)
f->SetElement(i, (*args)[i]);
stmt_flow_type flow = FLOW_NEXT;
Val* result = 0;
for ( size_t i = 0; i < bodies.size(); ++i )
{
if ( sample_logger )
sample_logger->LocationSeen(
bodies[i].stmts->GetLocationInfo());
Unref(result);
try
{
result = bodies[i].stmts->Exec(f, flow);
}
catch ( InterpreterException& e )
{
// Already reported, but we continue exec'ing remaining bodies.
continue;
}
if ( f->HasDelayed() )
{
assert(! result);
assert(parent);
parent->SetDelayed();
break;
}
if ( Flavor() == FUNC_FLAVOR_HOOK )
{
// Ignore any return values of hook bodies, final return value
// depends on whether a body returns as a result of break statement.
Unref(result);
result = 0;
if ( flow == FLOW_BREAK )
{
// Short-circuit execution of remaining hook handler bodies.
result = new Val(false, TYPE_BOOL);
break;
}
}
}
if ( Flavor() == FUNC_FLAVOR_HOOK )
{
if ( ! result )
result = new Val(true, TYPE_BOOL);
}
// Warn if the function returns something, but we returned from
// the function without an explicit return, or without a value.
else if ( FType()->YieldType() && FType()->YieldType()->Tag() != TYPE_VOID &&
(flow != FLOW_RETURN /* we fell off the end */ ||
! result /* explicit return with no result */) &&
! f->HasDelayed() )
reporter->Warning("non-void function returns without a value: %s",
Name());
if ( result && g_trace_state.DoTrace() )
{
ODesc d;
result->Describe(&d);
g_trace_state.LogTrace("Function return: %s\n", d.Description());
}
g_frame_stack.pop_back();
Unref(f);
return result;
}
void BroFunc::AddBody(Stmt* new_body, id_list* new_inits, int new_frame_size,
int priority)
{
if ( new_frame_size > frame_size )
frame_size = new_frame_size;
new_body = AddInits(new_body, new_inits);
if ( Flavor() == FUNC_FLAVOR_FUNCTION )
{
// For functions, we replace the old body with the new one.
assert(bodies.size() <= 1);
for ( unsigned int i = 0; i < bodies.size(); ++i )
Unref(bodies[i].stmts);
bodies.clear();
}
Body b;
b.stmts = new_body;
b.priority = priority;
bodies.push_back(b);
sort(bodies.begin(), bodies.end());
}
void BroFunc::Describe(ODesc* d) const
{
d->Add(Name());
d->NL();
d->AddCount(frame_size);
for ( unsigned int i = 0; i < bodies.size(); ++i )
{
bodies[i].stmts->AccessStats(d);
bodies[i].stmts->Describe(d);
}
}
Stmt* BroFunc::AddInits(Stmt* body, id_list* inits)
{
if ( ! inits || inits->length() == 0 )
return body;
StmtList* stmt_series = new StmtList;
stmt_series->Stmts().append(new InitStmt(inits));
stmt_series->Stmts().append(body);
return stmt_series;
}
IMPLEMENT_SERIAL(BroFunc, SER_BRO_FUNC);
bool BroFunc::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_BRO_FUNC, Func);
return SERIALIZE(frame_size);
}
bool BroFunc::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Func);
return UNSERIALIZE(&frame_size);
}
BuiltinFunc::BuiltinFunc(built_in_func arg_func, const char* arg_name,
int arg_is_pure)
: Func(BUILTIN_FUNC)
{
func = arg_func;
name = make_full_var_name(GLOBAL_MODULE_NAME, arg_name);
is_pure = arg_is_pure;
ID* id = lookup_ID(Name(), GLOBAL_MODULE_NAME, false);
if ( ! id )
reporter->InternalError("built-in function %s missing", Name());
if ( id->HasVal() )
reporter->InternalError("built-in function %s multiply defined", Name());
type = id->Type()->Ref();
id->SetVal(new Val(this));
Unref(id);
}
BuiltinFunc::~BuiltinFunc()
{
}
int BuiltinFunc::IsPure() const
{
return is_pure;
}
Val* BuiltinFunc::Call(val_list* args, Frame* parent) const
{
#ifdef PROFILE_BRO_FUNCTIONS
DEBUG_MSG("Function: %s\n", Name());
#endif
SegmentProfiler(segment_logger, Name());
if ( sample_logger )
sample_logger->FunctionSeen(this);
std::pair<bool, Val*> plugin_result = PLUGIN_HOOK_WITH_RESULT(HOOK_CALL_FUNCTION, HookCallFunction(this, parent, args), empty_hook_result);
plugin_result = HandlePluginResult(plugin_result, args, FUNC_FLAVOR_FUNCTION);
if ( plugin_result.first )
{
Val *result = plugin_result.second;
return result;
}
if ( g_trace_state.DoTrace() )
{
ODesc d;
DescribeDebug(&d, args);
g_trace_state.LogTrace("\tBuiltin Function called: %s\n", d.Description());
}
Val* result = func(parent, args);
loop_over_list(*args, i)
Unref((*args)[i]);
// Don't Unref() args, that's the caller's responsibility.
if ( result && g_trace_state.DoTrace() )
{
ODesc d;
result->Describe(&d);
g_trace_state.LogTrace("\tFunction return: %s\n", d.Description());
}
return result;
}
void BuiltinFunc::Describe(ODesc* d) const
{
d->Add(Name());
d->AddCount(is_pure);
}
IMPLEMENT_SERIAL(BuiltinFunc, SER_BUILTIN_FUNC);
bool BuiltinFunc::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_BUILTIN_FUNC, Func);
return true;
}
bool BuiltinFunc::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Func);
return true;
}
void builtin_error(const char* msg, BroObj* arg)
{
if ( calling_expr )
calling_expr->Error(msg, arg);
else
reporter->Error(msg, arg);
}
#include "bro.bif.func_h"
#include "reporter.bif.func_h"
#include "strings.bif.func_h"
#include "bro.bif.func_def"
#include "reporter.bif.func_def"
#include "strings.bif.func_def"
#include "__all__.bif.cc" // Autogenerated for compiling in the bif_target() code.
#include "__all__.bif.register.cc" // Autogenerated for compiling in the bif_target() code.
void init_builtin_funcs()
{
bro_resources = internal_type("bro_resources")->AsRecordType();
net_stats = internal_type("NetStats")->AsRecordType();
matcher_stats = internal_type("matcher_stats")->AsRecordType();
var_sizes = internal_type("var_sizes")->AsTableType();
gap_info = internal_type("gap_info")->AsRecordType();
#include "bro.bif.func_init"
#include "reporter.bif.func_init"
#include "strings.bif.func_init"
did_builtin_init = true;
}
void init_builtin_funcs_subdirs()
{
#include "__all__.bif.init.cc" // Autogenerated for compiling in the bif_target() code.
}
bool check_built_in_call(BuiltinFunc* f, CallExpr* call)
{
if ( f->TheFunc() != BifFunc::bro_fmt )
return true;
const expr_list& args = call->Args()->Exprs();
if ( args.length() == 0 )
{
// Empty calls are allowed, since you can't just
// use "print;" to get a blank line.
return true;
}
const Expr* fmt_str_arg = args[0];
if ( fmt_str_arg->Type()->Tag() != TYPE_STRING )
{
call->Error("first argument to fmt() needs to be a format string");
return false;
}
Val* fmt_str_val = fmt_str_arg->Eval(0);
if ( fmt_str_val )
{
const char* fmt_str = fmt_str_val->AsStringVal()->CheckString();
int num_fmt = 0;
while ( *fmt_str )
{
if ( *(fmt_str++) != '%' )
continue;
if ( ! *fmt_str )
{
call->Error("format string ends with bare '%'");
return false;
}
if ( *(fmt_str++) != '%' )
// Not a "%%" escape.
++num_fmt;
}
if ( args.length() != num_fmt + 1 )
{
call->Error("mismatch between format string to fmt() and number of arguments passed");
return false;
}
}
return true;
}