mirror of
https://github.com/zeek/zeek.git
synced 2025-10-17 14:08:20 +00:00
Merge branch 'master' into topic/jsiwek/gtp
This commit is contained in:
commit
cc8f20c104
286 changed files with 186014 additions and 951 deletions
|
@ -28,6 +28,7 @@
|
|||
#include "DCE_RPC.h"
|
||||
#include "Gnutella.h"
|
||||
#include "Ident.h"
|
||||
#include "Modbus.h"
|
||||
#include "NCP.h"
|
||||
#include "NetbiosSSN.h"
|
||||
#include "SMB.h"
|
||||
|
@ -130,6 +131,9 @@ const Analyzer::Config Analyzer::analyzer_configs[] = {
|
|||
{ AnalyzerTag::SYSLOG_BINPAC, "SYSLOG_BINPAC",
|
||||
Syslog_Analyzer_binpac::InstantiateAnalyzer,
|
||||
Syslog_Analyzer_binpac::Available, 0, false },
|
||||
{ AnalyzerTag::Modbus, "MODBUS",
|
||||
ModbusTCP_Analyzer::InstantiateAnalyzer,
|
||||
ModbusTCP_Analyzer::Available, 0, false },
|
||||
|
||||
{ AnalyzerTag::AYIYA, "AYIYA",
|
||||
AYIYA_Analyzer::InstantiateAnalyzer,
|
||||
|
|
|
@ -32,6 +32,7 @@ namespace AnalyzerTag {
|
|||
// Application-layer analyzers, binpac-generated.
|
||||
DHCP_BINPAC, DNS_TCP_BINPAC, DNS_UDP_BINPAC,
|
||||
HTTP_BINPAC, SSL, SYSLOG_BINPAC,
|
||||
Modbus,
|
||||
|
||||
// Decapsulation analyzers.
|
||||
AYIYA,
|
||||
|
|
|
@ -71,7 +71,9 @@ void Attr::DescribeReST(ODesc* d) const
|
|||
|
||||
else if ( expr->Type()->Tag() == TYPE_FUNC )
|
||||
{
|
||||
d->Add(":bro:type:`func`");
|
||||
d->Add(":bro:type:`");
|
||||
d->Add(expr->Type()->AsFuncType()->FlavorString());
|
||||
d->Add("`");
|
||||
}
|
||||
|
||||
else
|
||||
|
@ -401,13 +403,13 @@ void Attributes::CheckAttr(Attr* a)
|
|||
|
||||
case ATTR_GROUP:
|
||||
if ( type->Tag() != TYPE_FUNC ||
|
||||
! type->AsFuncType()->IsEvent() )
|
||||
type->AsFuncType()->Flavor() != FUNC_FLAVOR_EVENT )
|
||||
Error("&group only applicable to events");
|
||||
break;
|
||||
|
||||
case ATTR_ERROR_HANDLER:
|
||||
if ( type->Tag() != TYPE_FUNC ||
|
||||
! type->AsFuncType()->IsEvent() )
|
||||
type->AsFuncType()->Flavor() != FUNC_FLAVOR_EVENT )
|
||||
Error("&error_handler only applicable to events");
|
||||
break;
|
||||
|
||||
|
|
|
@ -271,6 +271,7 @@ void BroDoc::WriteInterface(const char* heading, char underline,
|
|||
WriteBroDocObjList(state_vars, isPublic, "State Variables", sub, isShort);
|
||||
WriteBroDocObjList(types, isPublic, "Types", sub, isShort);
|
||||
WriteBroDocObjList(events, isPublic, "Events", sub, isShort);
|
||||
WriteBroDocObjList(hooks, isPublic, "Hooks", sub, isShort);
|
||||
WriteBroDocObjList(functions, isPublic, "Functions", sub, isShort);
|
||||
WriteBroDocObjList(redefs, isPublic, "Redefinitions", sub, isShort);
|
||||
}
|
||||
|
|
26
src/BroDoc.h
26
src/BroDoc.h
|
@ -179,6 +179,30 @@ public:
|
|||
all.push_back(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules documentation of a hook declared by the script.
|
||||
* @param o A pointer to a BroDocObj which contains the internal
|
||||
* Bro language representation of the script hook and
|
||||
* also any associated comments about it.
|
||||
*/
|
||||
void AddHook(const BroDocObj* o)
|
||||
{
|
||||
hooks.push_back(o);
|
||||
all.push_back(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules documentation of a hook handler declared by the script.
|
||||
* @param o A pointer to a BroDocObj which contains the internal
|
||||
* Bro language representation of the script hook handler and
|
||||
* also any associated comments about it.
|
||||
*/
|
||||
void AddHookHandler(const BroDocObj* o)
|
||||
{
|
||||
hook_handlers.push_back(o);
|
||||
all.push_back(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules documentation of a function declared by the script.
|
||||
* @param o A pointer to a BroDocObj which contains the internal
|
||||
|
@ -241,6 +265,8 @@ protected:
|
|||
BroDocObjList notices;
|
||||
BroDocObjList events;
|
||||
BroDocObjList event_handlers;
|
||||
BroDocObjList hooks;
|
||||
BroDocObjList hook_handlers;
|
||||
BroDocObjMap functions;
|
||||
BroDocObjList redefs;
|
||||
|
||||
|
|
|
@ -34,11 +34,15 @@ public:
|
|||
typedef IdxVec::iterator IdxVecIt;
|
||||
typedef IdxVec::const_iterator IdxVecCIt;
|
||||
|
||||
BroString(int arg_final_NUL, byte_vec str, int arg_n);
|
||||
// Constructors creating internal copies of the data passed in.
|
||||
BroString(const u_char* str, int arg_n, int add_NUL);
|
||||
BroString(const char* str);
|
||||
BroString(const string& str);
|
||||
BroString(const BroString& bs);
|
||||
|
||||
// Constructor that takes owernship of the vector passed in.
|
||||
BroString(int arg_final_NUL, byte_vec str, int arg_n);
|
||||
|
||||
BroString();
|
||||
~BroString() { Reset(); }
|
||||
|
||||
|
|
|
@ -218,6 +218,8 @@ binpac_target(ssl.pac
|
|||
ssl-defs.pac ssl-protocol.pac ssl-analyzer.pac)
|
||||
binpac_target(syslog.pac
|
||||
syslog-protocol.pac syslog-analyzer.pac)
|
||||
binpac_target(modbus.pac
|
||||
modbus-protocol.pac modbus-analyzer.pac)
|
||||
|
||||
########################################################################
|
||||
## bro target
|
||||
|
@ -349,6 +351,7 @@ set(bro_SRCS
|
|||
Reporter.cc
|
||||
Login.cc
|
||||
MIME.cc
|
||||
Modbus.cc
|
||||
NCP.cc
|
||||
NFA.cc
|
||||
NFS.cc
|
||||
|
|
171
src/DNS_Mgr.cc
171
src/DNS_Mgr.cc
|
@ -46,13 +46,15 @@ extern int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
|
|||
|
||||
class DNS_Mgr_Request {
|
||||
public:
|
||||
DNS_Mgr_Request(const char* h, int af) { host = copy_string(h); fam = af; }
|
||||
DNS_Mgr_Request(const char* h, int af, bool is_txt)
|
||||
{ host = copy_string(h); fam = af; qtype = is_txt ? 16 : 0; }
|
||||
DNS_Mgr_Request(const IPAddr& a) { addr = a; host = 0; fam = 0; }
|
||||
~DNS_Mgr_Request() { delete [] host; }
|
||||
|
||||
// Returns nil if this was an address request.
|
||||
const char* ReqHost() const { return host; }
|
||||
const IPAddr& ReqAddr() const { return addr; }
|
||||
const bool ReqIsTxt() const { return qtype == 16; }
|
||||
|
||||
int MakeRequest(nb_dns_info* nb_dns);
|
||||
int RequestPending() const { return request_pending; }
|
||||
|
@ -61,7 +63,8 @@ public:
|
|||
|
||||
protected:
|
||||
char* host; // if non-nil, this is a host request
|
||||
int fam; // address family query type for host requests
|
||||
int fam; // address family query type for host requests
|
||||
int qtype; // Query type
|
||||
IPAddr addr;
|
||||
int request_pending;
|
||||
};
|
||||
|
@ -75,7 +78,7 @@ int DNS_Mgr_Request::MakeRequest(nb_dns_info* nb_dns)
|
|||
|
||||
char err[NB_DNS_ERRSIZE];
|
||||
if ( host )
|
||||
return nb_dns_host_request2(nb_dns, host, fam, (void*) this, err) >= 0;
|
||||
return nb_dns_host_request2(nb_dns, host, fam, qtype, (void*) this, err) >= 0;
|
||||
else
|
||||
{
|
||||
const uint32* bytes;
|
||||
|
@ -475,8 +478,8 @@ TableVal* DNS_Mgr::LookupHost(const char* name)
|
|||
// Not found, or priming.
|
||||
switch ( mode ) {
|
||||
case DNS_PRIME:
|
||||
requests.append(new DNS_Mgr_Request(name, AF_INET));
|
||||
requests.append(new DNS_Mgr_Request(name, AF_INET6));
|
||||
requests.append(new DNS_Mgr_Request(name, AF_INET, false));
|
||||
requests.append(new DNS_Mgr_Request(name, AF_INET6, false));
|
||||
return empty_addr_set();
|
||||
|
||||
case DNS_FORCE:
|
||||
|
@ -484,8 +487,8 @@ TableVal* DNS_Mgr::LookupHost(const char* name)
|
|||
return 0;
|
||||
|
||||
case DNS_DEFAULT:
|
||||
requests.append(new DNS_Mgr_Request(name, AF_INET));
|
||||
requests.append(new DNS_Mgr_Request(name, AF_INET6));
|
||||
requests.append(new DNS_Mgr_Request(name, AF_INET, false));
|
||||
requests.append(new DNS_Mgr_Request(name, AF_INET6, false));
|
||||
Resolve();
|
||||
return LookupHost(name);
|
||||
|
||||
|
@ -636,6 +639,7 @@ int DNS_Mgr::Save()
|
|||
|
||||
Save(f, host_mappings);
|
||||
Save(f, addr_mappings);
|
||||
// Save(f, text_mappings); // We don't save the TXT mappings (yet?).
|
||||
|
||||
fclose(f);
|
||||
|
||||
|
@ -704,39 +708,48 @@ void DNS_Mgr::AddResult(DNS_Mgr_Request* dr, struct nb_dns_result* r)
|
|||
new_dm = new DNS_Mapping(dr->ReqHost(), h, ttl);
|
||||
prev_dm = 0;
|
||||
|
||||
HostMap::iterator it = host_mappings.find(dr->ReqHost());
|
||||
if ( it == host_mappings.end() )
|
||||
if ( dr->ReqIsTxt() )
|
||||
{
|
||||
host_mappings[dr->ReqHost()].first =
|
||||
new_dm->Type() == AF_INET ? new_dm : 0;
|
||||
host_mappings[dr->ReqHost()].second =
|
||||
new_dm->Type() == AF_INET ? 0 : new_dm;
|
||||
TextMap::iterator it = text_mappings.find(dr->ReqHost());
|
||||
if ( it == text_mappings.end() )
|
||||
text_mappings[dr->ReqHost()] = new_dm;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if ( new_dm->Type() == AF_INET )
|
||||
HostMap::iterator it = host_mappings.find(dr->ReqHost());
|
||||
if ( it == host_mappings.end() )
|
||||
{
|
||||
prev_dm = it->second.first;
|
||||
it->second.first = new_dm;
|
||||
host_mappings[dr->ReqHost()].first =
|
||||
new_dm->Type() == AF_INET ? new_dm : 0;
|
||||
|
||||
host_mappings[dr->ReqHost()].second =
|
||||
new_dm->Type() == AF_INET ? 0 : new_dm;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_dm = it->second.second;
|
||||
it->second.second = new_dm;
|
||||
if ( new_dm->Type() == AF_INET )
|
||||
{
|
||||
prev_dm = it->second.first;
|
||||
it->second.first = new_dm;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_dm = it->second.second;
|
||||
it->second.second = new_dm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( new_dm->Failed() && prev_dm && prev_dm->Valid() )
|
||||
{
|
||||
// Put previous, valid entry back - CompareMappings
|
||||
// will generate a corresponding warning.
|
||||
if ( prev_dm->Type() == AF_INET )
|
||||
host_mappings[dr->ReqHost()].first = prev_dm;
|
||||
else
|
||||
host_mappings[dr->ReqHost()].second = prev_dm;
|
||||
if ( new_dm->Failed() && prev_dm && prev_dm->Valid() )
|
||||
{
|
||||
// Put previous, valid entry back - CompareMappings
|
||||
// will generate a corresponding warning.
|
||||
if ( prev_dm->Type() == AF_INET )
|
||||
host_mappings[dr->ReqHost()].first = prev_dm;
|
||||
else
|
||||
host_mappings[dr->ReqHost()].second = prev_dm;
|
||||
|
||||
++keep_prev;
|
||||
++keep_prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -928,7 +941,10 @@ TableVal* DNS_Mgr::LookupNameInCache(string name)
|
|||
{
|
||||
HostMap::iterator it = dns_mgr->host_mappings.find(name);
|
||||
if ( it == dns_mgr->host_mappings.end() )
|
||||
{
|
||||
it = dns_mgr->host_mappings.begin();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DNS_Mapping* d4 = it->second.first;
|
||||
DNS_Mapping* d6 = it->second.second;
|
||||
|
@ -951,6 +967,26 @@ TableVal* DNS_Mgr::LookupNameInCache(string name)
|
|||
return tv6;
|
||||
}
|
||||
|
||||
const char* DNS_Mgr::LookupTextInCache(string name)
|
||||
{
|
||||
TextMap::iterator it = dns_mgr->text_mappings.find(name);
|
||||
if ( it == dns_mgr->text_mappings.end() )
|
||||
return 0;
|
||||
|
||||
DNS_Mapping* d = it->second;
|
||||
|
||||
if ( d->Expired() )
|
||||
{
|
||||
dns_mgr->text_mappings.erase(it);
|
||||
delete d;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The escapes in the following strings are to avoid having it
|
||||
// interpreted as a trigraph sequence.
|
||||
return d->names ? d->names[0] : "<\?\?\?>";
|
||||
}
|
||||
|
||||
void DNS_Mgr::AsyncLookupAddr(const IPAddr& host, LookupCallback* callback)
|
||||
{
|
||||
if ( ! did_init )
|
||||
|
@ -1020,6 +1056,35 @@ void DNS_Mgr::AsyncLookupName(string name, LookupCallback* callback)
|
|||
IssueAsyncRequests();
|
||||
}
|
||||
|
||||
void DNS_Mgr::AsyncLookupNameText(string name, LookupCallback* callback)
|
||||
{
|
||||
if ( ! did_init )
|
||||
Init();
|
||||
|
||||
// Do we already know the answer?
|
||||
TableVal* addrs;
|
||||
|
||||
AsyncRequest* req = 0;
|
||||
|
||||
// Have we already a request waiting for this host?
|
||||
AsyncRequestTextMap::iterator i = asyncs_texts.find(name);
|
||||
if ( i != asyncs_texts.end() )
|
||||
req = i->second;
|
||||
else
|
||||
{
|
||||
// A new one.
|
||||
req = new AsyncRequest;
|
||||
req->name = name;
|
||||
req->is_txt = true;
|
||||
asyncs_queued.push_back(req);
|
||||
asyncs_texts.insert(AsyncRequestTextMap::value_type(name, req));
|
||||
}
|
||||
|
||||
req->callbacks.push_back(callback);
|
||||
|
||||
IssueAsyncRequests();
|
||||
}
|
||||
|
||||
void DNS_Mgr::IssueAsyncRequests()
|
||||
{
|
||||
while ( asyncs_queued.size() && asyncs_pending < MAX_PENDING_REQUESTS )
|
||||
|
@ -1036,8 +1101,9 @@ void DNS_Mgr::IssueAsyncRequests()
|
|||
dr = new DNS_Mgr_Request(req->host);
|
||||
else
|
||||
{
|
||||
dr = new DNS_Mgr_Request(req->name.c_str(), AF_INET);
|
||||
dr6 = new DNS_Mgr_Request(req->name.c_str(), AF_INET6);
|
||||
dr = new DNS_Mgr_Request(req->name.c_str(), AF_INET, req->is_txt);
|
||||
if ( ! req->is_txt )
|
||||
dr6 = new DNS_Mgr_Request(req->name.c_str(), AF_INET6, req->is_txt);
|
||||
}
|
||||
|
||||
if ( ! dr->MakeRequest(nb_dns) )
|
||||
|
@ -1109,6 +1175,38 @@ void DNS_Mgr::CheckAsyncAddrRequest(const IPAddr& addr, bool timeout)
|
|||
|
||||
}
|
||||
|
||||
void DNS_Mgr::CheckAsyncTextRequest(const char* host, bool timeout)
|
||||
{
|
||||
// Note that this code is a mirror of that for CheckAsyncAddrRequest.
|
||||
|
||||
AsyncRequestTextMap::iterator i = asyncs_texts.find(host);
|
||||
if ( i != asyncs_texts.end() )
|
||||
{
|
||||
const char* name = LookupTextInCache(host);
|
||||
if ( name )
|
||||
{
|
||||
++successful;
|
||||
i->second->Resolved(name);
|
||||
}
|
||||
|
||||
else if ( timeout )
|
||||
{
|
||||
AsyncRequestTextMap::iterator it = asyncs_texts.begin();
|
||||
++failed;
|
||||
i->second->Timeout();
|
||||
}
|
||||
|
||||
else
|
||||
return;
|
||||
|
||||
asyncs_texts.erase(i);
|
||||
--asyncs_pending;
|
||||
|
||||
// Don't delete the request. That will be done once it
|
||||
// eventually times out.
|
||||
}
|
||||
}
|
||||
|
||||
void DNS_Mgr::CheckAsyncHostRequest(const char* host, bool timeout)
|
||||
{
|
||||
// Note that this code is a mirror of that for CheckAsyncAddrRequest.
|
||||
|
@ -1157,8 +1255,12 @@ void DNS_Mgr::Flush()
|
|||
for ( AddrMap::iterator it2 = addr_mappings.begin(); it2 != addr_mappings.end(); ++it2 )
|
||||
delete it2->second;
|
||||
|
||||
for ( TextMap::iterator it3 = text_mappings.begin(); it3 != text_mappings.end(); ++it3 )
|
||||
delete it3->second;
|
||||
|
||||
host_mappings.clear();
|
||||
addr_mappings.clear();
|
||||
text_mappings.clear();
|
||||
}
|
||||
|
||||
void DNS_Mgr::Process()
|
||||
|
@ -1177,6 +1279,8 @@ void DNS_Mgr::DoProcess(bool flush)
|
|||
|
||||
if ( req->IsAddrReq() )
|
||||
CheckAsyncAddrRequest(req->host, true);
|
||||
else if ( req->is_txt )
|
||||
CheckAsyncTextRequest(req->name.c_str(), true);
|
||||
else
|
||||
CheckAsyncHostRequest(req->name.c_str(), true);
|
||||
|
||||
|
@ -1184,7 +1288,7 @@ void DNS_Mgr::DoProcess(bool flush)
|
|||
delete req;
|
||||
}
|
||||
|
||||
if ( asyncs_addrs.size() == 0 && asyncs_names.size() == 0 )
|
||||
if ( asyncs_addrs.size() == 0 && asyncs_names.size() == 0 && asyncs_texts.size() == 0 )
|
||||
return;
|
||||
|
||||
if ( AnswerAvailable(0) <= 0 )
|
||||
|
@ -1217,6 +1321,8 @@ void DNS_Mgr::DoProcess(bool flush)
|
|||
|
||||
if ( ! dr->ReqHost() )
|
||||
CheckAsyncAddrRequest(dr->ReqAddr(), true);
|
||||
else if ( dr->ReqIsTxt() )
|
||||
CheckAsyncTextRequest(dr->ReqHost(), do_host_timeout);
|
||||
else
|
||||
CheckAsyncHostRequest(dr->ReqHost(), do_host_timeout);
|
||||
|
||||
|
@ -1271,5 +1377,6 @@ void DNS_Mgr::GetStats(Stats* stats)
|
|||
stats->pending = asyncs_pending;
|
||||
stats->cached_hosts = host_mappings.size();
|
||||
stats->cached_addresses = addr_mappings.size();
|
||||
stats->cached_texts = text_mappings.size();
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ public:
|
|||
|
||||
const char* LookupAddrInCache(const IPAddr& addr);
|
||||
TableVal* LookupNameInCache(string name);
|
||||
const char* LookupTextInCache(string name);
|
||||
|
||||
// Support for async lookups.
|
||||
class LookupCallback {
|
||||
|
@ -77,6 +78,7 @@ public:
|
|||
|
||||
void AsyncLookupAddr(const IPAddr& host, LookupCallback* callback);
|
||||
void AsyncLookupName(string name, LookupCallback* callback);
|
||||
void AsyncLookupNameText(string name, LookupCallback* callback);
|
||||
|
||||
struct Stats {
|
||||
unsigned long requests; // These count only async requests.
|
||||
|
@ -85,6 +87,7 @@ public:
|
|||
unsigned long pending;
|
||||
unsigned long cached_hosts;
|
||||
unsigned long cached_addresses;
|
||||
unsigned long cached_texts;
|
||||
};
|
||||
|
||||
void GetStats(Stats* stats);
|
||||
|
@ -106,6 +109,7 @@ protected:
|
|||
|
||||
typedef map<string, pair<DNS_Mapping*, DNS_Mapping*> > HostMap;
|
||||
typedef map<IPAddr, DNS_Mapping*> AddrMap;
|
||||
typedef map<string, DNS_Mapping*> TextMap;
|
||||
void LoadCache(FILE* f);
|
||||
void Save(FILE* f, const AddrMap& m);
|
||||
void Save(FILE* f, const HostMap& m);
|
||||
|
@ -122,6 +126,7 @@ protected:
|
|||
// requested.
|
||||
void CheckAsyncAddrRequest(const IPAddr& addr, bool timeout);
|
||||
void CheckAsyncHostRequest(const char* host, bool timeout);
|
||||
void CheckAsyncTextRequest(const char* host, bool timeout);
|
||||
|
||||
// Process outstanding requests.
|
||||
void DoProcess(bool flush);
|
||||
|
@ -138,6 +143,7 @@ protected:
|
|||
|
||||
HostMap host_mappings;
|
||||
AddrMap addr_mappings;
|
||||
TextMap text_mappings;
|
||||
|
||||
DNS_mgr_request_list requests;
|
||||
|
||||
|
@ -165,8 +171,11 @@ protected:
|
|||
double time;
|
||||
IPAddr host;
|
||||
string name;
|
||||
bool is_txt;
|
||||
CallbackList callbacks;
|
||||
|
||||
AsyncRequest() : time(0.0), is_txt(false) { }
|
||||
|
||||
bool IsAddrReq() const { return name.length() == 0; }
|
||||
|
||||
void Resolved(const char* name)
|
||||
|
@ -210,6 +219,9 @@ protected:
|
|||
typedef map<string, AsyncRequest*> AsyncRequestNameMap;
|
||||
AsyncRequestNameMap asyncs_names;
|
||||
|
||||
typedef map<string, AsyncRequest*> AsyncRequestTextMap;
|
||||
AsyncRequestTextMap asyncs_texts;
|
||||
|
||||
typedef list<AsyncRequest*> QueuedList;
|
||||
QueuedList asyncs_queued;
|
||||
|
||||
|
|
31
src/Expr.cc
31
src/Expr.cc
|
@ -4374,7 +4374,7 @@ bool InExpr::DoUnserialize(UnserialInfo* info)
|
|||
return true;
|
||||
}
|
||||
|
||||
CallExpr::CallExpr(Expr* arg_func, ListExpr* arg_args)
|
||||
CallExpr::CallExpr(Expr* arg_func, ListExpr* arg_args, bool in_hook)
|
||||
: Expr(EXPR_CALL)
|
||||
{
|
||||
func = arg_func;
|
||||
|
@ -4402,8 +4402,33 @@ CallExpr::CallExpr(Expr* arg_func, ListExpr* arg_args)
|
|||
|
||||
if ( ! yield )
|
||||
{
|
||||
Error("event called in expression");
|
||||
SetError();
|
||||
switch ( func_type->AsFuncType()->Flavor() ) {
|
||||
|
||||
case FUNC_FLAVOR_FUNCTION:
|
||||
Error("function has no yield type");
|
||||
SetError();
|
||||
break;
|
||||
|
||||
case FUNC_FLAVOR_EVENT:
|
||||
Error("event called in expression, use event statement instead");
|
||||
SetError();
|
||||
break;
|
||||
|
||||
case FUNC_FLAVOR_HOOK:
|
||||
// It's fine to not have a yield if it's known that the call
|
||||
// is being done from a hook statement.
|
||||
if ( ! in_hook )
|
||||
{
|
||||
Error("hook called in expression, use hook statement instead");
|
||||
SetError();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Error("invalid function flavor");
|
||||
SetError();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
SetType(yield->Ref());
|
||||
|
|
|
@ -959,7 +959,7 @@ protected:
|
|||
|
||||
class CallExpr : public Expr {
|
||||
public:
|
||||
CallExpr(Expr* func, ListExpr* args);
|
||||
CallExpr(Expr* func, ListExpr* args, bool in_hook = false);
|
||||
~CallExpr();
|
||||
|
||||
Expr* Func() const { return func; }
|
||||
|
|
14
src/Func.cc
14
src/Func.cc
|
@ -284,8 +284,8 @@ Val* BroFunc::Call(val_list* args, Frame* parent) const
|
|||
#endif
|
||||
if ( ! bodies.size() )
|
||||
{
|
||||
// Can only happen for events.
|
||||
assert(IsEvent());
|
||||
// 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 0 ;
|
||||
|
@ -309,7 +309,7 @@ Val* BroFunc::Call(val_list* args, Frame* parent) const
|
|||
DescribeDebug(&d, args);
|
||||
|
||||
g_trace_state.LogTrace("%s called: %s\n",
|
||||
IsEvent() ? "event" : "function", d.Description());
|
||||
FType()->FlavorString().c_str(), d.Description());
|
||||
}
|
||||
|
||||
loop_over_list(*args, i)
|
||||
|
@ -348,6 +348,12 @@ Val* BroFunc::Call(val_list* args, Frame* parent) const
|
|||
parent->SetDelayed();
|
||||
break;
|
||||
}
|
||||
|
||||
if ( flow == FLOW_BREAK && Flavor() == FUNC_FLAVOR_HOOK )
|
||||
{
|
||||
// short-circuit execution of remaining hook handler bodies
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Warn if the function returns something, but we returned from
|
||||
|
@ -380,7 +386,7 @@ void BroFunc::AddBody(Stmt* new_body, id_list* new_inits, int new_frame_size,
|
|||
|
||||
new_body = AddInits(new_body, new_inits);
|
||||
|
||||
if ( ! IsEvent() )
|
||||
if ( Flavor() == FUNC_FLAVOR_FUNCTION )
|
||||
{
|
||||
// For functions, we replace the old body with the new one.
|
||||
assert(bodies.size() <= 1);
|
||||
|
|
|
@ -25,7 +25,7 @@ public:
|
|||
virtual ~Func();
|
||||
|
||||
virtual int IsPure() const = 0;
|
||||
int IsEvent() const { return FType()->IsEvent(); }
|
||||
function_flavor Flavor() const { return FType()->Flavor(); }
|
||||
|
||||
struct Body {
|
||||
Stmt* stmts;
|
||||
|
|
13
src/ID.cc
13
src/ID.cc
|
@ -107,7 +107,8 @@ void ID::SetVal(Val* v, Opcode op, bool arg_weak_ref)
|
|||
#endif
|
||||
|
||||
if ( type && val &&
|
||||
type->Tag() == TYPE_FUNC && type->AsFuncType()->IsEvent() )
|
||||
type->Tag() == TYPE_FUNC &&
|
||||
type->AsFuncType()->Flavor() == FUNC_FLAVOR_EVENT )
|
||||
{
|
||||
EventHandler* handler = event_registry->Lookup(name);
|
||||
if ( ! handler )
|
||||
|
@ -657,11 +658,19 @@ void ID::DescribeReSTShort(ODesc* d) const
|
|||
break;
|
||||
|
||||
case TYPE_FUNC:
|
||||
d->Add(type->AsFuncType()->IsEvent() ? "event" : type_name(t));
|
||||
d->Add(type->AsFuncType()->FlavorString());
|
||||
break;
|
||||
|
||||
case TYPE_ENUM:
|
||||
if ( is_type )
|
||||
d->Add(type_name(t));
|
||||
else
|
||||
d->Add(type->AsEnumType()->Name().c_str());
|
||||
break;
|
||||
|
||||
default:
|
||||
d->Add(type_name(t));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -248,10 +248,10 @@ IPPrefix::IPPrefix(const in6_addr& in6, uint8_t length)
|
|||
prefix.Mask(this->length);
|
||||
}
|
||||
|
||||
IPPrefix::IPPrefix(const IPAddr& addr, uint8_t length)
|
||||
IPPrefix::IPPrefix(const IPAddr& addr, uint8_t length, bool len_is_v6_relative)
|
||||
: prefix(addr)
|
||||
{
|
||||
if ( prefix.GetFamily() == IPv4 )
|
||||
if ( prefix.GetFamily() == IPv4 && ! len_is_v6_relative )
|
||||
{
|
||||
if ( length > 32 )
|
||||
reporter->InternalError("Bad IPAddr(v4) IPPrefix length : %d",
|
||||
|
|
44
src/IPAddr.h
44
src/IPAddr.h
|
@ -342,6 +342,21 @@ public:
|
|||
return memcmp(&addr1.in6, &addr2.in6, sizeof(in6_addr)) < 0;
|
||||
}
|
||||
|
||||
friend bool operator<=(const IPAddr& addr1, const IPAddr& addr2)
|
||||
{
|
||||
return addr1 < addr2 || addr1 == addr2;
|
||||
}
|
||||
|
||||
friend bool operator>=(const IPAddr& addr1, const IPAddr& addr2)
|
||||
{
|
||||
return ! ( addr1 < addr2 );
|
||||
}
|
||||
|
||||
friend bool operator>(const IPAddr& addr1, const IPAddr& addr2)
|
||||
{
|
||||
return ! ( addr1 <= addr2 );
|
||||
}
|
||||
|
||||
/** Converts the address into the type used internally by the
|
||||
* inter-thread communication.
|
||||
*/
|
||||
|
@ -481,8 +496,15 @@ public:
|
|||
* @param addr The IP address.
|
||||
*
|
||||
* @param length The prefix length in the range from 0 to 128
|
||||
*
|
||||
* @param len_is_v6_relative Whether \a length is relative to the full
|
||||
* 128 bits of an IPv6 address. If false and \a addr is an IPv4
|
||||
* address, then \a length is expected to range from 0 to 32. If true
|
||||
* \a length is expected to range from 0 to 128 even if \a addr is IPv4,
|
||||
* meaning that the mask is to apply to the IPv4-mapped-IPv6 representation.
|
||||
*/
|
||||
IPPrefix(const IPAddr& addr, uint8_t length);
|
||||
IPPrefix(const IPAddr& addr, uint8_t length,
|
||||
bool len_is_v6_relative = false);
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
|
@ -583,6 +605,11 @@ public:
|
|||
return net1.Prefix() == net2.Prefix() && net1.Length() == net2.Length();
|
||||
}
|
||||
|
||||
friend bool operator!=(const IPPrefix& net1, const IPPrefix& net2)
|
||||
{
|
||||
return ! (net1 == net2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison operator IP prefixes. This defines a well-defined order for
|
||||
* IP prefix. However, the order does not necessarily corresponding to their
|
||||
|
@ -600,6 +627,21 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
friend bool operator<=(const IPPrefix& net1, const IPPrefix& net2)
|
||||
{
|
||||
return net1 < net2 || net1 == net2;
|
||||
}
|
||||
|
||||
friend bool operator>=(const IPPrefix& net1, const IPPrefix& net2)
|
||||
{
|
||||
return ! (net1 < net2 );
|
||||
}
|
||||
|
||||
friend bool operator>(const IPPrefix& net1, const IPPrefix& net2)
|
||||
{
|
||||
return ! ( net1 <= net2 );
|
||||
}
|
||||
|
||||
private:
|
||||
IPAddr prefix; // We store it as an address with the non-prefix bits masked out via Mask().
|
||||
uint8_t length; // The bit length of the prefix relative to full IPv6 addr.
|
||||
|
|
41
src/Modbus.cc
Normal file
41
src/Modbus.cc
Normal file
|
@ -0,0 +1,41 @@
|
|||
|
||||
#include "Modbus.h"
|
||||
#include "TCP_Reassembler.h"
|
||||
|
||||
ModbusTCP_Analyzer::ModbusTCP_Analyzer(Connection* c)
|
||||
: TCP_ApplicationAnalyzer(AnalyzerTag::Modbus, c)
|
||||
{
|
||||
interp = new binpac::ModbusTCP::ModbusTCP_Conn(this);
|
||||
}
|
||||
|
||||
ModbusTCP_Analyzer::~ModbusTCP_Analyzer()
|
||||
{
|
||||
delete interp;
|
||||
}
|
||||
|
||||
void ModbusTCP_Analyzer::Done()
|
||||
{
|
||||
TCP_ApplicationAnalyzer::Done();
|
||||
|
||||
interp->FlowEOF(true);
|
||||
interp->FlowEOF(false);
|
||||
}
|
||||
|
||||
void ModbusTCP_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
|
||||
{
|
||||
TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
|
||||
interp->NewData(orig, data, data + len);
|
||||
}
|
||||
|
||||
void ModbusTCP_Analyzer::Undelivered(int seq, int len, bool orig)
|
||||
{
|
||||
TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
|
||||
interp->NewGap(orig, len);
|
||||
}
|
||||
|
||||
void ModbusTCP_Analyzer::EndpointEOF(bool is_orig)
|
||||
{
|
||||
TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
|
||||
interp->FlowEOF(is_orig);
|
||||
}
|
||||
|
58
src/Modbus.h
Normal file
58
src/Modbus.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
#ifndef MODBUS_H
|
||||
#define MODBUS_H
|
||||
|
||||
#include "TCP.h"
|
||||
#include "modbus_pac.h"
|
||||
|
||||
class ModbusTCP_Analyzer : public TCP_ApplicationAnalyzer {
|
||||
public:
|
||||
ModbusTCP_Analyzer(Connection* conn);
|
||||
virtual ~ModbusTCP_Analyzer();
|
||||
|
||||
virtual void Done();
|
||||
virtual void DeliverStream(int len, const u_char* data, bool orig);
|
||||
|
||||
virtual void Undelivered(int seq, int len, bool orig);
|
||||
virtual void EndpointEOF(bool is_orig);
|
||||
|
||||
static Analyzer* InstantiateAnalyzer(Connection* conn)
|
||||
{ return new ModbusTCP_Analyzer(conn); }
|
||||
|
||||
// Put event names in this function
|
||||
static bool Available()
|
||||
{
|
||||
return modbus_message
|
||||
| modbus_exception
|
||||
| modbus_read_coils_request
|
||||
| modbus_read_coils_response
|
||||
| modbus_read_discrete_inputs_request
|
||||
| modbus_read_discrete_inputs_response
|
||||
| modbus_read_holding_registers_request
|
||||
| modbus_read_holding_registers_response
|
||||
| modbus_read_input_registers_request
|
||||
| modbus_read_input_registers_response
|
||||
| modbus_write_single_coil_request
|
||||
| modbus_write_single_coil_response
|
||||
| modbus_write_single_register_request
|
||||
| modbus_write_single_register_response
|
||||
| modbus_write_multiple_coils_request
|
||||
| modbus_write_multiple_coils_response
|
||||
| modbus_write_multiple_registers_request
|
||||
| modbus_write_multiple_registers_response
|
||||
| modbus_read_file_record_request
|
||||
| modbus_read_file_record_response
|
||||
| modbus_write_file_record_request
|
||||
| modbus_write_file_record_response
|
||||
| modbus_mask_write_register_request
|
||||
| modbus_mask_write_register_response
|
||||
| modbus_read_write_multiple_registers_request
|
||||
| modbus_read_write_multiple_registers_response
|
||||
| modbus_read_fifo_queue_request
|
||||
| modbus_read_fifo_queue_response;
|
||||
}
|
||||
|
||||
protected:
|
||||
binpac::ModbusTCP::ModbusTCP_Conn* interp;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -219,16 +219,35 @@ void PktSrc::Process()
|
|||
// Get protocol being carried from the ethernet frame.
|
||||
protocol = (data[12] << 8) + data[13];
|
||||
|
||||
// MPLS carried over the ethernet frame.
|
||||
if ( protocol == 0x8847 )
|
||||
have_mpls = true;
|
||||
|
||||
// VLAN carried over ethernet frame.
|
||||
else if ( protocol == 0x8100 )
|
||||
switch ( protocol )
|
||||
{
|
||||
data += get_link_header_size(datalink);
|
||||
data += 4; // Skip the vlan header
|
||||
pkt_hdr_size = 0;
|
||||
// MPLS carried over the ethernet frame.
|
||||
case 0x8847:
|
||||
have_mpls = true;
|
||||
break;
|
||||
|
||||
// VLAN carried over the ethernet frame.
|
||||
case 0x8100:
|
||||
data += get_link_header_size(datalink);
|
||||
data += 4; // Skip the vlan header
|
||||
pkt_hdr_size = 0;
|
||||
break;
|
||||
|
||||
// PPPoE carried over the ethernet frame.
|
||||
case 0x8864:
|
||||
data += get_link_header_size(datalink);
|
||||
protocol = (data[6] << 8) + data[7];
|
||||
data += 8; // Skip the PPPoE session and PPP header
|
||||
pkt_hdr_size = 0;
|
||||
|
||||
if ( protocol != 0x0021 && protocol != 0x0057 )
|
||||
{
|
||||
// Neither IPv4 nor IPv6.
|
||||
sessions->Weird("non_ip_packet_in_pppoe_encapsulation", &hdr, data);
|
||||
data = 0;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
|
@ -41,6 +42,23 @@ RuleHdrTest::RuleHdrTest(Prot arg_prot, uint32 arg_offset, uint32 arg_size,
|
|||
level = 0;
|
||||
}
|
||||
|
||||
RuleHdrTest::RuleHdrTest(Prot arg_prot, Comp arg_comp, vector<IPPrefix> arg_v)
|
||||
{
|
||||
prot = arg_prot;
|
||||
offset = 0;
|
||||
size = 0;
|
||||
comp = arg_comp;
|
||||
vals = new maskedvalue_list;
|
||||
prefix_vals = arg_v;
|
||||
sibling = 0;
|
||||
child = 0;
|
||||
pattern_rules = 0;
|
||||
pure_rules = 0;
|
||||
ruleset = new IntSet;
|
||||
id = ++idcounter;
|
||||
level = 0;
|
||||
}
|
||||
|
||||
Val* RuleMatcher::BuildRuleStateValue(const Rule* rule,
|
||||
const RuleEndpointState* state) const
|
||||
{
|
||||
|
@ -63,6 +81,8 @@ RuleHdrTest::RuleHdrTest(RuleHdrTest& h)
|
|||
loop_over_list(*h.vals, i)
|
||||
vals->append(new MaskedValue(*(*h.vals)[i]));
|
||||
|
||||
prefix_vals = h.prefix_vals;
|
||||
|
||||
for ( int j = 0; j < Rule::TYPES; ++j )
|
||||
{
|
||||
loop_over_list(h.psets[j], k)
|
||||
|
@ -114,6 +134,10 @@ bool RuleHdrTest::operator==(const RuleHdrTest& h)
|
|||
(*vals)[i]->mask != (*h.vals)[i]->mask )
|
||||
return false;
|
||||
|
||||
for ( size_t i = 0; i < prefix_vals.size(); ++i )
|
||||
if ( ! (prefix_vals[i] == h.prefix_vals[i]) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -129,6 +153,9 @@ void RuleHdrTest::PrintDebug()
|
|||
fprintf(stderr, " 0x%08x/0x%08x",
|
||||
(*vals)[i]->val, (*vals)[i]->mask);
|
||||
|
||||
for ( size_t i = 0; i < prefix_vals.size(); ++i )
|
||||
fprintf(stderr, " %s", prefix_vals[i].AsString().c_str());
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
|
@ -410,29 +437,129 @@ static inline uint32 getval(const u_char* data, int size)
|
|||
}
|
||||
|
||||
|
||||
// A line which can be inserted into the macros below for debugging
|
||||
// fprintf(stderr, "%.06f %08x & %08x %s %08x\n", network_time, v, (mvals)[i]->mask, #op, (mvals)[i]->val);
|
||||
|
||||
// Evaluate a value list (matches if at least one value matches).
|
||||
#define DO_MATCH_OR( mvals, v, op ) \
|
||||
{ \
|
||||
loop_over_list((mvals), i) \
|
||||
{ \
|
||||
if ( ((v) & (mvals)[i]->mask) op (mvals)[i]->val ) \
|
||||
goto match; \
|
||||
} \
|
||||
goto no_match; \
|
||||
template <typename FuncT>
|
||||
static inline bool match_or(const maskedvalue_list& mvals, uint32 v, FuncT comp)
|
||||
{
|
||||
loop_over_list(mvals, i)
|
||||
{
|
||||
if ( comp(v & mvals[i]->mask, mvals[i]->val) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Evaluate a prefix list (matches if at least one value matches).
|
||||
template <typename FuncT>
|
||||
static inline bool match_or(const vector<IPPrefix>& prefixes, const IPAddr& a,
|
||||
FuncT comp)
|
||||
{
|
||||
for ( size_t i = 0; i < prefixes.size(); ++i )
|
||||
{
|
||||
IPAddr masked(a);
|
||||
masked.Mask(prefixes[i].LengthIPv6());
|
||||
if ( comp(masked, prefixes[i].Prefix()) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Evaluate a value list (doesn't match if any value matches).
|
||||
#define DO_MATCH_NOT_AND( mvals, v, op ) \
|
||||
{ \
|
||||
loop_over_list((mvals), i) \
|
||||
{ \
|
||||
if ( ((v) & (mvals)[i]->mask) op (mvals)[i]->val ) \
|
||||
goto no_match; \
|
||||
} \
|
||||
goto match; \
|
||||
template <typename FuncT>
|
||||
static inline bool match_not_and(const maskedvalue_list& mvals, uint32 v,
|
||||
FuncT comp)
|
||||
{
|
||||
loop_over_list(mvals, i)
|
||||
{
|
||||
if ( comp(v & mvals[i]->mask, mvals[i]->val) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Evaluate a prefix list (doesn't match if any value matches).
|
||||
template <typename FuncT>
|
||||
static inline bool match_not_and(const vector<IPPrefix>& prefixes,
|
||||
const IPAddr& a, FuncT comp)
|
||||
{
|
||||
for ( size_t i = 0; i < prefixes.size(); ++i )
|
||||
{
|
||||
IPAddr masked(a);
|
||||
masked.Mask(prefixes[i].LengthIPv6());
|
||||
if ( comp(masked, prefixes[i].Prefix()) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool compare(const maskedvalue_list& mvals, uint32 v,
|
||||
RuleHdrTest::Comp comp)
|
||||
{
|
||||
switch ( comp ) {
|
||||
case RuleHdrTest::EQ:
|
||||
return match_or(mvals, v, std::equal_to<uint32>());
|
||||
break;
|
||||
|
||||
case RuleHdrTest::NE:
|
||||
return match_not_and(mvals, v, std::equal_to<uint32>());
|
||||
break;
|
||||
|
||||
case RuleHdrTest::LT:
|
||||
return match_or(mvals, v, std::less<uint32>());
|
||||
break;
|
||||
|
||||
case RuleHdrTest::GT:
|
||||
return match_or(mvals, v, std::greater<uint32>());
|
||||
break;
|
||||
|
||||
case RuleHdrTest::LE:
|
||||
return match_or(mvals, v, std::less_equal<uint32>());
|
||||
break;
|
||||
|
||||
case RuleHdrTest::GE:
|
||||
return match_or(mvals, v, std::greater_equal<uint32>());
|
||||
break;
|
||||
|
||||
default:
|
||||
reporter->InternalError("unknown comparison type");
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool compare(const vector<IPPrefix>& prefixes, const IPAddr& a,
|
||||
RuleHdrTest::Comp comp)
|
||||
{
|
||||
switch ( comp ) {
|
||||
case RuleHdrTest::EQ:
|
||||
return match_or(prefixes, a, std::equal_to<IPAddr>());
|
||||
break;
|
||||
|
||||
case RuleHdrTest::NE:
|
||||
return match_not_and(prefixes, a, std::equal_to<IPAddr>());
|
||||
break;
|
||||
|
||||
case RuleHdrTest::LT:
|
||||
return match_or(prefixes, a, std::less<IPAddr>());
|
||||
break;
|
||||
|
||||
case RuleHdrTest::GT:
|
||||
return match_or(prefixes, a, std::greater<IPAddr>());
|
||||
break;
|
||||
|
||||
case RuleHdrTest::LE:
|
||||
return match_or(prefixes, a, std::less_equal<IPAddr>());
|
||||
break;
|
||||
|
||||
case RuleHdrTest::GE:
|
||||
return match_or(prefixes, a, std::greater_equal<IPAddr>());
|
||||
break;
|
||||
|
||||
default:
|
||||
reporter->InternalError("unknown comparison type");
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
RuleEndpointState* RuleMatcher::InitEndpoint(Analyzer* analyzer,
|
||||
|
@ -492,66 +619,54 @@ RuleEndpointState* RuleMatcher::InitEndpoint(Analyzer* analyzer,
|
|||
|
||||
if ( ip )
|
||||
{
|
||||
// Get start of transport layer.
|
||||
const u_char* transport = ip->Payload();
|
||||
|
||||
// Descend the RuleHdrTest tree further.
|
||||
for ( RuleHdrTest* h = hdr_test->child; h;
|
||||
h = h->sibling )
|
||||
{
|
||||
const u_char* data;
|
||||
bool match = false;
|
||||
|
||||
// Evaluate the header test.
|
||||
switch ( h->prot ) {
|
||||
case RuleHdrTest::NEXT:
|
||||
match = compare(*h->vals, ip->NextProto(), h->comp);
|
||||
break;
|
||||
|
||||
case RuleHdrTest::IP:
|
||||
data = (const u_char*) ip->IP4_Hdr();
|
||||
if ( ! ip->IP4_Hdr() )
|
||||
continue;
|
||||
|
||||
match = compare(*h->vals, getval((const u_char*)ip->IP4_Hdr() + h->offset, h->size), h->comp);
|
||||
break;
|
||||
|
||||
case RuleHdrTest::IPv6:
|
||||
if ( ! ip->IP6_Hdr() )
|
||||
continue;
|
||||
|
||||
match = compare(*h->vals, getval((const u_char*)ip->IP6_Hdr() + h->offset, h->size), h->comp);
|
||||
break;
|
||||
|
||||
case RuleHdrTest::ICMP:
|
||||
case RuleHdrTest::ICMPv6:
|
||||
case RuleHdrTest::TCP:
|
||||
case RuleHdrTest::UDP:
|
||||
data = transport;
|
||||
match = compare(*h->vals, getval(ip->Payload() + h->offset, h->size), h->comp);
|
||||
break;
|
||||
|
||||
case RuleHdrTest::IPSrc:
|
||||
match = compare(h->prefix_vals, ip->IPHeaderSrcAddr(), h->comp);
|
||||
break;
|
||||
|
||||
case RuleHdrTest::IPDst:
|
||||
match = compare(h->prefix_vals, ip->IPHeaderDstAddr(), h->comp);
|
||||
break;
|
||||
|
||||
default:
|
||||
data = 0;
|
||||
reporter->InternalError("unknown protocol");
|
||||
break;
|
||||
}
|
||||
|
||||
// ### data can be nil here if it's an
|
||||
// IPv6 packet and we're doing an IP test.
|
||||
if ( ! data )
|
||||
continue;
|
||||
|
||||
// Sorry for the hidden gotos :-)
|
||||
switch ( h->comp ) {
|
||||
case RuleHdrTest::EQ:
|
||||
DO_MATCH_OR(*h->vals, getval(data + h->offset, h->size), ==);
|
||||
|
||||
case RuleHdrTest::NE:
|
||||
DO_MATCH_NOT_AND(*h->vals, getval(data + h->offset, h->size), ==);
|
||||
|
||||
case RuleHdrTest::LT:
|
||||
DO_MATCH_OR(*h->vals, getval(data + h->offset, h->size), <);
|
||||
|
||||
case RuleHdrTest::GT:
|
||||
DO_MATCH_OR(*h->vals, getval(data + h->offset, h->size), >);
|
||||
|
||||
case RuleHdrTest::LE:
|
||||
DO_MATCH_OR(*h->vals, getval(data + h->offset, h->size), <=);
|
||||
|
||||
case RuleHdrTest::GE:
|
||||
DO_MATCH_OR(*h->vals, getval(data + h->offset, h->size), >=);
|
||||
|
||||
default:
|
||||
reporter->InternalError("unknown comparision type");
|
||||
}
|
||||
|
||||
no_match:
|
||||
continue;
|
||||
|
||||
match:
|
||||
tests.append(h);
|
||||
if ( match )
|
||||
tests.append(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1028,7 +1143,7 @@ void RuleMatcher::DumpStateStats(BroFile* f, RuleHdrTest* hdr_test)
|
|||
Rule* r = Rule::rule_table[set->ids[k] - 1];
|
||||
f->Write(fmt("%s ", r->ID()));
|
||||
}
|
||||
|
||||
|
||||
f->Write("\n");
|
||||
}
|
||||
}
|
||||
|
@ -1050,8 +1165,11 @@ static Val* get_bro_val(const char* label)
|
|||
}
|
||||
|
||||
|
||||
// Converts an atomic Val and appends it to the list
|
||||
static bool val_to_maskedval(Val* v, maskedvalue_list* append_to)
|
||||
// Converts an atomic Val and appends it to the list. For subnet types,
|
||||
// if the prefix_vector param isn't null, appending to that is preferred
|
||||
// over appending to the masked val list.
|
||||
static bool val_to_maskedval(Val* v, maskedvalue_list* append_to,
|
||||
vector<IPPrefix>* prefix_vector)
|
||||
{
|
||||
MaskedValue* mval = new MaskedValue;
|
||||
|
||||
|
@ -1071,29 +1189,37 @@ static bool val_to_maskedval(Val* v, maskedvalue_list* append_to)
|
|||
|
||||
case TYPE_SUBNET:
|
||||
{
|
||||
const uint32* n;
|
||||
uint32 m[4];
|
||||
v->AsSubNet().Prefix().GetBytes(&n);
|
||||
v->AsSubNetVal()->Mask().CopyIPv6(m);
|
||||
|
||||
for ( unsigned int i = 0; i < 4; ++i )
|
||||
m[i] = ntohl(m[i]);
|
||||
|
||||
bool is_v4_mask = m[0] == 0xffffffff &&
|
||||
m[1] == m[0] && m[2] == m[0];
|
||||
|
||||
if ( v->AsSubNet().Prefix().GetFamily() == IPv4 &&
|
||||
is_v4_mask )
|
||||
if ( prefix_vector )
|
||||
{
|
||||
mval->val = ntohl(*n);
|
||||
mval->mask = m[3];
|
||||
prefix_vector->push_back(v->AsSubNet());
|
||||
delete mval;
|
||||
return true;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
rules_error("IPv6 subnets not supported");
|
||||
mval->val = 0;
|
||||
mval->mask = 0;
|
||||
const uint32* n;
|
||||
uint32 m[4];
|
||||
v->AsSubNet().Prefix().GetBytes(&n);
|
||||
v->AsSubNetVal()->Mask().CopyIPv6(m);
|
||||
|
||||
for ( unsigned int i = 0; i < 4; ++i )
|
||||
m[i] = ntohl(m[i]);
|
||||
|
||||
bool is_v4_mask = m[0] == 0xffffffff &&
|
||||
m[1] == m[0] && m[2] == m[0];
|
||||
|
||||
|
||||
if ( v->AsSubNet().Prefix().GetFamily() == IPv4 && is_v4_mask )
|
||||
{
|
||||
mval->val = ntohl(*n);
|
||||
mval->mask = m[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
rules_error("IPv6 subnets not supported");
|
||||
mval->val = 0;
|
||||
mval->mask = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1108,7 +1234,8 @@ static bool val_to_maskedval(Val* v, maskedvalue_list* append_to)
|
|||
return true;
|
||||
}
|
||||
|
||||
void id_to_maskedvallist(const char* id, maskedvalue_list* append_to)
|
||||
void id_to_maskedvallist(const char* id, maskedvalue_list* append_to,
|
||||
vector<IPPrefix>* prefix_vector)
|
||||
{
|
||||
Val* v = get_bro_val(id);
|
||||
if ( ! v )
|
||||
|
@ -1118,7 +1245,7 @@ void id_to_maskedvallist(const char* id, maskedvalue_list* append_to)
|
|||
{
|
||||
val_list* vals = v->AsTableVal()->ConvertToPureList()->Vals();
|
||||
loop_over_list(*vals, i )
|
||||
if ( ! val_to_maskedval((*vals)[i], append_to) )
|
||||
if ( ! val_to_maskedval((*vals)[i], append_to, prefix_vector) )
|
||||
{
|
||||
delete_vals(vals);
|
||||
return;
|
||||
|
@ -1128,7 +1255,7 @@ void id_to_maskedvallist(const char* id, maskedvalue_list* append_to)
|
|||
}
|
||||
|
||||
else
|
||||
val_to_maskedval(v, append_to);
|
||||
val_to_maskedval(v, append_to, prefix_vector);
|
||||
}
|
||||
|
||||
char* id_to_str(const char* id)
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
#define sigs_h
|
||||
|
||||
#include <limits.h>
|
||||
#include <vector>
|
||||
|
||||
#include "IPAddr.h"
|
||||
#include "BroString.h"
|
||||
#include "List.h"
|
||||
#include "RE.h"
|
||||
|
@ -59,17 +61,19 @@ declare(PList, BroString);
|
|||
typedef PList(BroString) bstr_list;
|
||||
|
||||
// Get values from Bro's script-level variables.
|
||||
extern void id_to_maskedvallist(const char* id, maskedvalue_list* append_to);
|
||||
extern void id_to_maskedvallist(const char* id, maskedvalue_list* append_to,
|
||||
vector<IPPrefix>* prefix_vector = 0);
|
||||
extern char* id_to_str(const char* id);
|
||||
extern uint32 id_to_uint(const char* id);
|
||||
|
||||
class RuleHdrTest {
|
||||
public:
|
||||
enum Comp { LE, GE, LT, GT, EQ, NE };
|
||||
enum Prot { NOPROT, IP, ICMP, TCP, UDP };
|
||||
enum Prot { NOPROT, IP, IPv6, ICMP, ICMPv6, TCP, UDP, NEXT, IPSrc, IPDst };
|
||||
|
||||
RuleHdrTest(Prot arg_prot, uint32 arg_offset, uint32 arg_size,
|
||||
Comp arg_comp, maskedvalue_list* arg_vals);
|
||||
RuleHdrTest(Prot arg_prot, Comp arg_comp, vector<IPPrefix> arg_v);
|
||||
~RuleHdrTest();
|
||||
|
||||
void PrintDebug();
|
||||
|
@ -86,6 +90,7 @@ private:
|
|||
Prot prot;
|
||||
Comp comp;
|
||||
maskedvalue_list* vals;
|
||||
vector<IPPrefix> prefix_vals; // for use with IPSrc/IPDst comparisons
|
||||
uint32 offset;
|
||||
uint32 size;
|
||||
|
||||
|
|
|
@ -165,6 +165,7 @@ SERIAL_STMT(EVENT_BODY_LIST, 16)
|
|||
SERIAL_STMT(INIT_STMT, 17)
|
||||
SERIAL_STMT(NULL_STMT, 18)
|
||||
SERIAL_STMT(WHEN_STMT, 19)
|
||||
SERIAL_STMT(HOOK_STMT, 20)
|
||||
|
||||
#define SERIAL_TYPE(name, val) SERIAL_CONST(name, val, BRO_TYPE)
|
||||
SERIAL_TYPE(BRO_TYPE, 1)
|
||||
|
|
|
@ -389,23 +389,35 @@ bool Serializer::UnserializeCall(UnserialInfo* info)
|
|||
{
|
||||
if ( info->print )
|
||||
fprintf(info->print, "%s [%.06f] %s(%s)\n",
|
||||
functype->IsEvent() ? "Event" : "Function call",
|
||||
functype->FlavorString().c_str(),
|
||||
time, name, types ? d.Description() : "<ignored>");
|
||||
|
||||
if ( functype->IsEvent() )
|
||||
switch ( functype->Flavor() ) {
|
||||
|
||||
case FUNC_FLAVOR_EVENT:
|
||||
{
|
||||
EventHandler* handler = event_registry->Lookup(name);
|
||||
assert(handler);
|
||||
|
||||
if ( ! info->ignore_callbacks )
|
||||
GotEvent(name, time, handler, args);
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
||||
case FUNC_FLAVOR_FUNCTION:
|
||||
case FUNC_FLAVOR_HOOK:
|
||||
if ( ! info->ignore_callbacks )
|
||||
GotFunctionCall(name, time, id->ID_Val()->AsFunc(), args);
|
||||
break;
|
||||
|
||||
default:
|
||||
reporter->InternalError("invalid function flavor");
|
||||
break;
|
||||
}
|
||||
|
||||
if ( info->ignore_callbacks )
|
||||
delete_vals(args);
|
||||
|
||||
}
|
||||
else
|
||||
delete_vals(args);
|
||||
|
|
|
@ -125,7 +125,7 @@ protected:
|
|||
|
||||
// This will be increased whenever there is an incompatible change
|
||||
// in the data format.
|
||||
static const uint32 DATA_FORMAT_VERSION = 22;
|
||||
static const uint32 DATA_FORMAT_VERSION = 23;
|
||||
|
||||
ChunkedIO* io;
|
||||
|
||||
|
|
49
src/Stmt.cc
49
src/Stmt.cc
|
@ -23,7 +23,7 @@ const char* stmt_name(BroStmtTag t)
|
|||
"print", "event", "expr", "if", "when", "switch",
|
||||
"for", "next", "break", "return", "add", "delete",
|
||||
"list", "bodylist",
|
||||
"<init>",
|
||||
"<init>", "hook",
|
||||
"null",
|
||||
};
|
||||
|
||||
|
@ -933,6 +933,52 @@ bool EventStmt::DoUnserialize(UnserialInfo* info)
|
|||
return event_expr != 0;
|
||||
}
|
||||
|
||||
HookStmt::HookStmt(CallExpr* arg_e) : ExprStmt(STMT_HOOK, arg_e)
|
||||
{
|
||||
call_expr = arg_e;
|
||||
}
|
||||
|
||||
Val* HookStmt::Exec(Frame* f, stmt_flow_type& flow) const
|
||||
{
|
||||
RegisterAccess();
|
||||
|
||||
Val* ret = call_expr->Eval(f);
|
||||
Unref(ret);
|
||||
|
||||
flow = FLOW_NEXT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TraversalCode HookStmt::Traverse(TraversalCallback* cb) const
|
||||
{
|
||||
TraversalCode tc = cb->PreStmt(this);
|
||||
HANDLE_TC_STMT_PRE(tc);
|
||||
|
||||
// call expr is stored in base class's "e" field.
|
||||
tc = e->Traverse(cb);
|
||||
HANDLE_TC_STMT_PRE(tc);
|
||||
|
||||
tc = cb->PostStmt(this);
|
||||
HANDLE_TC_STMT_POST(tc);
|
||||
}
|
||||
|
||||
IMPLEMENT_SERIAL(HookStmt, SER_HOOK_STMT);
|
||||
|
||||
bool HookStmt::DoSerialize(SerialInfo* info) const
|
||||
{
|
||||
DO_SERIALIZE(SER_HOOK_STMT, ExprStmt);
|
||||
return call_expr->Serialize(info);
|
||||
}
|
||||
|
||||
bool HookStmt::DoUnserialize(UnserialInfo* info)
|
||||
{
|
||||
DO_UNSERIALIZE(ExprStmt);
|
||||
|
||||
call_expr = (CallExpr*) Expr::Unserialize(info, EXPR_CALL);
|
||||
return call_expr != 0;
|
||||
}
|
||||
|
||||
ForStmt::ForStmt(id_list* arg_loop_vars, Expr* loop_expr)
|
||||
: ExprStmt(STMT_FOR, loop_expr)
|
||||
{
|
||||
|
@ -1944,6 +1990,7 @@ int same_stmt(const Stmt* s1, const Stmt* s2)
|
|||
case STMT_RETURN:
|
||||
case STMT_EXPR:
|
||||
case STMT_EVENT:
|
||||
case STMT_HOOK:
|
||||
{
|
||||
const ExprStmt* e1 = (const ExprStmt*) s1;
|
||||
const ExprStmt* e2 = (const ExprStmt*) s2;
|
||||
|
|
18
src/Stmt.h
18
src/Stmt.h
|
@ -286,6 +286,24 @@ protected:
|
|||
EventExpr* event_expr;
|
||||
};
|
||||
|
||||
class HookStmt : public ExprStmt {
|
||||
public:
|
||||
HookStmt(CallExpr* e);
|
||||
|
||||
Val* Exec(Frame* f, stmt_flow_type& flow) const;
|
||||
|
||||
TraversalCode Traverse(TraversalCallback* cb) const;
|
||||
|
||||
protected:
|
||||
friend class Stmt;
|
||||
|
||||
HookStmt() { call_expr = 0; }
|
||||
|
||||
DECLARE_SERIAL(HookStmt);
|
||||
|
||||
CallExpr* call_expr;
|
||||
};
|
||||
|
||||
class ForStmt : public ExprStmt {
|
||||
public:
|
||||
ForStmt(id_list* loop_vars, Expr* loop_expr);
|
||||
|
|
|
@ -15,7 +15,7 @@ typedef enum {
|
|||
STMT_RETURN,
|
||||
STMT_ADD, STMT_DELETE,
|
||||
STMT_LIST, STMT_EVENT_BODY_LIST,
|
||||
STMT_INIT,
|
||||
STMT_INIT, STMT_HOOK,
|
||||
STMT_NULL
|
||||
#define NUM_STMTS (int(STMT_NULL) + 1)
|
||||
} BroStmtTag;
|
||||
|
|
88
src/Type.cc
88
src/Type.cc
|
@ -651,12 +651,12 @@ bool SetType::DoUnserialize(UnserialInfo* info)
|
|||
return true;
|
||||
}
|
||||
|
||||
FuncType::FuncType(RecordType* arg_args, BroType* arg_yield, int arg_is_event)
|
||||
FuncType::FuncType(RecordType* arg_args, BroType* arg_yield, function_flavor arg_flavor)
|
||||
: BroType(TYPE_FUNC)
|
||||
{
|
||||
args = arg_args;
|
||||
yield = arg_yield;
|
||||
is_event = arg_is_event;
|
||||
flavor = arg_flavor;
|
||||
|
||||
arg_types = new TypeList();
|
||||
|
||||
|
@ -664,6 +664,25 @@ FuncType::FuncType(RecordType* arg_args, BroType* arg_yield, int arg_is_event)
|
|||
arg_types->Append(args->FieldType(i)->Ref());
|
||||
}
|
||||
|
||||
string FuncType::FlavorString() const
|
||||
{
|
||||
switch ( flavor ) {
|
||||
|
||||
case FUNC_FLAVOR_FUNCTION:
|
||||
return "function";
|
||||
|
||||
case FUNC_FLAVOR_EVENT:
|
||||
return "event";
|
||||
|
||||
case FUNC_FLAVOR_HOOK:
|
||||
return "hook";
|
||||
|
||||
default:
|
||||
reporter->InternalError("Invalid function flavor");
|
||||
return "invalid_func_flavor";
|
||||
}
|
||||
}
|
||||
|
||||
FuncType::~FuncType()
|
||||
{
|
||||
Unref(arg_types);
|
||||
|
@ -698,7 +717,7 @@ void FuncType::Describe(ODesc* d) const
|
|||
{
|
||||
if ( d->IsReadable() )
|
||||
{
|
||||
d->Add(is_event ? "event" : "function");
|
||||
d->Add(FlavorString());
|
||||
d->Add("(");
|
||||
args->DescribeFields(d);
|
||||
d->Add(")");
|
||||
|
@ -712,7 +731,7 @@ void FuncType::Describe(ODesc* d) const
|
|||
else
|
||||
{
|
||||
d->Add(int(Tag()));
|
||||
d->Add(is_event);
|
||||
d->Add(flavor);
|
||||
d->Add(yield != 0);
|
||||
args->DescribeFields(d);
|
||||
if ( yield )
|
||||
|
@ -723,7 +742,7 @@ void FuncType::Describe(ODesc* d) const
|
|||
void FuncType::DescribeReST(ODesc* d) const
|
||||
{
|
||||
d->Add(":bro:type:`");
|
||||
d->Add(is_event ? "event" : "function");
|
||||
d->Add(FlavorString());
|
||||
d->Add("`");
|
||||
d->Add(" (");
|
||||
args->DescribeFieldsReST(d, true);
|
||||
|
@ -755,9 +774,30 @@ bool FuncType::DoSerialize(SerialInfo* info) const
|
|||
|
||||
SERIALIZE_OPTIONAL(yield);
|
||||
|
||||
int ser_flavor = 0;
|
||||
|
||||
switch ( flavor ) {
|
||||
|
||||
case FUNC_FLAVOR_FUNCTION:
|
||||
ser_flavor = 0;
|
||||
break;
|
||||
|
||||
case FUNC_FLAVOR_EVENT:
|
||||
ser_flavor = 1;
|
||||
break;
|
||||
|
||||
case FUNC_FLAVOR_HOOK:
|
||||
ser_flavor = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
reporter->InternalError("Invalid function flavor serialization");
|
||||
break;
|
||||
}
|
||||
|
||||
return args->Serialize(info) &&
|
||||
arg_types->Serialize(info) &&
|
||||
SERIALIZE(is_event);
|
||||
SERIALIZE(ser_flavor);
|
||||
}
|
||||
|
||||
bool FuncType::DoUnserialize(UnserialInfo* info)
|
||||
|
@ -774,7 +814,27 @@ bool FuncType::DoUnserialize(UnserialInfo* info)
|
|||
if ( ! arg_types )
|
||||
return false;
|
||||
|
||||
return UNSERIALIZE(&is_event);
|
||||
int ser_flavor = 0;
|
||||
|
||||
if ( ! UNSERIALIZE(&ser_flavor) )
|
||||
return false;
|
||||
|
||||
switch ( ser_flavor ) {
|
||||
case 0:
|
||||
flavor = FUNC_FLAVOR_FUNCTION;
|
||||
break;
|
||||
case 1:
|
||||
flavor = FUNC_FLAVOR_EVENT;
|
||||
break;
|
||||
case 2:
|
||||
flavor = FUNC_FLAVOR_HOOK;
|
||||
break;
|
||||
default:
|
||||
reporter->InternalError("Invalid function flavor unserialization");
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TypeDecl::TypeDecl(BroType* t, const char* i, attr_list* arg_attrs, bool in_record)
|
||||
|
@ -1202,9 +1262,10 @@ bool FileType::DoUnserialize(UnserialInfo* info)
|
|||
return yield != 0;
|
||||
}
|
||||
|
||||
EnumType::EnumType()
|
||||
EnumType::EnumType(const string& arg_name)
|
||||
: BroType(TYPE_ENUM)
|
||||
{
|
||||
name = arg_name;
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
|
@ -1327,6 +1388,13 @@ const char* EnumType::Lookup(bro_int_t value)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void EnumType::DescribeReST(ODesc* d) const
|
||||
{
|
||||
d->Add(":bro:type:`");
|
||||
d->Add(name.c_str());
|
||||
d->Add("`");
|
||||
}
|
||||
|
||||
void CommentedEnumType::DescribeReST(ODesc* d) const
|
||||
{
|
||||
// create temporary, reverse name map so that enums can be documented
|
||||
|
@ -1595,7 +1663,7 @@ int same_type(const BroType* t1, const BroType* t2, int is_init)
|
|||
const FuncType* ft1 = (const FuncType*) t1;
|
||||
const FuncType* ft2 = (const FuncType*) t2;
|
||||
|
||||
if ( ft1->IsEvent() != ft2->IsEvent() )
|
||||
if ( ft1->Flavor() != ft2->Flavor() )
|
||||
return 0;
|
||||
|
||||
if ( t1->YieldType() || t2->YieldType() )
|
||||
|
@ -1882,7 +1950,7 @@ BroType* merge_types(const BroType* t1, const BroType* t2)
|
|||
BroType* yield = t1->YieldType() ?
|
||||
merge_types(t1->YieldType(), t2->YieldType()) : 0;
|
||||
|
||||
return new FuncType(args->AsRecordType(), yield, ft1->IsEvent());
|
||||
return new FuncType(args->AsRecordType(), yield, ft1->Flavor());
|
||||
}
|
||||
|
||||
case TYPE_RECORD:
|
||||
|
|
34
src/Type.h
34
src/Type.h
|
@ -35,7 +35,11 @@ typedef enum {
|
|||
#define NUM_TYPES (int(TYPE_ERROR) + 1)
|
||||
} TypeTag;
|
||||
|
||||
typedef enum { FUNC_FLAVOR_FUNCTION, FUNC_FLAVOR_EVENT } function_flavor;
|
||||
typedef enum {
|
||||
FUNC_FLAVOR_FUNCTION,
|
||||
FUNC_FLAVOR_EVENT,
|
||||
FUNC_FLAVOR_HOOK
|
||||
} function_flavor;
|
||||
|
||||
typedef enum {
|
||||
TYPE_INTERNAL_VOID,
|
||||
|
@ -350,18 +354,19 @@ protected:
|
|||
|
||||
class FuncType : public BroType {
|
||||
public:
|
||||
FuncType(RecordType* args, BroType* yield, int is_event);
|
||||
FuncType(RecordType* args, BroType* yield, function_flavor f);
|
||||
|
||||
~FuncType();
|
||||
|
||||
RecordType* Args() const { return args; }
|
||||
BroType* YieldType();
|
||||
void SetYieldType(BroType* arg_yield) { yield = arg_yield; }
|
||||
int IsEvent() const { return is_event; }
|
||||
function_flavor Flavor() const { return flavor; }
|
||||
string FlavorString() const;
|
||||
|
||||
// Used to convert a function type to an event type.
|
||||
void ClearYieldType()
|
||||
{ Unref(yield); yield = 0; is_event = 1; }
|
||||
// Used to convert a function type to an event or hook type.
|
||||
void ClearYieldType(function_flavor arg_flav)
|
||||
{ Unref(yield); yield = 0; flavor = arg_flav; }
|
||||
|
||||
int MatchesIndex(ListExpr*& index) const;
|
||||
int CheckArgs(const type_list* args) const;
|
||||
|
@ -374,14 +379,13 @@ public:
|
|||
void DescribeReST(ODesc* d) const;
|
||||
|
||||
protected:
|
||||
FuncType() { args = 0; arg_types = 0; yield = 0; return_value = 0; }
|
||||
FuncType() { args = 0; arg_types = 0; yield = 0; flavor = FUNC_FLAVOR_FUNCTION; }
|
||||
DECLARE_SERIAL(FuncType)
|
||||
|
||||
RecordType* args;
|
||||
TypeList* arg_types;
|
||||
BroType* yield;
|
||||
int is_event;
|
||||
ID* return_value;
|
||||
function_flavor flavor;
|
||||
};
|
||||
|
||||
class TypeType : public BroType {
|
||||
|
@ -497,7 +501,7 @@ protected:
|
|||
|
||||
class EnumType : public BroType {
|
||||
public:
|
||||
EnumType();
|
||||
EnumType(const string& arg_name);
|
||||
~EnumType();
|
||||
|
||||
// The value of this name is next internal counter value, starting
|
||||
|
@ -513,7 +517,12 @@ public:
|
|||
bro_int_t Lookup(const string& module_name, const char* name);
|
||||
const char* Lookup(bro_int_t value); // Returns 0 if not found
|
||||
|
||||
string Name() const { return name; }
|
||||
|
||||
void DescribeReST(ODesc* d) const;
|
||||
|
||||
protected:
|
||||
EnumType() { counter = 0; }
|
||||
DECLARE_SERIAL(EnumType)
|
||||
|
||||
virtual void AddNameInternal(const string& module_name,
|
||||
|
@ -529,11 +538,14 @@ protected:
|
|||
// as a flag to prevent mixing of auto-increment and explicit
|
||||
// enumerator specifications.
|
||||
bro_int_t counter;
|
||||
|
||||
// The name of the enum type is stored for documentation purposes.
|
||||
string name;
|
||||
};
|
||||
|
||||
class CommentedEnumType: public EnumType {
|
||||
public:
|
||||
CommentedEnumType() {}
|
||||
CommentedEnumType(const string& arg_name) : EnumType(arg_name) {}
|
||||
~CommentedEnumType();
|
||||
|
||||
void DescribeReST(ODesc* d) const;
|
||||
|
|
38
src/Var.cc
38
src/Var.cc
|
@ -171,7 +171,9 @@ static void make_var(ID* id, BroType* t, init_class c, Expr* init,
|
|||
|
||||
id->UpdateValAttrs();
|
||||
|
||||
if ( t && t->Tag() == TYPE_FUNC && t->AsFuncType()->IsEvent() )
|
||||
if ( t && t->Tag() == TYPE_FUNC &&
|
||||
(t->AsFuncType()->Flavor() == FUNC_FLAVOR_EVENT ||
|
||||
t->AsFuncType()->Flavor() == FUNC_FLAVOR_HOOK) )
|
||||
{
|
||||
// For events, add a function value (without any body) here so that
|
||||
// we can later access the ID even if no implementations have been
|
||||
|
@ -258,7 +260,7 @@ void add_type(ID* id, BroType* t, attr_list* attr, int /* is_event */)
|
|||
case TYPE_FUNC:
|
||||
tnew = new FuncType(t->AsFuncType()->Args(),
|
||||
t->AsFuncType()->YieldType(),
|
||||
t->AsFuncType()->IsEvent());
|
||||
t->AsFuncType()->Flavor());
|
||||
break;
|
||||
default:
|
||||
SerializationFormat* form = new BinarySerializationFormat();
|
||||
|
@ -292,14 +294,14 @@ void add_type(ID* id, BroType* t, attr_list* attr, int /* is_event */)
|
|||
void begin_func(ID* id, const char* module_name, function_flavor flavor,
|
||||
int is_redef, FuncType* t)
|
||||
{
|
||||
if ( flavor == FUNC_FLAVOR_EVENT )
|
||||
if ( flavor == FUNC_FLAVOR_EVENT || flavor == FUNC_FLAVOR_HOOK )
|
||||
{
|
||||
const BroType* yt = t->YieldType();
|
||||
|
||||
if ( yt && yt->Tag() != TYPE_VOID )
|
||||
id->Error("event cannot yield a value", t);
|
||||
id->Error("event/hook cannot yield a value", t);
|
||||
|
||||
t->ClearYieldType();
|
||||
t->ClearYieldType(flavor);
|
||||
}
|
||||
|
||||
if ( id->Type() )
|
||||
|
@ -313,21 +315,29 @@ void begin_func(ID* id, const char* module_name, function_flavor flavor,
|
|||
|
||||
if ( id->HasVal() )
|
||||
{
|
||||
int id_is_event = id->ID_Val()->AsFunc()->IsEvent();
|
||||
function_flavor id_flavor = id->ID_Val()->AsFunc()->Flavor();
|
||||
|
||||
if ( id_is_event != (flavor == FUNC_FLAVOR_EVENT) )
|
||||
id->Error("inconsistency between event and function", t);
|
||||
if ( id_is_event )
|
||||
{
|
||||
if ( id_flavor != flavor )
|
||||
id->Error("inconsistent function flavor", t);
|
||||
|
||||
switch ( id_flavor ) {
|
||||
|
||||
case FUNC_FLAVOR_EVENT:
|
||||
case FUNC_FLAVOR_HOOK:
|
||||
if ( is_redef )
|
||||
// Clear out value so it will be replaced.
|
||||
id->SetVal(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
|
||||
case FUNC_FLAVOR_FUNCTION:
|
||||
if ( ! id->IsRedefinable() )
|
||||
id->Error("already defined");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
reporter->InternalError("invalid function flavor");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
id->SetType(t);
|
||||
|
|
86
src/bro.bif
86
src/bro.bif
|
@ -11,6 +11,7 @@
|
|||
#include <cmath>
|
||||
#include <sys/stat.h>
|
||||
#include <cstdio>
|
||||
#include <time.h>
|
||||
|
||||
#include "digest.h"
|
||||
#include "Reporter.h"
|
||||
|
@ -2615,15 +2616,15 @@ function to_double%(str: string%): double
|
|||
%{
|
||||
const char* s = str->CheckString();
|
||||
char* end_s;
|
||||
|
||||
|
||||
double d = strtod(s, &end_s);
|
||||
|
||||
|
||||
if ( s[0] == '\0' || end_s[0] != '\0' )
|
||||
{
|
||||
{
|
||||
builtin_error("bad conversion to double", @ARG@[0]);
|
||||
d = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new Val(d, TYPE_DOUBLE);
|
||||
%}
|
||||
|
||||
|
@ -2700,6 +2701,27 @@ function to_port%(s: string%): port
|
|||
return new PortVal(port, TRANSPORT_UNKNOWN);
|
||||
%}
|
||||
|
||||
## Converts a string of bytes (in network byte order) to a :bro:type:`double`.
|
||||
##
|
||||
## s: A string of bytes containing the binary representation of a double value.
|
||||
##
|
||||
## Returns: The double value contained in *s*, or 0 if the conversion
|
||||
## failed.
|
||||
##
|
||||
function bytestring_to_double%(s: string%): double
|
||||
%{
|
||||
if ( s->Len() != sizeof(double) )
|
||||
{
|
||||
builtin_error("bad conversion to double");
|
||||
return new Val(0.0, TYPE_DOUBLE);
|
||||
}
|
||||
|
||||
// See #908 for a discussion of portability.
|
||||
double d;
|
||||
memcpy(&d, s->Bytes(), sizeof(double));
|
||||
return new Val(ntohd(d), TYPE_DOUBLE);
|
||||
%}
|
||||
|
||||
## Converts a reverse pointer name to an address. For example,
|
||||
## ``1.0.168.192.in-addr.arpa`` to ``192.168.0.1``.
|
||||
##
|
||||
|
@ -3285,6 +3307,31 @@ function strftime%(fmt: string, d: time%) : string
|
|||
return new StringVal(buffer);
|
||||
%}
|
||||
|
||||
|
||||
## Parse a textual representation of a date/time value into a ``time`` type value.
|
||||
##
|
||||
## fmt: The format string used to parse the following *d* argument. See ``man strftime``
|
||||
## for the syntax.
|
||||
##
|
||||
## d: The string representing the time.
|
||||
##
|
||||
## Returns: The time value calculated from parsing *d* with *fmt*.
|
||||
function strptime%(fmt: string, d: string%) : time
|
||||
%{
|
||||
const time_t timeval = time_t(NULL);
|
||||
struct tm t = *localtime(&timeval);
|
||||
|
||||
if ( strptime(d->CheckString(), fmt->CheckString(), &t) == NULL )
|
||||
{
|
||||
reporter->Warning("strptime conversion failed: fmt:%s d:%s", fmt->CheckString(), d->CheckString());
|
||||
return new Val(0.0, TYPE_TIME);
|
||||
}
|
||||
|
||||
double ret = mktime(&t);
|
||||
return new Val(ret, TYPE_TIME);
|
||||
%}
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
#
|
||||
# Network Type Processing
|
||||
|
@ -3744,6 +3791,35 @@ function lookup_addr%(host: addr%) : string
|
|||
return 0;
|
||||
%}
|
||||
|
||||
## Issues an asynchronous TEXT DNS lookup and delays the function result.
|
||||
## This function can therefore only be called inside a ``when`` condition,
|
||||
## e.g., ``when ( local h = lookup_hostname_txt("www.bro-ids.org") ) { f(h); }``.
|
||||
##
|
||||
## host: The hostname to lookup.
|
||||
##
|
||||
## Returns: The DNS TXT record associated with *host*.
|
||||
##
|
||||
## .. bro:see:: lookup_hostname
|
||||
function lookup_hostname_txt%(host: string%) : string
|
||||
%{
|
||||
// FIXME: Is should be easy to adapt the function to synchronous
|
||||
// lookups if we're reading a trace.
|
||||
Trigger* trigger = frame->GetTrigger();
|
||||
|
||||
if ( ! trigger)
|
||||
{
|
||||
builtin_error("lookup_hostname_txt() can only be called inside a when-condition");
|
||||
return new StringVal("<error>");
|
||||
}
|
||||
|
||||
frame->SetDelayed();
|
||||
trigger->Hold();
|
||||
|
||||
dns_mgr->AsyncLookupNameText(host->CheckString(),
|
||||
new LookupHostCallback(trigger, frame->GetCall(), true));
|
||||
return 0;
|
||||
%}
|
||||
|
||||
## Issues an asynchronous DNS lookup and delays the function result.
|
||||
## This function can therefore only be called inside a ``when`` condition,
|
||||
## e.g., ``when ( local h = lookup_hostname("www.bro-ids.org") ) { f(h); }``.
|
||||
|
|
297
src/event.bif
297
src/event.bif
|
@ -150,7 +150,7 @@ event new_connection%(c: connection%);
|
|||
## encapsulation value of *e* after this event is raised. If the desired
|
||||
## behavior is to track the latest tunnel encapsulation per-connection,
|
||||
## then a handler of this event should assign *e* to ``c$tunnel`` (which Bro's
|
||||
## default scripts are doing).
|
||||
## default scripts are doing).
|
||||
##
|
||||
## c: The connection whose tunnel/encapsulation changed.
|
||||
##
|
||||
|
@ -6560,6 +6560,301 @@ event netflow_v5_header%(h: nf_v5_header%);
|
|||
## .. bro:see:: netflow_v5_record
|
||||
event netflow_v5_record%(r: nf_v5_record%);
|
||||
|
||||
## Generated for any modbus message regardless if the particular function
|
||||
## is further supported or not.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## is_orig: True if the event is raised for the originator side.
|
||||
event modbus_message%(c: connection, headers: ModbusHeaders, is_orig: bool%);
|
||||
|
||||
## Generated for any modbus exception message.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## code: The exception code.
|
||||
event modbus_exception%(c: connection, headers: ModbusHeaders, code: count%);
|
||||
|
||||
## Generated for a Modbus read coils request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address where of the first coil to be read.
|
||||
##
|
||||
## quantity: The number of coils to be read.
|
||||
event modbus_read_coils_request%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Generated for a Modbus read coils response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## coils: The coil values returned from the device.
|
||||
event modbus_read_coils_response%(c: connection, headers: ModbusHeaders, coils: ModbusCoils%);
|
||||
|
||||
## Generated for a Modbus read discrete inputs request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first coil to be read.
|
||||
##
|
||||
## quantity: The number of coils to be read.
|
||||
event modbus_read_discrete_inputs_request%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Generated for a Modbus read discrete inputs response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## coils: The coil values returned from the device.
|
||||
event modbus_read_discrete_inputs_response%(c: connection, headers: ModbusHeaders, coils: ModbusCoils%);
|
||||
|
||||
## Generated for a Modbus read holding registers request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first register to be read.
|
||||
##
|
||||
## quantity: The number of registers to be read.
|
||||
event modbus_read_holding_registers_request%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Generated for a Modbus read holding registers response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## registers: The register values returned from the device.
|
||||
event modbus_read_holding_registers_response%(c: connection, headers: ModbusHeaders, registers: ModbusRegisters%);
|
||||
|
||||
## Generated for a Modbus read input registers request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first register to be read.
|
||||
##
|
||||
## quantity: The number of registers to be read.
|
||||
event modbus_read_input_registers_request%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Generated for a Modbus read input registers response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## registers: The register values returned from the device.
|
||||
event modbus_read_input_registers_response%(c: connection, headers: ModbusHeaders, registers: ModbusRegisters%);
|
||||
|
||||
## Generated for a Modbus write single coil request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the coil to be written.
|
||||
##
|
||||
## value: The value to be written to the coil.
|
||||
event modbus_write_single_coil_request%(c: connection, headers: ModbusHeaders, address: count, value: bool%);
|
||||
|
||||
## Generated for a Modbus write single coil response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the coil that was written.
|
||||
##
|
||||
## value: The value that was written to the coil.
|
||||
event modbus_write_single_coil_response%(c: connection, headers: ModbusHeaders, address: count, value: bool%);
|
||||
|
||||
## Generated for a Modbus write single register request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the register to be written.
|
||||
##
|
||||
## value: The value to be written to the register.
|
||||
event modbus_write_single_register_request%(c: connection, headers: ModbusHeaders, address: count, value: count%);
|
||||
|
||||
## Generated for a Modbus write single register response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the register that was written.
|
||||
##
|
||||
## value: The value that was written to the register.
|
||||
event modbus_write_single_register_response%(c: connection, headers: ModbusHeaders, address: count, value: count%);
|
||||
|
||||
## Generated for a Modbus write multiple coils request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first coil to be written.
|
||||
##
|
||||
## value: The values to be written to the coils.
|
||||
event modbus_write_multiple_coils_request%(c: connection, headers: ModbusHeaders, start_address: count, coils: ModbusCoils%);
|
||||
|
||||
## Generated for a Modbus write multiple coils response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first coil that was written.
|
||||
##
|
||||
## quantity: The quantity of coils that were written.
|
||||
event modbus_write_multiple_coils_response%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Generated for a Modbus write multiple registers request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first register to be written.
|
||||
##
|
||||
## registers: The values to be written to the registers.
|
||||
event modbus_write_multiple_registers_request%(c: connection, headers: ModbusHeaders, start_address: count, registers: ModbusRegisters%);
|
||||
|
||||
## Generated for a Modbus write multiple registers response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first register that was written.
|
||||
##
|
||||
## quantity: The quantity of registers that were written.
|
||||
event modbus_write_multiple_registers_response%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Generated for a Modbus read file record request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## .. note: This event is incomplete. The information from the data structure is not
|
||||
## yet passed through to the event.
|
||||
event modbus_read_file_record_request%(c: connection, headers: ModbusHeaders%);
|
||||
|
||||
## Generated for a Modbus read file record response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## .. note: This event is incomplete. The information from the data structure is not
|
||||
## yet passed through to the event.
|
||||
event modbus_read_file_record_response%(c: connection, headers: ModbusHeaders%);
|
||||
|
||||
## Generated for a Modbus write file record request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## .. note: This event is incomplete. The information from the data structure is not
|
||||
## yet passed through to the event.
|
||||
event modbus_write_file_record_request%(c: connection, headers: ModbusHeaders%);
|
||||
|
||||
## Generated for a Modbus write file record response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## .. note: This event is incomplete. The information from the data structure is not
|
||||
## yet passed through to the event.
|
||||
event modbus_write_file_record_response%(c: connection, headers: ModbusHeaders%);
|
||||
|
||||
## Generated for a Modbus mask write register request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the register where the masks should be applied.
|
||||
##
|
||||
## and_mask: The value of the logical AND mask to apply to the register.
|
||||
##
|
||||
## or_mask: The value of the logical OR mask to apply to the register.
|
||||
event modbus_mask_write_register_request%(c: connection, headers: ModbusHeaders, address: count, and_mask: count, or_mask: count%);
|
||||
|
||||
## Generated for a Modbus mask write register request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the register where the masks were applied.
|
||||
##
|
||||
## and_mask: The value of the logical AND mask applied register.
|
||||
##
|
||||
## or_mask: The value of the logical OR mask applied to the register.
|
||||
event modbus_mask_write_register_response%(c: connection, headers: ModbusHeaders, address: count, and_mask: count, or_mask: count%);
|
||||
|
||||
## Generated for a Modbus read/write multiple registers request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## read_start_address: The memory address of the first register to be read.
|
||||
##
|
||||
## read_quantity: The number of registers to read.
|
||||
##
|
||||
## write_start_address: The memory address of the first register to be written.
|
||||
##
|
||||
## write_registers: The values to be written to the registers.
|
||||
event modbus_read_write_multiple_registers_request%(c: connection, headers: ModbusHeaders, read_start_address: count, read_quantity: count, write_start_address: count, write_registers: ModbusRegisters%);
|
||||
|
||||
## Generated for a Modbus read/write multiple registers response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## written_registers: The register values read from the registers specified in the request.
|
||||
event modbus_read_write_multiple_registers_response%(c: connection, headers: ModbusHeaders, written_registers: ModbusRegisters%);
|
||||
|
||||
## Generated for a Modbus read FIFO queue request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The address of the FIFO queue to read.
|
||||
event modbus_read_fifo_queue_request%(c: connection, headers: ModbusHeaders, start_address: count%);
|
||||
|
||||
## Generated for a Modbus read FIFO queue response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## fifos: The register values read from the FIFO queue on the device.
|
||||
event modbus_read_fifo_queue_response%(c: connection, headers: ModbusHeaders, fifos: ModbusRegisters%);
|
||||
|
||||
## Raised for informational messages reported via Bro's reporter framework. Such
|
||||
## messages may be generated internally by the event engine and also by other
|
||||
## scripts calling :bro:id:`Reporter::info`.
|
||||
|
|
|
@ -393,7 +393,7 @@ bool Manager::CreateEventStream(RecordVal* fval)
|
|||
|
||||
bool allow_file_func = false;
|
||||
|
||||
if ( ! etype->IsEvent() )
|
||||
if ( etype->Flavor() != FUNC_FLAVOR_EVENT )
|
||||
{
|
||||
reporter->Error("stream event is a function, not an event");
|
||||
return false;
|
||||
|
@ -566,7 +566,7 @@ bool Manager::CreateTableStream(RecordVal* fval)
|
|||
{
|
||||
FuncType* etype = event->FType()->AsFuncType();
|
||||
|
||||
if ( ! etype->IsEvent() )
|
||||
if ( etype->Flavor() != FUNC_FLAVOR_EVENT )
|
||||
{
|
||||
reporter->Error("stream event is a function, not an event");
|
||||
return false;
|
||||
|
|
|
@ -327,7 +327,7 @@ bool Manager::CreateStream(EnumVal* id, RecordVal* sval)
|
|||
// Make sure the event is prototyped as expected.
|
||||
FuncType* etype = event->FType()->AsFuncType();
|
||||
|
||||
if ( ! etype->IsEvent() )
|
||||
if ( etype->Flavor() != FUNC_FLAVOR_EVENT )
|
||||
{
|
||||
reporter->Error("stream event is a function, not an event");
|
||||
return false;
|
||||
|
|
|
@ -19,6 +19,7 @@ Ascii::Ascii(WriterFrontend* frontend) : WriterBackend(frontend)
|
|||
{
|
||||
fd = 0;
|
||||
ascii_done = false;
|
||||
only_single_header_row = false;
|
||||
|
||||
output_to_stdout = BifConst::LogAscii::output_to_stdout;
|
||||
include_meta = BifConst::LogAscii::include_meta;
|
||||
|
@ -80,7 +81,7 @@ void Ascii::CloseFile(double t)
|
|||
if ( ! fd )
|
||||
return;
|
||||
|
||||
if ( include_meta )
|
||||
if ( include_meta && ! only_single_header_row )
|
||||
WriteHeaderField("close", Timestamp(0));
|
||||
|
||||
safe_close(fd);
|
||||
|
@ -108,29 +109,29 @@ bool Ascii::DoInit(const WriterInfo& info, int num_fields, const Field* const *
|
|||
return false;
|
||||
}
|
||||
|
||||
for ( WriterInfo::config_map::const_iterator i = info.config.begin(); i != info.config.end(); i++ )
|
||||
{
|
||||
if ( strcmp(i->first, "only_single_header_row") == 0 )
|
||||
{
|
||||
if ( strcmp(i->second, "T") == 0 )
|
||||
only_single_header_row = true;
|
||||
|
||||
else if ( strcmp(i->second, "F") == 0 )
|
||||
only_single_header_row = false;
|
||||
|
||||
else
|
||||
{
|
||||
Error("invalid value for 'only_single_header_row', must be boolean (T/F)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( include_meta )
|
||||
{
|
||||
string names;
|
||||
string types;
|
||||
|
||||
string str = string(meta_prefix, meta_prefix_len)
|
||||
+ "separator " // Always use space as separator here.
|
||||
+ get_escaped_string(string(separator, separator_len), false)
|
||||
+ "\n";
|
||||
|
||||
if ( ! safe_write(fd, str.c_str(), str.length()) )
|
||||
goto write_error;
|
||||
|
||||
if ( ! (WriteHeaderField("set_separator", get_escaped_string(
|
||||
string(set_separator, set_separator_len), false)) &&
|
||||
WriteHeaderField("empty_field", get_escaped_string(
|
||||
string(empty_field, empty_field_len), false)) &&
|
||||
WriteHeaderField("unset_field", get_escaped_string(
|
||||
string(unset_field, unset_field_len), false)) &&
|
||||
WriteHeaderField("path", get_escaped_string(path, false)) &&
|
||||
WriteHeaderField("open", Timestamp(0))) )
|
||||
goto write_error;
|
||||
|
||||
for ( int i = 0; i < num_fields; ++i )
|
||||
{
|
||||
if ( i > 0 )
|
||||
|
@ -143,11 +144,39 @@ bool Ascii::DoInit(const WriterInfo& info, int num_fields, const Field* const *
|
|||
types += fields[i]->TypeName().c_str();
|
||||
}
|
||||
|
||||
if ( only_single_header_row )
|
||||
{
|
||||
// A single CSV-style line is all we need.
|
||||
string str = names + "\n";
|
||||
if ( ! safe_write(fd, str.c_str(), str.length()) )
|
||||
goto write_error;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string str = string(meta_prefix, meta_prefix_len)
|
||||
+ "separator " // Always use space as separator here.
|
||||
+ get_escaped_string(string(separator, separator_len), false)
|
||||
+ "\n";
|
||||
|
||||
if ( ! safe_write(fd, str.c_str(), str.length()) )
|
||||
goto write_error;
|
||||
|
||||
if ( ! (WriteHeaderField("set_separator", get_escaped_string(
|
||||
string(set_separator, set_separator_len), false)) &&
|
||||
WriteHeaderField("empty_field", get_escaped_string(
|
||||
string(empty_field, empty_field_len), false)) &&
|
||||
WriteHeaderField("unset_field", get_escaped_string(
|
||||
string(unset_field, unset_field_len), false)) &&
|
||||
WriteHeaderField("path", get_escaped_string(path, false)) &&
|
||||
WriteHeaderField("open", Timestamp(0))) )
|
||||
goto write_error;
|
||||
|
||||
if ( ! (WriteHeaderField("fields", names)
|
||||
&& WriteHeaderField("types", types)) )
|
||||
goto write_error;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
write_error:
|
||||
|
|
|
@ -45,6 +45,7 @@ private:
|
|||
// Options set from the script-level.
|
||||
bool output_to_stdout;
|
||||
bool include_meta;
|
||||
bool only_single_header_row;
|
||||
|
||||
char* separator;
|
||||
int separator_len;
|
||||
|
|
610
src/modbus-analyzer.pac
Normal file
610
src/modbus-analyzer.pac
Normal file
|
@ -0,0 +1,610 @@
|
|||
#
|
||||
# The development of Bro's Modbus analyzer has been made possible thanks to
|
||||
# the support of the Ministry of Security and Justice of the Kingdom of the
|
||||
# Netherlands within the projects of Hermes, Castor and Midas.
|
||||
#
|
||||
# Useful references: http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
|
||||
# http://www.simplymodbus.ca/faq.htm
|
||||
#
|
||||
|
||||
%header{
|
||||
VectorVal* bytestring_to_coils(bytestring coils, uint quantity);
|
||||
RecordVal* HeaderToBro(ModbusTCP_TransportHeader *header);
|
||||
%}
|
||||
|
||||
%code{
|
||||
VectorVal* bytestring_to_coils(bytestring coils, uint quantity)
|
||||
{
|
||||
VectorVal* modbus_coils = new VectorVal(BifType::Vector::ModbusCoils);
|
||||
|
||||
return modbus_coils;
|
||||
}
|
||||
|
||||
RecordVal* HeaderToBro(ModbusTCP_TransportHeader *header)
|
||||
{
|
||||
RecordVal* modbus_header = new RecordVal(BifType::Record::ModbusHeaders);
|
||||
modbus_header->Assign(0, new Val(header->tid(), TYPE_COUNT));
|
||||
modbus_header->Assign(1, new Val(header->pid(), TYPE_COUNT));
|
||||
modbus_header->Assign(2, new Val(header->uid(), TYPE_COUNT));
|
||||
modbus_header->Assign(3, new Val(header->fc(), TYPE_COUNT));
|
||||
return modbus_header;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
refine flow ModbusTCP_Flow += {
|
||||
|
||||
function deliver_message(header: ModbusTCP_TransportHeader): bool
|
||||
%{
|
||||
if ( ::modbus_message )
|
||||
{
|
||||
BifEvent::generate_modbus_message(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
is_orig());
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# EXCEPTION
|
||||
function deliver_Exception(header: ModbusTCP_TransportHeader, message: Exception): bool
|
||||
%{
|
||||
if ( ::modbus_exception )
|
||||
{
|
||||
BifEvent::generate_modbus_exception(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.code});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# REQUEST FC=1
|
||||
function deliver_ReadCoilsRequest(header: ModbusTCP_TransportHeader, message: ReadCoilsRequest): bool
|
||||
%{
|
||||
if ( ::modbus_read_coils_request )
|
||||
{
|
||||
BifEvent::generate_modbus_read_coils_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address},
|
||||
${message.quantity});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=1
|
||||
function deliver_ReadCoilsResponse(header: ModbusTCP_TransportHeader, message: ReadCoilsResponse): bool
|
||||
%{
|
||||
if ( ::modbus_read_coils_response )
|
||||
{
|
||||
BifEvent::generate_modbus_read_coils_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
bytestring_to_coils(${message.bits}, ${message.bits}.length()*8));
|
||||
}
|
||||
return true;
|
||||
%}
|
||||
|
||||
# REQUEST FC=2
|
||||
function deliver_ReadDiscreteInputsRequest(header: ModbusTCP_TransportHeader, message: ReadDiscreteInputsRequest): bool
|
||||
%{
|
||||
if ( ::modbus_read_discrete_inputs_request )
|
||||
{
|
||||
BifEvent::generate_modbus_read_discrete_inputs_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address}, ${message.quantity});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=2
|
||||
function deliver_ReadDiscreteInputsResponse(header: ModbusTCP_TransportHeader, message: ReadDiscreteInputsResponse): bool
|
||||
%{
|
||||
if ( ::modbus_read_discrete_inputs_response )
|
||||
{
|
||||
BifEvent::generate_modbus_read_discrete_inputs_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
bytestring_to_coils(${message.bits}, ${message.bits}.length()*8));
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# REQUEST FC=3
|
||||
function deliver_ReadHoldingRegistersRequest(header: ModbusTCP_TransportHeader, message: ReadHoldingRegistersRequest): bool
|
||||
%{
|
||||
if ( ::modbus_read_holding_registers_request )
|
||||
{
|
||||
BifEvent::generate_modbus_read_holding_registers_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address}, ${message.quantity});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=3
|
||||
function deliver_ReadHoldingRegistersResponse(header: ModbusTCP_TransportHeader, message: ReadHoldingRegistersResponse): bool
|
||||
%{
|
||||
if ( ${message.byte_count} % 2 != 0 )
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
fmt("invalid value for modbus read holding register response byte count %d", ${message.byte_count}));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ::modbus_read_holding_registers_response )
|
||||
{
|
||||
|
||||
VectorVal* t = new VectorVal(BifType::Vector::ModbusRegisters);
|
||||
for ( unsigned int i=0; i < ${message.registers}->size(); ++i )
|
||||
{
|
||||
Val* r = new Val(${message.registers[i]}, TYPE_COUNT);
|
||||
t->Assign(i, r, 0, OP_ASSIGN);
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_read_holding_registers_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
t);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# REQUEST FC=4
|
||||
function deliver_ReadInputRegistersRequest(header: ModbusTCP_TransportHeader, message: ReadInputRegistersRequest): bool
|
||||
%{
|
||||
if ( ::modbus_read_input_registers_request )
|
||||
{
|
||||
BifEvent::generate_modbus_read_input_registers_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address}, ${message.quantity});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=4
|
||||
function deliver_ReadInputRegistersResponse(header: ModbusTCP_TransportHeader, message: ReadInputRegistersResponse): bool
|
||||
%{
|
||||
if ( ${message.byte_count} % 2 != 0 )
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
fmt("invalid value for modbus read input register response byte count %d", ${message.byte_count}));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ::modbus_read_input_registers_response )
|
||||
{
|
||||
VectorVal* t = new VectorVal(BifType::Vector::ModbusRegisters);
|
||||
for ( unsigned int i=0; i < (${message.registers})->size(); ++i )
|
||||
{
|
||||
Val* r = new Val(${message.registers[i]}, TYPE_COUNT);
|
||||
t->Assign(i, r, 0, OP_ASSIGN);
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_read_input_registers_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
t);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# REQUEST FC=5
|
||||
function deliver_WriteSingleCoilRequest(header: ModbusTCP_TransportHeader, message: WriteSingleCoilRequest): bool
|
||||
%{
|
||||
if ( ::modbus_write_single_coil_request )
|
||||
{
|
||||
int val;
|
||||
if ( ${message.value} == 0x0000 )
|
||||
val = 0;
|
||||
else if ( ${message.value} == 0xFF00 )
|
||||
val = 1;
|
||||
else
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(fmt("invalid value for modbus write single coil request %d",
|
||||
${message.value}));
|
||||
return false;
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_write_single_coil_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.address},
|
||||
val);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=5
|
||||
function deliver_WriteSingleCoilResponse(header: ModbusTCP_TransportHeader, message: WriteSingleCoilResponse): bool
|
||||
%{
|
||||
if ( ::modbus_write_single_coil_response )
|
||||
{
|
||||
int val;
|
||||
if ( ${message.value} == 0x0000 )
|
||||
val = 0;
|
||||
else if ( ${message.value} == 0xFF00 )
|
||||
val = 1;
|
||||
else
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(fmt("invalid value for modbus write single coil response %d",
|
||||
${message.value}));
|
||||
return false;
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_write_single_coil_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.address},
|
||||
val);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# REQUEST FC=6
|
||||
function deliver_WriteSingleRegisterRequest(header: ModbusTCP_TransportHeader, message: WriteSingleRegisterRequest): bool
|
||||
%{
|
||||
if ( ::modbus_write_single_register_request )
|
||||
{
|
||||
BifEvent::generate_modbus_write_single_register_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.address}, ${message.value});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=6
|
||||
function deliver_WriteSingleRegisterResponse(header: ModbusTCP_TransportHeader, message: WriteSingleRegisterResponse): bool
|
||||
%{
|
||||
if ( ::modbus_write_single_register_response )
|
||||
{
|
||||
BifEvent::generate_modbus_write_single_register_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.address}, ${message.value});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# REQUEST FC=15
|
||||
function deliver_WriteMultipleCoilsRequest(header: ModbusTCP_TransportHeader, message: WriteMultipleCoilsRequest): bool
|
||||
%{
|
||||
if ( ::modbus_write_multiple_coils_request )
|
||||
{
|
||||
BifEvent::generate_modbus_write_multiple_coils_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address},
|
||||
bytestring_to_coils(${message.coils}, ${message.quantity}));
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=15
|
||||
function deliver_WriteMultipleCoilsResponse(header: ModbusTCP_TransportHeader, message: WriteMultipleCoilsResponse): bool
|
||||
%{
|
||||
if ( ::modbus_write_multiple_coils_response )
|
||||
{
|
||||
BifEvent::generate_modbus_write_multiple_coils_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address}, ${message.quantity});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# REQUEST FC=16
|
||||
function deliver_WriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader, message: WriteMultipleRegistersRequest): bool
|
||||
%{
|
||||
if ( ${message.byte_count} % 2 != 0 )
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
fmt("invalid value for modbus write multiple registers request byte count %d", ${message.byte_count}));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ::modbus_write_multiple_registers_request )
|
||||
{
|
||||
VectorVal * t = new VectorVal(BifType::Vector::ModbusRegisters);
|
||||
for ( unsigned int i = 0; i < (${message.registers}->size()); ++i )
|
||||
{
|
||||
Val* r = new Val(${message.registers[i]}, TYPE_COUNT);
|
||||
t->Assign(i, r, 0, OP_ASSIGN);
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_write_multiple_registers_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address}, t);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=16
|
||||
function deliver_WriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader, message: WriteMultipleRegistersResponse): bool
|
||||
%{
|
||||
if ( ::modbus_write_multiple_registers_response )
|
||||
{
|
||||
BifEvent::generate_modbus_write_multiple_registers_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address}, ${message.quantity});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# REQUEST FC=20
|
||||
function deliver_ReadFileRecordRequest(header: ModbusTCP_TransportHeader, message: ReadFileRecordRequest): bool
|
||||
%{
|
||||
if ( ::modbus_read_file_record_request )
|
||||
{
|
||||
//TODO: this need to be a vector of some Reference Request record type
|
||||
//VectorVal *t = new VectorVal(new VectorType(base_type(TYPE_COUNT)));
|
||||
//for ( unsigned int i = 0; i < (${message.references}->size()); ++i )
|
||||
// {
|
||||
// Val* r = new Val((${message.references[i].ref_type}), TYPE_COUNT);
|
||||
// t->Assign(i, r, 0, OP_ASSIGN);
|
||||
//
|
||||
// Val* k = new Val((${message.references[i].file_num}), TYPE_COUNT);
|
||||
// t->Assign(i, k, 0, OP_ASSIGN);
|
||||
//
|
||||
// Val* l = new Val((${message.references[i].record_num}), TYPE_COUNT);
|
||||
// t->Assign(i, l, 0, OP_ASSIGN);
|
||||
// }
|
||||
|
||||
BifEvent::generate_modbus_read_file_record_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header));
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=20
|
||||
function deliver_ReadFileRecordResponse(header: ModbusTCP_TransportHeader, message: ReadFileRecordResponse): bool
|
||||
%{
|
||||
if ( ::modbus_read_file_record_response )
|
||||
{
|
||||
//VectorVal *t = new VectorVal(new VectorType(base_type(TYPE_COUNT)));
|
||||
//for ( unsigned int i = 0; i < ${message.references}->size(); ++i )
|
||||
// {
|
||||
// //TODO: work the reference type in here somewhere
|
||||
// Val* r = new Val(${message.references[i].record_data}), TYPE_COUNT);
|
||||
// t->Assign(i, r, 0, OP_ASSIGN);
|
||||
// }
|
||||
|
||||
BifEvent::generate_modbus_read_file_record_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header));
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# REQUEST FC=21
|
||||
function deliver_WriteFileRecordRequest(header: ModbusTCP_TransportHeader, message: WriteFileRecordRequest): bool
|
||||
%{
|
||||
if ( ::modbus_write_file_record_request )
|
||||
{
|
||||
//VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_COUNT)));
|
||||
//for ( unsigned int i = 0; i < (${message.references}->size()); ++i )
|
||||
// {
|
||||
// Val* r = new Val((${message.references[i].ref_type}), TYPE_COUNT);
|
||||
// t->Assign(i, r, 0, OP_ASSIGN);
|
||||
//
|
||||
// Val* k = new Val((${message.references[i].file_num}), TYPE_COUNT);
|
||||
// t->Assign(i, k, 0, OP_ASSIGN);
|
||||
//
|
||||
// Val* n = new Val((${message.references[i].record_num}), TYPE_COUNT);
|
||||
// t->Assign(i, n, 0, OP_ASSIGN);
|
||||
//
|
||||
// for ( unsigned int j = 0; j < (${message.references[i].register_value}->size()); ++j )
|
||||
// {
|
||||
// k = new Val((${message.references[i].register_value[j]}), TYPE_COUNT);
|
||||
// t->Assign(i, k, 0, OP_ASSIGN);
|
||||
// }
|
||||
// }
|
||||
|
||||
BifEvent::generate_modbus_write_file_record_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header));
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# RESPONSE FC=21
|
||||
function deliver_WriteFileRecordResponse(header: ModbusTCP_TransportHeader, message: WriteFileRecordResponse): bool
|
||||
%{
|
||||
if ( ::modbus_write_file_record_response )
|
||||
{
|
||||
//VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_COUNT)));
|
||||
//for ( unsigned int i = 0; i < (${messages.references}->size()); ++i )
|
||||
// {
|
||||
// Val* r = new Val((${message.references[i].ref_type}), TYPE_COUNT);
|
||||
// t->Assign(i, r, 0, OP_ASSIGN);
|
||||
//
|
||||
// Val* f = new Val((${message.references[i].file_num}), TYPE_COUNT);
|
||||
// t->Assign(i, f, 0, OP_ASSIGN);
|
||||
//
|
||||
// Val* rn = new Val((${message.references[i].record_num}), TYPE_COUNT);
|
||||
// t->Assign(i, rn, 0, OP_ASSIGN);
|
||||
//
|
||||
// for ( unsigned int j = 0; j<(${message.references[i].register_value}->size()); ++j )
|
||||
// {
|
||||
// Val* k = new Val((${message.references[i].register_value[j]}), TYPE_COUNT);
|
||||
// t->Assign(i, k, 0, OP_ASSIGN);
|
||||
// }
|
||||
|
||||
BifEvent::generate_modbus_write_file_record_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header));
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# REQUEST FC=22
|
||||
function deliver_MaskWriteRegisterRequest(header: ModbusTCP_TransportHeader, message: MaskWriteRegisterRequest): bool
|
||||
%{
|
||||
if ( ::modbus_mask_write_register_request )
|
||||
{
|
||||
BifEvent::generate_modbus_mask_write_register_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.address},
|
||||
${message.and_mask}, ${message.or_mask});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=22
|
||||
function deliver_MaskWriteRegisterResponse(header: ModbusTCP_TransportHeader, message: MaskWriteRegisterResponse): bool
|
||||
%{
|
||||
if ( ::modbus_mask_write_register_response )
|
||||
{
|
||||
BifEvent::generate_modbus_mask_write_register_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.address},
|
||||
${message.and_mask}, ${message.or_mask});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# REQUEST FC=23
|
||||
function deliver_ReadWriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader, message: ReadWriteMultipleRegistersRequest): bool
|
||||
%{
|
||||
if ( ${message.write_byte_count} % 2 != 0 )
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
fmt("invalid value for modbus read write multiple registers request write byte count %d", ${message.write_byte_count}));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ::modbus_read_write_multiple_registers_request )
|
||||
{
|
||||
VectorVal* t = new VectorVal(BifType::Vector::ModbusRegisters);
|
||||
for ( unsigned int i = 0; i < ${message.write_register_values}->size(); ++i )
|
||||
{
|
||||
Val* r = new Val(${message.write_register_values[i]}, TYPE_COUNT);
|
||||
t->Assign(i, r, 0, OP_ASSIGN);
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_read_write_multiple_registers_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.read_start_address},
|
||||
${message.read_quantity},
|
||||
${message.write_start_address},
|
||||
t);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=23
|
||||
function deliver_ReadWriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader, message: ReadWriteMultipleRegistersResponse): bool
|
||||
%{
|
||||
if ( ${message.byte_count} % 2 != 0 )
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
fmt("invalid value for modbus read write multiple registers response byte count %d", ${message.byte_count}));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ::modbus_read_write_multiple_registers_response )
|
||||
{
|
||||
VectorVal* t = new VectorVal(BifType::Vector::ModbusRegisters);
|
||||
for ( unsigned int i = 0; i < ${message.registers}->size(); ++i )
|
||||
{
|
||||
Val* r = new Val(${message.registers[i]}, TYPE_COUNT);
|
||||
t->Assign(i, r, 0, OP_ASSIGN);
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_read_write_multiple_registers_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
t);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# REQUEST FC=24
|
||||
function deliver_ReadFIFOQueueRequest(header: ModbusTCP_TransportHeader, message: ReadFIFOQueueRequest): bool
|
||||
%{
|
||||
if ( ::modbus_read_fifo_queue_request )
|
||||
{
|
||||
BifEvent::generate_modbus_read_fifo_queue_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# RESPONSE FC=24
|
||||
function deliver_ReadFIFOQueueResponse(header: ModbusTCP_TransportHeader, message: ReadFIFOQueueResponse): bool
|
||||
%{
|
||||
if ( ${message.byte_count} % 2 != 0 )
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
fmt("invalid value for modbus read FIFO queue response byte count %d", ${message.byte_count}));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ::modbus_read_fifo_queue_response )
|
||||
{
|
||||
VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_COUNT)));
|
||||
for ( unsigned int i = 0; i < (${message.register_data})->size(); ++i )
|
||||
{
|
||||
Val* r = new Val(${message.register_data[i]}, TYPE_COUNT);
|
||||
t->Assign(i, r, 0, OP_ASSIGN);
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_read_fifo_queue_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
t);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
};
|
||||
|
||||
|
||||
|
394
src/modbus-protocol.pac
Normal file
394
src/modbus-protocol.pac
Normal file
|
@ -0,0 +1,394 @@
|
|||
#
|
||||
# The development of Bro's Modbus analyzer has been made possible thanks to
|
||||
# the support of the Ministry of Security and Justice of the Kingdom of the
|
||||
# Netherlands within the projects of Hermes, Castor and Midas.
|
||||
#
|
||||
# Useful references: http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
|
||||
# http://www.simplymodbus.ca/faq.htm
|
||||
|
||||
enum function_codes {
|
||||
# Standard functions
|
||||
READ_COILS = 0x01,
|
||||
READ_DISCRETE_INPUTS = 0x02,
|
||||
READ_HOLDING_REGISTERS = 0x03,
|
||||
READ_INPUT_REGISTERS = 0x04,
|
||||
WRITE_SINGLE_COIL = 0x05,
|
||||
WRITE_SINGLE_REGISTER = 0x06,
|
||||
# READ_EXCEPTION_STATUS = 0x07,
|
||||
# DIAGNOSTICS = 0x08,
|
||||
# GET_COMM_EVENT_COUNTER = 0x0B,
|
||||
# GET_COMM_EVENT_LOG = 0x0C,
|
||||
WRITE_MULTIPLE_COILS = 0x0F,
|
||||
WRITE_MULTIPLE_REGISTERS = 0x10,
|
||||
# REPORT_SLAVE_ID = 0x11,
|
||||
READ_FILE_RECORD = 0x14,
|
||||
WRITE_FILE_RECORD = 0x15,
|
||||
MASK_WRITE_REGISTER = 0x16,
|
||||
READ_WRITE_MULTIPLE_REGISTERS = 0x17,
|
||||
READ_FIFO_QUEUE = 0x18,
|
||||
ENCAP_INTERFACE_TRANSPORT = 0x2B,
|
||||
|
||||
# Machine/vendor/network specific functions
|
||||
PROGRAM_484 = 0x09,
|
||||
POLL_484 = 0x0A,
|
||||
PROGRAM_584_984 = 0x0D,
|
||||
POLL_584_984 = 0x0E,
|
||||
PROGRAM_884_U84 = 0x12,
|
||||
RESET_COMM_LINK_884_U84 = 0x13,
|
||||
PROGRAM_CONCEPT = 0x28,
|
||||
FIRMWARE_REPLACEMENT = 0x7D,
|
||||
PROGRAM_584_984_2 = 0x7E,
|
||||
REPORT_LOCAL_ADDRESS = 0x7F,
|
||||
|
||||
# Exceptions (not really function codes but they are used similarly)
|
||||
READ_COILS_EXCEPTION = 0x81,
|
||||
READ_DISCRETE_INPUTS_EXCEPTION = 0x82,
|
||||
READ_HOLDING_REGISTERS_EXCEPTION = 0x83,
|
||||
READ_INPUT_REGISTERS_EXCEPTION = 0x84,
|
||||
WRITE_SINGLE_COIL_EXCEPTION = 0x85,
|
||||
WRITE_SINGLE_REGISTER_EXCEPTION = 0x86,
|
||||
READ_EXCEPTION_STATUS_EXCEPTION = 0x87,
|
||||
WRITE_MULTIPLE_COILS_EXCEPTION = 0x8F,
|
||||
WRITE_MULTIPLE_REGISTERS_EXCEPTION = 0x90,
|
||||
READ_FILE_RECORD_EXCEPTION = 0x94,
|
||||
WRITE_FILE_RECORD_EXCEPTION = 0x95,
|
||||
MASK_WRITE_REGISTER_EXCEPTION = 0x96,
|
||||
READ_WRITE_MULTIPLE_REGISTERS_EXCEPTION = 0x97,
|
||||
READ_FIFO_QUEUE_EXCEPTION = 0x98,
|
||||
};
|
||||
|
||||
# Main Modbus/TCP PDU
|
||||
type ModbusTCP_PDU(is_orig: bool) = record {
|
||||
header: ModbusTCP_TransportHeader;
|
||||
body: case is_orig of {
|
||||
true -> request: ModbusTCP_Request(header);
|
||||
false -> response: ModbusTCP_Response(header);
|
||||
};
|
||||
} &length=header.len+6, &byteorder=bigendian;
|
||||
|
||||
type ModbusTCP_TransportHeader = record {
|
||||
tid: uint16; # Transaction identifier
|
||||
pid: uint16; # Protocol identifier
|
||||
len: uint16; # Length of everyting after this field
|
||||
uid: uint8; # Unit identifier (previously 'slave address')
|
||||
fc: uint8; # MODBUS function code (see function_codes enum)
|
||||
} &byteorder=bigendian, &let {
|
||||
deliver: bool = $context.flow.deliver_message(this);
|
||||
};
|
||||
|
||||
type ModbusTCP_Request(header: ModbusTCP_TransportHeader) = case header.fc of {
|
||||
READ_COILS -> readCoils: ReadCoilsRequest(header);
|
||||
READ_DISCRETE_INPUTS -> readDiscreteInputs: ReadDiscreteInputsRequest(header);
|
||||
READ_HOLDING_REGISTERS -> readHoldingRegisters: ReadHoldingRegistersRequest(header);
|
||||
READ_INPUT_REGISTERS -> readInputRegisters: ReadInputRegistersRequest(header);
|
||||
WRITE_SINGLE_COIL -> writeSingleCoil: WriteSingleCoilRequest(header);
|
||||
WRITE_SINGLE_REGISTER -> writeSingleRegister: WriteSingleRegisterRequest(header);
|
||||
#READ_EXCEPTION_STATUS -> readExceptionStatus: ReadExceptionStatusRequest(header);
|
||||
#DIAGNOSTICS -> diagnostics: DiagnosticsRequest(header);
|
||||
#GET_COMM_EVENT_COUNTER -> getCommEventCounter: GetCommEventCounterRequest(header);
|
||||
#GET_COMM_EVENT_LOG -> getCommEventLog: GetCommEventLogRequest(header);
|
||||
WRITE_MULTIPLE_COILS -> writeMultipleCoils: WriteMultipleCoilsRequest(header);
|
||||
WRITE_MULTIPLE_REGISTERS -> writeMultRegisters: WriteMultipleRegistersRequest(header);
|
||||
#REPORT_SLAVE_ID
|
||||
READ_FILE_RECORD -> readFileRecord: ReadFileRecordRequest(header);
|
||||
WRITE_FILE_RECORD -> writeFileRecord: WriteFileRecordRequest(header);
|
||||
MASK_WRITE_REGISTER -> maskWriteRegister: MaskWriteRegisterRequest(header);
|
||||
READ_WRITE_MULTIPLE_REGISTERS -> readWriteMultipleRegisters: ReadWriteMultipleRegistersRequest(header);
|
||||
READ_FIFO_QUEUE -> readFIFOQueue: ReadFIFOQueueRequest(header);
|
||||
#ENCAP_INTERFACE_TRANSPORT
|
||||
|
||||
# All the rest
|
||||
default -> unknown: bytestring &restofdata;
|
||||
};
|
||||
|
||||
# Responses
|
||||
#
|
||||
type ModbusTCP_Response(header: ModbusTCP_TransportHeader) = case header.fc of {
|
||||
READ_COILS -> readCoils: ReadCoilsResponse(header);
|
||||
READ_DISCRETE_INPUTS -> readDiscreteInputs: ReadDiscreteInputsResponse(header);
|
||||
READ_HOLDING_REGISTERS -> readHoldingRegisters: ReadHoldingRegistersResponse(header);
|
||||
READ_INPUT_REGISTERS -> readInputRegisters: ReadInputRegistersResponse(header);
|
||||
WRITE_SINGLE_COIL -> writeSingleCoil: WriteSingleCoilResponse(header);
|
||||
WRITE_SINGLE_REGISTER -> writeSingleRegister: WriteSingleRegisterResponse(header);
|
||||
#READ_EXCEPTION_STATUS -> readExceptionStatus: ReadExceptionStatusResponse(header);
|
||||
#DIAGNOSTICS -> diagnostics: DiagnosticsResponse(header);
|
||||
#GET_COMM_EVENT_COUNTER -> getCommEventCounter: GetCommEventCounterResponse(header);
|
||||
#GET_COMM_EVENT_LOG -> getCommEventLog: GetCommEventLogResponse(header);
|
||||
WRITE_MULTIPLE_COILS -> writeMultipleCoils: WriteMultipleCoilsResponse(header);
|
||||
WRITE_MULTIPLE_REGISTERS -> writeMultRegisters: WriteMultipleRegistersResponse(header);
|
||||
#REPORT_SLAVE_ID
|
||||
READ_FILE_RECORD -> readFileRecord: ReadFileRecordResponse(header);
|
||||
WRITE_FILE_RECORD -> writeFileRecord: WriteFileRecordResponse(header);
|
||||
MASK_WRITE_REGISTER -> maskWriteRegister: MaskWriteRegisterResponse(header);
|
||||
READ_WRITE_MULTIPLE_REGISTERS -> readWriteMultipleRegisters: ReadWriteMultipleRegistersResponse(header);
|
||||
READ_FIFO_QUEUE -> readFIFOQueue: ReadFIFOQueueResponse(header);
|
||||
|
||||
# Exceptions
|
||||
READ_HOLDING_REGISTERS_EXCEPTION -> readHoldingRegistersException: Exception(header);
|
||||
WRITE_MULTIPLE_REGISTERS_EXCEPTION -> writeMultRegistersException: Exception(header);
|
||||
READ_COILS_EXCEPTION -> readCoilsException: Exception(header);
|
||||
READ_DISCRETE_INPUTS_EXCEPTION -> readDiscreteInputsException: Exception(header);
|
||||
READ_INPUT_REGISTERS_EXCEPTION -> readInputRegistersException: Exception(header);
|
||||
WRITE_SINGLE_COIL_EXCEPTION -> writeCoilException: Exception(header);
|
||||
WRITE_SINGLE_REGISTER_EXCEPTION -> writeSingleRegisterException: Exception(header);
|
||||
READ_EXCEPTION_STATUS_EXCEPTION -> readExceptionStatusException: Exception(header);
|
||||
WRITE_MULTIPLE_COILS_EXCEPTION -> forceMultipleCoilsException: Exception(header);
|
||||
READ_FILE_RECORD_EXCEPTION -> readGeneralReferenceException: Exception(header);
|
||||
WRITE_FILE_RECORD_EXCEPTION -> writeGeneralReferenceException: Exception(header);
|
||||
MASK_WRITE_REGISTER_EXCEPTION -> maskWriteRegisterException: Exception(header);
|
||||
READ_WRITE_MULTIPLE_REGISTERS_EXCEPTION -> readWriteRegistersException: Exception(header);
|
||||
READ_FIFO_QUEUE_EXCEPTION -> readFIFOQueueException: Exception(header);
|
||||
|
||||
# All the rest
|
||||
default -> unknown: bytestring &restofdata;
|
||||
};
|
||||
|
||||
type Exception(header: ModbusTCP_TransportHeader) = record {
|
||||
code: uint8;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_Exception(header, this);
|
||||
};
|
||||
|
||||
# REQUEST FC=1
|
||||
type ReadCoilsRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16 &check(quantity <= 2000);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadCoilsRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=1
|
||||
type ReadCoilsResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
bits: bytestring &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadCoilsResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=2
|
||||
type ReadDiscreteInputsRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16 &check(quantity <= 2000);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadDiscreteInputsRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=2
|
||||
type ReadDiscreteInputsResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
bits: bytestring &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadDiscreteInputsResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=3
|
||||
type ReadHoldingRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16 &check(1 <= quantity && quantity <= 125);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadHoldingRegistersRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=3
|
||||
type ReadHoldingRegistersResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
registers: uint16[byte_count/2] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadHoldingRegistersResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=4
|
||||
type ReadInputRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16 &check(1 <= quantity && quantity <= 125);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadInputRegistersRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=4
|
||||
type ReadInputRegistersResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
registers: uint16[byte_count/2] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadInputRegistersResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=5
|
||||
type WriteSingleCoilRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
address: uint16;
|
||||
value: uint16 &check(value == 0x0000 || value == 0xFF00);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteSingleCoilRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=5
|
||||
type WriteSingleCoilResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
address: uint16;
|
||||
value: uint16 &check(value == 0x0000 || value == 0xFF00);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteSingleCoilResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=6
|
||||
type WriteSingleRegisterRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
address: uint16;
|
||||
value: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteSingleRegisterRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=6
|
||||
type WriteSingleRegisterResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
address: uint16;
|
||||
value: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteSingleRegisterResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=15
|
||||
type WriteMultipleCoilsRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16 &check(quantity <= 0x07B0);
|
||||
byte_count: uint8 &check(byte_count == (quantity + 7)/8);
|
||||
coils: bytestring &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteMultipleCoilsRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=15
|
||||
type WriteMultipleCoilsResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16 &check(quantity <= 0x07B0);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteMultipleCoilsResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=16
|
||||
type WriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16;
|
||||
byte_count: uint8;
|
||||
# We specify registers buffer with quantity and byte_count so that the analyzer
|
||||
# will choke if something doesn't match right (correct devices should make it right).
|
||||
registers: uint16[quantity] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteMultipleRegistersRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=16
|
||||
type WriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteMultipleRegistersResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# Support data structure for following message type.
|
||||
type FileRecordRequest = record {
|
||||
ref_type: uint8 &check(ref_type == 6);
|
||||
file_num: uint16 &check(file_num > 0);
|
||||
record_num: uint16 &check(record_num <= 0x270F);
|
||||
record_len: uint16;
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=20
|
||||
type ReadFileRecordRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8 &check(byte_count >= 0x07 && byte_count <= 0xF5);
|
||||
references: FileRecordRequest[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadFileRecordRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# Support data structure for the following message type.
|
||||
type FileRecordResponse = record {
|
||||
file_len: uint8 &check(file_len >= 0x07 && file_len <= 0xF5);
|
||||
ref_type: uint8 &check(ref_type == 6);
|
||||
record_data: uint16[file_len/2] &length=file_len;
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=20
|
||||
type ReadFileRecordResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8 &check(byte_count >= 0x07 && byte_count <= 0xF5);
|
||||
references: FileRecordResponse[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadFileRecordResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# Support data structure for the two following message types.
|
||||
type ReferenceWithData = record {
|
||||
ref_type: uint8;
|
||||
file_num: uint16;
|
||||
record_num: uint16;
|
||||
word_count: uint16;
|
||||
register_value: uint16[word_count];
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=21
|
||||
type WriteFileRecordRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
references: ReferenceWithData[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteFileRecordRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=21
|
||||
type WriteFileRecordResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
references: ReferenceWithData[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteFileRecordResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=22
|
||||
type MaskWriteRegisterRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
address: uint16;
|
||||
and_mask: uint16;
|
||||
or_mask: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_MaskWriteRegisterRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=22
|
||||
type MaskWriteRegisterResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
address: uint16;
|
||||
and_mask: uint16;
|
||||
or_mask: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_MaskWriteRegisterResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=23
|
||||
type ReadWriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
read_start_address: uint16;
|
||||
read_quantity: uint16 &check(read_quantity <= 125);
|
||||
write_start_address: uint16;
|
||||
write_quantity: uint16 &check(write_quantity <= 100);
|
||||
write_byte_count: uint8;
|
||||
write_register_values: uint16[write_quantity] &length=write_byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadWriteMultipleRegistersRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=23
|
||||
type ReadWriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
registers: uint16[byte_count/2] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadWriteMultipleRegistersResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=24
|
||||
type ReadFIFOQueueRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
} &let{
|
||||
deliver: bool = $context.flow.deliver_ReadFIFOQueueRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=24
|
||||
type ReadFIFOQueueResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint16 &check(byte_count <= 62);
|
||||
fifo_count: uint16 &check(fifo_count <= 31);
|
||||
register_data: uint16[fifo_count] &length=fifo_count*2;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadFIFOQueueResponse(header, this);
|
||||
} &byteorder=bigendian;
|
28
src/modbus.pac
Normal file
28
src/modbus.pac
Normal file
|
@ -0,0 +1,28 @@
|
|||
#
|
||||
# The development of Bro's Modbus analyzer has been made possible thanks to
|
||||
# the support of the Ministry of Security and Justice of the Kingdom of the
|
||||
# Netherlands within the projects of Hermes, Castor and Midas.
|
||||
#
|
||||
# Useful references: http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
|
||||
# http://www.simplymodbus.ca/faq.htm
|
||||
|
||||
%include binpac.pac
|
||||
%include bro.pac
|
||||
|
||||
analyzer ModbusTCP withcontext {
|
||||
connection: ModbusTCP_Conn;
|
||||
flow: ModbusTCP_Flow;
|
||||
};
|
||||
|
||||
connection ModbusTCP_Conn(bro_analyzer: BroAnalyzer) {
|
||||
upflow = ModbusTCP_Flow(true);
|
||||
downflow = ModbusTCP_Flow(false);
|
||||
};
|
||||
|
||||
%include modbus-protocol.pac
|
||||
|
||||
flow ModbusTCP_Flow(is_orig: bool) {
|
||||
flowunit = ModbusTCP_PDU(is_orig) withcontext (connection, this);
|
||||
}
|
||||
|
||||
%include modbus-analyzer.pac
|
44
src/nb_dns.c
44
src/nb_dns.c
|
@ -307,31 +307,32 @@ nb_dns_host_request(register struct nb_dns_info *nd, register const char *name,
|
|||
register void *cookie, register char *errstr)
|
||||
{
|
||||
|
||||
return (nb_dns_host_request2(nd, name, AF_INET, cookie, errstr));
|
||||
return (nb_dns_host_request2(nd, name, AF_INET, 0, cookie, errstr));
|
||||
}
|
||||
|
||||
int
|
||||
nb_dns_host_request2(register struct nb_dns_info *nd, register const char *name,
|
||||
register int af, register void *cookie, register char *errstr)
|
||||
register int af, register int qtype, register void *cookie, register char *errstr)
|
||||
{
|
||||
register int qtype;
|
||||
if (qtype != 16) {
|
||||
|
||||
switch (af) {
|
||||
switch (af) {
|
||||
|
||||
case AF_INET:
|
||||
qtype = T_A;
|
||||
break;
|
||||
case AF_INET:
|
||||
qtype = T_A;
|
||||
break;
|
||||
|
||||
#ifdef AF_INET6
|
||||
case AF_INET6:
|
||||
qtype = T_AAAA;
|
||||
break;
|
||||
case AF_INET6:
|
||||
qtype = T_AAAA;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
snprintf(errstr, NB_DNS_ERRSIZE,
|
||||
"nb_dns_host_request2(): uknown address family %d", af);
|
||||
return (-1);
|
||||
default:
|
||||
snprintf(errstr, NB_DNS_ERRSIZE,
|
||||
"nb_dns_host_request2(): unknown address family %d", af);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
return (_nb_dns_mkquery(nd, name, af, qtype, cookie, errstr));
|
||||
}
|
||||
|
@ -579,6 +580,7 @@ nb_dns_activity(struct nb_dns_info *nd, struct nb_dns_result *nr, char *errstr)
|
|||
nr->host_errno = NO_RECOVERY;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(bp, rdata, rdlen);
|
||||
*hap++ = bp;
|
||||
bp += rdlen;
|
||||
|
@ -587,6 +589,20 @@ nb_dns_activity(struct nb_dns_info *nd, struct nb_dns_result *nr, char *errstr)
|
|||
/* Keep looking for more A records */
|
||||
break;
|
||||
|
||||
case T_TXT:
|
||||
if (bp + rdlen >= ep) {
|
||||
snprintf(errstr, NB_DNS_ERRSIZE,
|
||||
"nb_dns_activity(): overflow 1 for txt");
|
||||
nr->host_errno = NO_RECOVERY;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(bp, rdata, rdlen);
|
||||
he->h_name = bp+1; /* First char is a control character. */
|
||||
nr->hostent = he;
|
||||
nr->ttl = rttl;
|
||||
return (1);
|
||||
|
||||
case T_PTR:
|
||||
n = dn_expand((const u_char *)msg,
|
||||
(const u_char *)msg + msglen, rdata, bp, ep - bp);
|
||||
|
|
|
@ -22,7 +22,7 @@ void nb_dns_finish(struct nb_dns_info *);
|
|||
int nb_dns_fd(struct nb_dns_info *);
|
||||
|
||||
int nb_dns_host_request(struct nb_dns_info *, const char *, void *, char *);
|
||||
int nb_dns_host_request2(struct nb_dns_info *, const char *, int,
|
||||
int nb_dns_host_request2(struct nb_dns_info *, const char *, int, int,
|
||||
void *, char *);
|
||||
|
||||
int nb_dns_addr_request(struct nb_dns_info *, nb_uint32_t, void *, char *);
|
||||
|
|
103
src/parse.y
103
src/parse.y
|
@ -2,14 +2,14 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
%}
|
||||
|
||||
%expect 87
|
||||
%expect 88
|
||||
|
||||
%token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY
|
||||
%token TOK_ATENDIF TOK_ATELSE TOK_ATIF TOK_ATIFDEF TOK_ATIFNDEF
|
||||
%token TOK_BOOL TOK_BREAK TOK_CASE TOK_CONST
|
||||
%token TOK_CONSTANT TOK_COPY TOK_COUNT TOK_COUNTER TOK_DEFAULT TOK_DELETE
|
||||
%token TOK_DOUBLE TOK_ELSE TOK_ENUM TOK_EVENT TOK_EXPORT TOK_FILE TOK_FOR
|
||||
%token TOK_FUNCTION TOK_GLOBAL TOK_ID TOK_IF TOK_INT
|
||||
%token TOK_FUNCTION TOK_GLOBAL TOK_HOOK TOK_ID TOK_IF TOK_INT
|
||||
%token TOK_INTERVAL TOK_LIST TOK_LOCAL TOK_MODULE
|
||||
%token TOK_NEXT TOK_OF TOK_PATTERN TOK_PATTERN_TEXT
|
||||
%token TOK_PORT TOK_PRINT TOK_RECORD TOK_REDEF
|
||||
|
@ -56,6 +56,7 @@
|
|||
%type <re> pattern
|
||||
%type <expr> expr init anonymous_function
|
||||
%type <event_expr> event
|
||||
%type <call_expr> hook
|
||||
%type <stmt> stmt stmt_list func_body for_head
|
||||
%type <type> type opt_type enum_body
|
||||
%type <func_type> func_hdr func_params
|
||||
|
@ -131,16 +132,18 @@ const char* cur_enum_elem_id = 0;
|
|||
type_decl_list* fake_type_decl_list = 0;
|
||||
TypeDecl* last_fake_type_decl = 0;
|
||||
|
||||
static ID* cur_decl_type_id = 0;
|
||||
|
||||
static void parser_new_enum (void)
|
||||
{
|
||||
/* Starting a new enum definition. */
|
||||
assert(cur_enum_type == NULL);
|
||||
cur_enum_type = new EnumType();
|
||||
cur_enum_type = new EnumType(cur_decl_type_id->Name());
|
||||
|
||||
// For documentation purposes, a separate type object is created
|
||||
// in order to avoid overlap that can be caused by redefs.
|
||||
if ( generate_documentation )
|
||||
cur_enum_type_doc = new CommentedEnumType();
|
||||
cur_enum_type_doc = new CommentedEnumType(cur_decl_type_id->Name());
|
||||
}
|
||||
|
||||
static void parser_redef_enum (ID *id)
|
||||
|
@ -158,7 +161,7 @@ static void parser_redef_enum (ID *id)
|
|||
}
|
||||
|
||||
if ( generate_documentation )
|
||||
cur_enum_type_doc = new CommentedEnumType();
|
||||
cur_enum_type_doc = new CommentedEnumType(id->Name());
|
||||
}
|
||||
|
||||
static void add_enum_comment (std::list<std::string>* comments)
|
||||
|
@ -456,17 +459,24 @@ expr:
|
|||
|
||||
| '[' expr_list ']'
|
||||
{
|
||||
// A little crufty: we peek at the type of
|
||||
// the first expression in the list. If it's
|
||||
// a record or a field assignment, then this
|
||||
// is a record constructor. If not, then this
|
||||
// is a list used for an initializer.
|
||||
|
||||
set_location(@1, @3);
|
||||
|
||||
Expr* e0 = $2->Exprs()[0];
|
||||
if ( e0->Tag() == EXPR_FIELD_ASSIGN ||
|
||||
e0->Type()->Tag() == TYPE_RECORD )
|
||||
bool is_record_ctor = true;
|
||||
|
||||
// If every expression in the list is a field assignment,
|
||||
// then treat it as a record constructor, else as a list
|
||||
// used for an initializer.
|
||||
|
||||
for ( int i = 0; i < $2->Exprs().length(); ++i )
|
||||
{
|
||||
if ( $2->Exprs()[i]->Tag() != EXPR_FIELD_ASSIGN )
|
||||
{
|
||||
is_record_ctor = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_record_ctor )
|
||||
$$ = new RecordConstructorExpr($2);
|
||||
else
|
||||
$$ = $2;
|
||||
|
@ -859,7 +869,13 @@ type:
|
|||
| TOK_EVENT '(' formal_args ')'
|
||||
{
|
||||
set_location(@1, @3);
|
||||
$$ = new FuncType($3, 0, 1);
|
||||
$$ = new FuncType($3, 0, FUNC_FLAVOR_EVENT);
|
||||
}
|
||||
|
||||
| TOK_HOOK '(' formal_args ')'
|
||||
{
|
||||
set_location(@1, @3);
|
||||
$$ = new FuncType($3, 0, FUNC_FLAVOR_HOOK);
|
||||
}
|
||||
|
||||
| TOK_FILE TOK_OF type
|
||||
|
@ -991,12 +1007,27 @@ decl:
|
|||
ID* id = $2;
|
||||
if ( id->Type()->Tag() == TYPE_FUNC )
|
||||
{
|
||||
if ( id->Type()->AsFuncType()->IsEvent() )
|
||||
current_reST_doc->AddEvent(
|
||||
new BroDocObj(id, reST_doc_comments));
|
||||
else
|
||||
switch ( id->Type()->AsFuncType()->Flavor() ) {
|
||||
|
||||
case FUNC_FLAVOR_FUNCTION:
|
||||
current_reST_doc->AddFunction(
|
||||
new BroDocObj(id, reST_doc_comments));
|
||||
break;
|
||||
|
||||
case FUNC_FLAVOR_EVENT:
|
||||
current_reST_doc->AddEvent(
|
||||
new BroDocObj(id, reST_doc_comments));
|
||||
break;
|
||||
|
||||
case FUNC_FLAVOR_HOOK:
|
||||
current_reST_doc->AddHook(
|
||||
new BroDocObj(id, reST_doc_comments));
|
||||
break;
|
||||
|
||||
default:
|
||||
reporter->InternalError("invalid function flavor");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
|
@ -1098,9 +1129,10 @@ decl:
|
|||
}
|
||||
}
|
||||
|
||||
| TOK_TYPE global_id ':' type opt_attr ';'
|
||||
| TOK_TYPE global_id ':' { cur_decl_type_id = $2; } type opt_attr ';'
|
||||
{
|
||||
add_type($2, $4, $5, 0);
|
||||
cur_decl_type_id = 0;
|
||||
add_type($2, $5, $6, 0);
|
||||
|
||||
if ( generate_documentation )
|
||||
{
|
||||
|
@ -1175,6 +1207,15 @@ func_hdr:
|
|||
current_reST_doc->AddEventHandler(
|
||||
new BroDocObj($2, reST_doc_comments));
|
||||
}
|
||||
| TOK_HOOK def_global_id func_params
|
||||
{
|
||||
begin_func($2, current_module.c_str(),
|
||||
FUNC_FLAVOR_HOOK, 0, $3);
|
||||
$$ = $3;
|
||||
if ( generate_documentation )
|
||||
current_reST_doc->AddHookHandler(
|
||||
new BroDocObj($2, reST_doc_comments));
|
||||
}
|
||||
| TOK_REDEF TOK_EVENT event_id func_params
|
||||
{
|
||||
begin_func($3, current_module.c_str(),
|
||||
|
@ -1209,9 +1250,9 @@ begin_func:
|
|||
|
||||
func_params:
|
||||
'(' formal_args ')' ':' type
|
||||
{ $$ = new FuncType($2, $5, 0); }
|
||||
{ $$ = new FuncType($2, $5, FUNC_FLAVOR_FUNCTION); }
|
||||
| '(' formal_args ')'
|
||||
{ $$ = new FuncType($2, base_type(TYPE_VOID), 0); }
|
||||
{ $$ = new FuncType($2, base_type(TYPE_VOID), FUNC_FLAVOR_FUNCTION); }
|
||||
;
|
||||
|
||||
opt_type:
|
||||
|
@ -1331,6 +1372,14 @@ stmt:
|
|||
brofiler.AddStmt($$);
|
||||
}
|
||||
|
||||
| TOK_HOOK hook ';' opt_no_test
|
||||
{
|
||||
set_location(@1, @4);
|
||||
$$ = new HookStmt($2);
|
||||
if ( ! $4 )
|
||||
brofiler.AddStmt($$);
|
||||
}
|
||||
|
||||
| TOK_IF '(' expr ')' stmt
|
||||
{
|
||||
set_location(@1, @4);
|
||||
|
@ -1484,6 +1533,14 @@ event:
|
|||
}
|
||||
;
|
||||
|
||||
hook:
|
||||
expr '(' opt_expr_list ')'
|
||||
{
|
||||
set_location(@1, @4);
|
||||
$$ = new CallExpr($1, $3, true);
|
||||
}
|
||||
;
|
||||
|
||||
case_list:
|
||||
case_list case
|
||||
{ $1->append($2); }
|
||||
|
|
|
@ -1,13 +1,30 @@
|
|||
%{
|
||||
#include <stdio.h>
|
||||
#include <netinet/in.h>
|
||||
#include <vector>
|
||||
#include "config.h"
|
||||
#include "RuleMatcher.h"
|
||||
#include "Reporter.h"
|
||||
#include "IPAddr.h"
|
||||
#include "net_util.h"
|
||||
|
||||
extern void begin_PS();
|
||||
extern void end_PS();
|
||||
|
||||
Rule* current_rule = 0;
|
||||
const char* current_rule_file = 0;
|
||||
|
||||
static uint8_t mask_to_len(uint32_t mask)
|
||||
{
|
||||
if ( mask == 0xffffffff )
|
||||
return 32;
|
||||
|
||||
uint32_t x = ~mask + 1;
|
||||
uint8_t len;
|
||||
for ( len = 0; len < 32 && (! (x & (1 << len))); ++len );
|
||||
|
||||
return len;
|
||||
}
|
||||
%}
|
||||
|
||||
%token TOK_COMP
|
||||
|
@ -21,6 +38,7 @@ const char* current_rule_file = 0;
|
|||
%token TOK_IDENT
|
||||
%token TOK_INT
|
||||
%token TOK_IP
|
||||
%token TOK_IP6
|
||||
%token TOK_IP_OPTIONS
|
||||
%token TOK_IP_OPTION_SYM
|
||||
%token TOK_IP_PROTO
|
||||
|
@ -49,7 +67,9 @@ const char* current_rule_file = 0;
|
|||
%type <hdr_test> hdr_expr
|
||||
%type <range> range rangeopt
|
||||
%type <vallist> value_list
|
||||
%type <prefix_val_list> prefix_value_list
|
||||
%type <mval> TOK_IP value
|
||||
%type <prefixval> TOK_IP6 prefix_value
|
||||
%type <prot> TOK_PROT
|
||||
%type <ptype> TOK_PATTERN_TYPE
|
||||
|
||||
|
@ -57,6 +77,8 @@ const char* current_rule_file = 0;
|
|||
Rule* rule;
|
||||
RuleHdrTest* hdr_test;
|
||||
maskedvalue_list* vallist;
|
||||
vector<IPPrefix>* prefix_val_list;
|
||||
IPPrefix* prefixval;
|
||||
|
||||
bool bl;
|
||||
int val;
|
||||
|
@ -91,11 +113,11 @@ rule_attr_list:
|
|||
;
|
||||
|
||||
rule_attr:
|
||||
TOK_DST_IP TOK_COMP value_list
|
||||
TOK_DST_IP TOK_COMP prefix_value_list
|
||||
{
|
||||
current_rule->AddHdrTest(new RuleHdrTest(
|
||||
RuleHdrTest::IP, 16, 4,
|
||||
(RuleHdrTest::Comp) $2, $3));
|
||||
RuleHdrTest::IPDst,
|
||||
(RuleHdrTest::Comp) $2, *($3)));
|
||||
}
|
||||
|
||||
| TOK_DST_PORT TOK_COMP value_list
|
||||
|
@ -123,10 +145,14 @@ rule_attr:
|
|||
{
|
||||
int proto = 0;
|
||||
switch ( $3 ) {
|
||||
case RuleHdrTest::ICMP: proto = 1; break;
|
||||
case RuleHdrTest::ICMP: proto = IPPROTO_ICMP; break;
|
||||
case RuleHdrTest::ICMPv6: proto = IPPROTO_ICMPV6; break;
|
||||
// signature matching against outer packet headers of IP-in-IP
|
||||
// tunneling not supported, so do a no-op there
|
||||
case RuleHdrTest::IP: proto = 0; break;
|
||||
case RuleHdrTest::TCP: proto = 6; break;
|
||||
case RuleHdrTest::UDP: proto = 17; break;
|
||||
case RuleHdrTest::IPv6: proto = 0; break;
|
||||
case RuleHdrTest::TCP: proto = IPPROTO_TCP; break;
|
||||
case RuleHdrTest::UDP: proto = IPPROTO_UDP; break;
|
||||
default:
|
||||
rules_error("internal_error: unknown protocol");
|
||||
}
|
||||
|
@ -140,16 +166,20 @@ rule_attr:
|
|||
val->mask = 0xffffffff;
|
||||
vallist->append(val);
|
||||
|
||||
// offset & size params are dummies, actual next proto value in
|
||||
// header is retrieved dynamically via IP_Hdr::NextProto()
|
||||
current_rule->AddHdrTest(new RuleHdrTest(
|
||||
RuleHdrTest::IP, 9, 1,
|
||||
RuleHdrTest::NEXT, 0, 0,
|
||||
(RuleHdrTest::Comp) $2, vallist));
|
||||
}
|
||||
}
|
||||
|
||||
| TOK_IP_PROTO TOK_COMP value_list
|
||||
{
|
||||
// offset & size params are dummies, actual next proto value in
|
||||
// header is retrieved dynamically via IP_Hdr::NextProto()
|
||||
current_rule->AddHdrTest(new RuleHdrTest(
|
||||
RuleHdrTest::IP, 9, 1,
|
||||
RuleHdrTest::NEXT, 0, 0,
|
||||
(RuleHdrTest::Comp) $2, $3));
|
||||
}
|
||||
|
||||
|
@ -193,11 +223,11 @@ rule_attr:
|
|||
| TOK_SAME_IP
|
||||
{ current_rule->AddCondition(new RuleConditionSameIP()); }
|
||||
|
||||
| TOK_SRC_IP TOK_COMP value_list
|
||||
| TOK_SRC_IP TOK_COMP prefix_value_list
|
||||
{
|
||||
current_rule->AddHdrTest(new RuleHdrTest(
|
||||
RuleHdrTest::IP, 12, 4,
|
||||
(RuleHdrTest::Comp) $2, $3));
|
||||
RuleHdrTest::IPSrc,
|
||||
(RuleHdrTest::Comp) $2, *($3)));
|
||||
}
|
||||
|
||||
| TOK_SRC_PORT TOK_COMP value_list
|
||||
|
@ -254,6 +284,38 @@ value_list:
|
|||
}
|
||||
;
|
||||
|
||||
prefix_value_list:
|
||||
prefix_value_list ',' prefix_value
|
||||
{
|
||||
$$ = $1;
|
||||
$$->push_back(*($3));
|
||||
}
|
||||
| prefix_value_list ',' TOK_IDENT
|
||||
{
|
||||
$$ = $1;
|
||||
id_to_maskedvallist($3, 0, $1);
|
||||
}
|
||||
| prefix_value
|
||||
{
|
||||
$$ = new vector<IPPrefix>();
|
||||
$$->push_back(*($1));
|
||||
}
|
||||
| TOK_IDENT
|
||||
{
|
||||
$$ = new vector<IPPrefix>();
|
||||
id_to_maskedvallist($1, 0, $$);
|
||||
}
|
||||
;
|
||||
|
||||
prefix_value:
|
||||
TOK_IP
|
||||
{
|
||||
$$ = new IPPrefix(IPAddr(IPv4, &($1.val), IPAddr::Host),
|
||||
mask_to_len($1.mask));
|
||||
}
|
||||
| TOK_IP6
|
||||
;
|
||||
|
||||
value:
|
||||
TOK_INT
|
||||
{ $$.val = $1; $$.mask = 0xffffffff; }
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
%{
|
||||
typedef unsigned int uint32;
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "RuleMatcher.h"
|
||||
#include "IPAddr.h"
|
||||
#include "util.h"
|
||||
#include "rule-parse.h"
|
||||
|
||||
int rules_line_number = 0;
|
||||
|
@ -14,11 +15,14 @@ int rules_line_number = 0;
|
|||
|
||||
%x PS
|
||||
|
||||
OWS [ \t]*
|
||||
WS [ \t]+
|
||||
D [0-9]+
|
||||
H [0-9a-fA-F]+
|
||||
HEX {H}
|
||||
STRING \"([^\n\"]|\\\")*\"
|
||||
ID ([0-9a-zA-Z_-]+::)*[0-9a-zA-Z_-]+
|
||||
IP6 ("["({HEX}:){7}{HEX}"]")|("["0x{HEX}({HEX}|:)*"::"({HEX}|:)*"]")|("["({HEX}|:)*"::"({HEX}|:)*"]")|("["({HEX}|:)*"::"({HEX}|:)*({D}"."){3}{D}"]")
|
||||
RE \/(\\\/)?([^/]|[^\\]\\\/)*\/
|
||||
META \.[^ \t]+{WS}[^\n]+
|
||||
PID ([0-9a-zA-Z_-]|"::")+
|
||||
|
@ -34,6 +38,18 @@ PID ([0-9a-zA-Z_-]|"::")+
|
|||
\n ++rules_line_number;
|
||||
}
|
||||
|
||||
{IP6} {
|
||||
rules_lval.prefixval = new IPPrefix(IPAddr(extract_ip(yytext)), 128, true);
|
||||
return TOK_IP6;
|
||||
}
|
||||
|
||||
{IP6}{OWS}"/"{OWS}{D} {
|
||||
int len = 0;
|
||||
string ip = extract_ip_and_len(yytext, &len);
|
||||
rules_lval.prefixval = new IPPrefix(IPAddr(ip), len, true);
|
||||
return TOK_IP6;
|
||||
}
|
||||
|
||||
[!\]\[{}&:,] return rules_text[0];
|
||||
|
||||
"<=" { rules_lval.val = RuleHdrTest::LE; return TOK_COMP; }
|
||||
|
@ -45,7 +61,9 @@ PID ([0-9a-zA-Z_-]|"::")+
|
|||
"!=" { rules_lval.val = RuleHdrTest::NE; return TOK_COMP; }
|
||||
|
||||
ip { rules_lval.val = RuleHdrTest::IP; return TOK_PROT; }
|
||||
ip6 { rules_lval.val = RuleHdrTest::IPv6; return TOK_PROT; }
|
||||
icmp { rules_lval.val = RuleHdrTest::ICMP; return TOK_PROT; }
|
||||
icmp6 { rules_lval.val = RuleHdrTest::ICMPv6; return TOK_PROT; }
|
||||
tcp { rules_lval.val = RuleHdrTest::TCP; return TOK_PROT; }
|
||||
udp { rules_lval.val = RuleHdrTest::UDP; return TOK_PROT; }
|
||||
|
||||
|
@ -123,7 +141,7 @@ http { rules_lval.val = Rule::HTTP_REQUEST; return TOK_PATTERN_TYPE; }
|
|||
ftp { rules_lval.val = Rule::FTP; return TOK_PATTERN_TYPE; }
|
||||
finger { rules_lval.val = Rule::FINGER; return TOK_PATTERN_TYPE; }
|
||||
|
||||
{D}("."{D}){3}"/"{D} {
|
||||
{D}("."{D}){3}{OWS}"/"{OWS}{D} {
|
||||
char* s = strchr(yytext, '/');
|
||||
*s++ = '\0';
|
||||
|
||||
|
|
32
src/scan.l
32
src/scan.l
|
@ -148,6 +148,7 @@ D [0-9]+
|
|||
HEX [0-9a-fA-F]+
|
||||
IDCOMPONENT [A-Za-z_][A-Za-z_0-9]*
|
||||
ID {IDCOMPONENT}(::{IDCOMPONENT})*
|
||||
IP6 ("["({HEX}:){7}{HEX}"]")|("["0x{HEX}({HEX}|:)*"::"({HEX}|:)*"]")|("["({HEX}|:)*"::"({HEX}|:)*"]")|("["({HEX}|:)*"::"({HEX}|:)*({D}"."){3}{D}"]")
|
||||
FILE [^ \t\n]+
|
||||
PREFIX [^ \t\n]+
|
||||
FLOAT (({D}*"."?{D})|({D}"."?{D}*))([eE][-+]?{D})?
|
||||
|
@ -229,21 +230,23 @@ ESCSEQ (\\([^\n]|[0-7]+|x[[:xdigit:]]+))
|
|||
}
|
||||
|
||||
/* IPv6 literal constant patterns */
|
||||
"["({HEX}:){7}{HEX}"]" {
|
||||
string s(yytext+1);
|
||||
RET_CONST(new AddrVal(s.erase(s.size()-1)))
|
||||
{IP6} {
|
||||
RET_CONST(new AddrVal(extract_ip(yytext)))
|
||||
}
|
||||
"["0x{HEX}({HEX}|:)*"::"({HEX}|:)*"]" {
|
||||
string s(yytext+3);
|
||||
RET_CONST(new AddrVal(s.erase(s.size()-1)))
|
||||
|
||||
{IP6}{OWS}"/"{OWS}{D} {
|
||||
int len = 0;
|
||||
string ip = extract_ip_and_len(yytext, &len);
|
||||
RET_CONST(new SubNetVal(IPPrefix(IPAddr(ip), len, true)))
|
||||
}
|
||||
"["({HEX}|:)*"::"({HEX}|:)*"]" {
|
||||
string s(yytext+1);
|
||||
RET_CONST(new AddrVal(s.erase(s.size()-1)))
|
||||
}
|
||||
"["({HEX}|:)*"::"({HEX}|:)*({D}"."){3}{D}"]" {
|
||||
string s(yytext+1);
|
||||
RET_CONST(new AddrVal(s.erase(s.size()-1)))
|
||||
|
||||
/* IPv4 literal constant patterns */
|
||||
({D}"."){3}{D} RET_CONST(new AddrVal(yytext))
|
||||
|
||||
({D}"."){3}{D}{OWS}"/"{OWS}{D} {
|
||||
int len = 0;
|
||||
string ip = extract_ip_and_len(yytext, &len);
|
||||
RET_CONST(new SubNetVal(IPPrefix(IPAddr(ip), len)))
|
||||
}
|
||||
|
||||
[!%*/+\-,:;<=>?()\[\]{}~$|] return yytext[0];
|
||||
|
@ -284,6 +287,7 @@ for return TOK_FOR;
|
|||
function return TOK_FUNCTION;
|
||||
global return TOK_GLOBAL;
|
||||
"?$" return TOK_HAS_FIELD;
|
||||
hook return TOK_HOOK;
|
||||
if return TOK_IF;
|
||||
in return TOK_IN;
|
||||
"!"{OWS}in/[^A-Za-z0-9] return TOK_NOT_IN; /* don't confuse w "! infoo"! */
|
||||
|
@ -484,8 +488,6 @@ F RET_CONST(new Val(false, TYPE_BOOL))
|
|||
{FLOAT}{OWS}msec(s?) RET_CONST(new IntervalVal(atof(yytext),Milliseconds))
|
||||
{FLOAT}{OWS}usec(s?) RET_CONST(new IntervalVal(atof(yytext),Microseconds))
|
||||
|
||||
({D}"."){3}{D} RET_CONST(new AddrVal(yytext))
|
||||
|
||||
"0x"{HEX}+ RET_CONST(new Val(static_cast<bro_uint_t>(strtoull(yytext, 0, 16)), TYPE_COUNT))
|
||||
|
||||
{H}("."{H})+ RET_CONST(dns_mgr->LookupHost(yytext))
|
||||
|
|
|
@ -156,6 +156,13 @@ type readdir_reply_t: record;
|
|||
|
||||
type fsstat_t: record;
|
||||
|
||||
|
||||
module GLOBAL;
|
||||
|
||||
type ModbusHeaders: record;
|
||||
type ModbusCoils: vector;
|
||||
type ModbusRegisters: vector;
|
||||
|
||||
module Log;
|
||||
|
||||
enum Writer %{
|
||||
|
|
34
src/util.cc
34
src/util.cc
|
@ -43,6 +43,40 @@
|
|||
#include "Net.h"
|
||||
#include "Reporter.h"
|
||||
|
||||
/**
|
||||
* Return IP address without enclosing brackets and any leading 0x.
|
||||
*/
|
||||
std::string extract_ip(const std::string& i)
|
||||
{
|
||||
std::string s(skip_whitespace(i.c_str()));
|
||||
if ( s.size() > 0 && s[0] == '[' )
|
||||
s.erase(0, 1);
|
||||
|
||||
if ( s.size() > 1 && s.substr(0, 2) == "0x" )
|
||||
s.erase(0, 2);
|
||||
|
||||
size_t pos = 0;
|
||||
if ( (pos = s.find(']')) != std::string::npos )
|
||||
s = s.substr(0, pos);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a subnet string, return IP address and subnet length separately.
|
||||
*/
|
||||
std::string extract_ip_and_len(const std::string& i, int* len)
|
||||
{
|
||||
size_t pos = i.find('/');
|
||||
if ( pos == std::string::npos )
|
||||
return i;
|
||||
|
||||
if ( len )
|
||||
*len = atoi(i.substr(pos + 1).c_str());
|
||||
|
||||
return extract_ip(i.substr(0, pos));
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a string, unescapes all characters that are escaped as hex codes
|
||||
* (\x##) and turns them into the equivalent ascii-codes. Returns a string
|
||||
|
|
|
@ -91,6 +91,9 @@ void delete_each(T* t)
|
|||
delete *it;
|
||||
}
|
||||
|
||||
std::string extract_ip(const std::string& i);
|
||||
std::string extract_ip_and_len(const std::string& i, int* len);
|
||||
|
||||
std::string get_unescaped_string(const std::string& str);
|
||||
std::string get_escaped_string(const std::string& str, bool escape_all);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue