Merge remote-tracking branch 'origin/master' into topic/seth/metrics-merge

This commit is contained in:
Seth Hall 2013-01-04 20:49:37 -05:00
commit 89bc65f23c
51 changed files with 2112 additions and 485 deletions

60
CHANGES
View file

@ -1,4 +1,64 @@
2.1-265 | 2012-12-20 17:38:42 -0800
* Add array-style index accessor for strings. Addresses #422. (Jon
Siwek)
The index expression can take up to two indices for the start and
end index of the substring to return (e.g. "mystring[1,3]").
Negative indices are allowed, with -1 representing the last
character in the string. The indexing is not cyclic -- if the
starting index is >= the length of the string an empty string is
returned, and if the ending index is >= the length of the string
then it's interpreted as the last index of the string. Assigning
to substrings accessed like this isn't allowed.
2.1-263 | 2012-12-20 16:22:09 -0800
* Bro's language now has a new set of types "opaque of X". (Matthias
Vallentin)
Opaque values can be passed around like other values but they can
only be manipulated with BiF functions, not with other operators.
Currently, the following opaque types are supported:
- opaque of md5
- opaque of sha1
- opaque of sha256
- opaquey of entropy.
They go along with the corrsponding BiF functions md5_*, sha1_*,
sha256_*, and entropy_*, respectively. Note that these functions
have changed their signatures to work with opaques types rather
than global state as it was before.
2.1-240 | 2012-12-20 15:21:07 -0800
* Improve error for invalid use of types as values. Addresses #923.
(Jon Siwek)
2.1-238 | 2012-12-20 15:11:25 -0800
* Finish implementation of script-layer switch statement. Addresses
#754. (Jon Siwek)
They behave like C-style switches except case labels can be
comprised of multiple literal constants delimited by commas. Only
atomic types are allowed for now. Case label bodies that don't
execute a "return" or "break" statement will fall through to
subsequent cases. A default case label is allowed.
* Fix a case where c$resp$size is misrepresented. Addresses #730.
(Jon Siwek)
2.1-234 | 2012-12-20 12:12:19 -0800
* Fix return value of hook calls that have no handlers. For this
case, the return value is always true. (Jon Siwek)
* Fix to_port() BIF for port strings with a port number of zero.
(Jon Siwek)
2.1-231 | 2012-12-14 14:51:35 -0800
* Make const variables actually constant. Both local and global

30
NEWS
View file

@ -24,6 +24,33 @@ New Functionality
- The ASCII writer can now output CSV files on a per filter basis.
- Bro's language now has a working "switch" statement that generally
behaves like C-style switches except case labels can be comprised of
multiple literal constants delimited by commas. Only atomic types
are allowed for now. Case label bodies that don't execute a
"return" or "break" statement will fall through to subsequent cases.
A default case label is allowed.
- Bro's language now has a new set of types "opaque of X". Opaque
values can be passed around like other values but they can only be
manipulated with BiF functions, not with other operators. Currently,
the following opaque types are supported:
- opaque of md5
- opaque of sha1
- opaque of sha256
- opaquey of entropy.
They go along with the corrsponding BiF functions md5_*, sha1_*,
sha256_*, and entropy_*, respectively. Note that these functions
have changed their signatures to work with opaques types rather
than global state as it was before.
- Strings now support the subscript operator to extract individual
characters and substrings (e.g., s[4], s[1,5]). The index expression
can take up to two indices for the start and end index of the
substring to return (e.g. "mystring[1,3]").
Changed Functionality
~~~~~~~~~~~~~~~~~~~~~
@ -37,6 +64,9 @@ Changed Functionality
make_connection_persistent(), generate_idmef(),
split_complete()
- md5_*, sha1_*, sha256_*, and entropy_* have all changed
their signatures to work with opaque types (see above).
- Removed a now unused argument from "do_split" helper function.
- "this" is no longer a reserved keyword.

View file

@ -1 +1 @@
2.1-231
2.1-265

@ -1 +1 @@
Subproject commit 06682dbb15d26d2688bdc9ad76efec17d38dc80f
Subproject commit 073404dd29dc6e90ff0e4eb8bc836f8adbf3931e

@ -1 +1 @@
Subproject commit f4d6a2af15404dc1349d12d2ad21a3eebcb2ff1e
Subproject commit 2bf6b37177b895329173acac2bb98f38a8783bc1

@ -1 +1 @@
Subproject commit d83e10c5f76cbfdf81c843575351fbc7b544fc93
Subproject commit d9c747f268ca50275f3bc2e1581464b599d5a3ea

View file

@ -13,16 +13,16 @@ export {
redef record Info += {
## MD5 sum for a file transferred over HTTP calculated from the
## response body.
md5: string &log &optional;
md5: string &log &optional;
## This value can be set per-transfer to determine per request
## if a file should have an MD5 sum generated. It must be
## set to T at the time of or before the first chunk of body data.
calc_md5: bool &default=F;
calc_md5: bool &default=F;
## Indicates if an MD5 sum is being calculated for the current
## request/response pair.
calculating_md5: bool &default=F;
md5_handle: opaque of md5 &optional;
};
## Generate MD5 sums for these filetypes.
@ -41,13 +41,12 @@ event http_entity_data(c: connection, is_orig: bool, length: count, data: string
if ( c$http$calc_md5 ||
(c$http?$mime_type && generate_md5 in c$http$mime_type) )
{
c$http$calculating_md5 = T;
md5_hash_init(c$id);
c$http$md5_handle = md5_hash_init();
}
}
if ( c$http$calculating_md5 )
md5_hash_update(c$id, data);
if ( c$http?$md5_handle )
md5_hash_update(c$http$md5_handle, data);
}
## In the event of a content gap during a file transfer, detect the state for
@ -55,11 +54,11 @@ event http_entity_data(c: connection, is_orig: bool, length: count, data: string
## incorrect anyway.
event content_gap(c: connection, is_orig: bool, seq: count, length: count) &priority=5
{
if ( is_orig || ! c?$http || ! c$http$calculating_md5 ) return;
if ( is_orig || ! c?$http || ! c$http?$md5_handle ) return;
set_state(c, F, is_orig);
c$http$calculating_md5 = F;
md5_hash_finish(c$id);
md5_hash_finish(c$http$md5_handle); # Ignore return value.
delete c$http$md5_handle;
}
## When the file finishes downloading, finish the hash and generate a notice.
@ -67,11 +66,11 @@ event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) &
{
if ( is_orig || ! c?$http ) return;
if ( c$http$calculating_md5 )
if ( c$http?$md5_handle )
{
local url = build_url_http(c$http);
c$http$calculating_md5 = F;
c$http$md5 = md5_hash_finish(c$id);
c$http$md5 = md5_hash_finish(c$http$md5_handle);
delete c$http$md5_handle;
NOTICE([$note=MD5, $msg=fmt("%s %s %s", c$id$orig_h, c$http$md5, url),
$sub=c$http$md5, $conn=c, $URL=url]);
@ -82,11 +81,12 @@ event connection_state_remove(c: connection) &priority=-5
{
if ( c?$http_state &&
c$http_state$current_response in c$http_state$pending &&
c$http_state$pending[c$http_state$current_response]$calculating_md5 )
c$http_state$pending[c$http_state$current_response]?$md5_handle )
{
# The MD5 sum isn't going to be saved anywhere since the entire
# body wouldn't have been seen anyway and we'd just be giving an
# incorrect MD5 sum.
md5_hash_finish(c$id);
md5_hash_finish(c$http$md5_handle);
delete c$http$md5_handle;
}
}

View file

@ -16,33 +16,33 @@ export {
type EntityInfo: record {
## This is the timestamp of when the MIME content transfer began.
ts: time &log;
uid: string &log;
id: conn_id &log;
ts: time &log;
uid: string &log;
id: conn_id &log;
## A count to represent the depth of this message transaction in a
## single connection where multiple messages were transferred.
trans_depth: count &log;
trans_depth: count &log;
## The filename seen in the Content-Disposition header.
filename: string &log &optional;
filename: string &log &optional;
## Track how many bytes of the MIME encoded file have been seen.
content_len: count &log &default=0;
content_len: count &log &default=0;
## The mime type of the entity discovered through magic bytes identification.
mime_type: string &log &optional;
mime_type: string &log &optional;
## The calculated MD5 sum for the MIME entity.
md5: string &log &optional;
md5: string &log &optional;
## Optionally calculate the file's MD5 sum. Must be set prior to the
## first data chunk being see in an event.
calc_md5: bool &default=F;
calc_md5: bool &default=F;
## This boolean value indicates if an MD5 sum is being calculated
## for the current file transfer.
calculating_md5: bool &default=F;
md5_handle: opaque of md5 &optional;
## Optionally write the file to disk. Must be set prior to first
## data chunk being seen in an event.
extract_file: bool &default=F;
extract_file: bool &default=F;
## Store the file handle here for the file currently being extracted.
extraction_file: file &log &optional;
extraction_file: file &log &optional;
};
redef record Info += {
@ -126,18 +126,16 @@ event mime_segment_data(c: connection, length: count, data: string) &priority=-5
if ( c$smtp$current_entity$content_len == 0 )
{
if ( generate_md5 in c$smtp$current_entity$mime_type && ! never_calc_md5 )
c$smtp$current_entity$calc_md5 = T;
local entity = c$smtp$current_entity;
if ( generate_md5 in entity$mime_type && ! never_calc_md5 )
entity$calc_md5 = T;
if ( c$smtp$current_entity$calc_md5 )
{
c$smtp$current_entity$calculating_md5 = T;
md5_hash_init(c$id);
}
if ( entity$calc_md5 )
entity$md5_handle = md5_hash_init();
}
if ( c$smtp$current_entity$calculating_md5 )
md5_hash_update(c$id, data);
if ( c$smtp$current_entity?$md5_handle )
md5_hash_update(entity$md5_handle, data);
}
## In the event of a content gap during the MIME transfer, detect the state for
@ -147,10 +145,11 @@ event content_gap(c: connection, is_orig: bool, seq: count, length: count) &prio
{
if ( is_orig || ! c?$smtp || ! c$smtp?$current_entity ) return;
if ( c$smtp$current_entity$calculating_md5 )
local entity = c$smtp$current_entity;
if ( entity?$md5_handle )
{
c$smtp$current_entity$calculating_md5 = F;
md5_hash_finish(c$id);
md5_hash_finish(entity$md5_handle);
delete entity$md5_handle;
}
}
@ -161,12 +160,14 @@ event mime_end_entity(c: connection) &priority=-3
if ( ! c?$smtp || ! c$smtp?$current_entity )
return;
if ( c$smtp$current_entity$calculating_md5 )
local entity = c$smtp$current_entity;
if ( entity?$md5_handle )
{
c$smtp$current_entity$md5 = md5_hash_finish(c$id);
entity$md5 = md5_hash_finish(entity$md5_handle);
delete entity$md5_handle;
NOTICE([$note=MD5, $msg=fmt("Calculated a hash for a MIME entity from %s", c$id$orig_h),
$sub=c$smtp$current_entity$md5, $conn=c]);
$sub=entity$md5, $conn=c]);
}
}

View file

@ -28,7 +28,7 @@ event signature_match(state: signature_state, msg: string, data: string) &priori
if ( /^webapp-/ !in state$sig_id ) return;
local c = state$conn;
local si = Software::Info;
local si: Software::Info;
si = [$name=msg, $unparsed_version=msg, $host=c$id$resp_h, $host_p=c$id$resp_p, $software_type=WEB_APPLICATION];
si$url = build_url_http(c$http);
if ( c$id$resp_h in Software::tracked &&

View file

@ -361,6 +361,7 @@ set(bro_SRCS
NetVar.cc
NetbiosSSN.cc
Obj.cc
OpaqueVal.cc
OSFinger.cc
PacketFilter.cc
PacketSort.cc

View file

@ -233,7 +233,11 @@ NameExpr::NameExpr(ID* arg_id, bool const_init) : Expr(EXPR_NAME)
{
id = arg_id;
in_const_init = const_init;
SetType(id->Type()->Ref());
if ( id->AsType() )
SetType(new TypeType(id->AsType()));
else
SetType(id->Type()->Ref());
EventHandler* h = event_registry->Lookup(id->Name());
if ( h )
@ -2808,11 +2812,17 @@ IndexExpr::IndexExpr(Expr* arg_op1, ListExpr* arg_op2)
SetError("not an index type");
else if ( ! op1->Type()->YieldType() )
{
if ( IsString(op1->Type()->Tag()) &&
match_type == MATCHES_INDEX_SCALAR )
SetType(base_type(TYPE_STRING));
else
// It's a set - so indexing it yields void. We don't
// directly generate an error message, though, since this
// expression might be part of an add/delete statement,
// rather than yielding a value.
SetType(base_type(TYPE_VOID));
SetType(base_type(TYPE_VOID));
}
else if ( match_type == MATCHES_INDEX_SCALAR )
SetType(op1->Type()->YieldType()->Ref());
@ -2888,6 +2898,9 @@ void IndexExpr::Delete(Frame* f)
Expr* IndexExpr::MakeLvalue()
{
if ( IsString(op1->Type()->Tag()) )
ExprError("cannot assign to string index expression");
return new RefExpr(this);
}
@ -2961,10 +2974,37 @@ Val* IndexExpr::Fold(Val* v1, Val* v2) const
Val* v = 0;
if ( v1->Type()->Tag() == TYPE_VECTOR )
switch ( v1->Type()->Tag() ) {
case TYPE_VECTOR:
v = v1->AsVectorVal()->Lookup(v2);
else
break;
case TYPE_TABLE:
v = v1->AsTableVal()->Lookup(v2);
break;
case TYPE_STRING:
{
const ListVal* lv = v2->AsListVal();
const BroString* s = v1->AsString();
int len = s->Len();
bro_int_t first = lv->Index(0)->AsInt();
bro_int_t last = lv->Length() > 1 ? lv->Index(1)->AsInt() : first;
if ( first < 0 )
first += len;
if ( last < 0 )
last += len;
BroString* substring = s->GetSubstring(first, last - first + 1);
return new StringVal(substring ? substring : new BroString(""));
}
default:
Error("type cannot be indexed");
break;
}
if ( v )
return v->Ref();
@ -2991,14 +3031,25 @@ void IndexExpr::Assign(Frame* f, Val* v, Opcode op)
return;
}
if ( v1->Type()->Tag() == TYPE_VECTOR )
{
switch ( v1->Type()->Tag() ) {
case TYPE_VECTOR:
if ( ! v1->AsVectorVal()->Assign(v2, v, this, op) )
Internal("assignment failed");
}
break;
else if ( ! v1->AsTableVal()->Assign(v2, v, op) )
Internal("assignment failed");
case TYPE_TABLE:
if ( ! v1->AsTableVal()->Assign(v2, v, op) )
Internal("assignment failed");
break;
case TYPE_STRING:
Internal("assignment via string index accessor not allowed");
break;
default:
Internal("bad index expression type in assignment");
break;
}
Unref(v1);
Unref(v2);

View file

@ -282,13 +282,14 @@ Val* BroFunc::Call(val_list* args, Frame* parent) const
#ifdef PROFILE_BRO_FUNCTIONS
DEBUG_MSG("Function: %s\n", id->Name());
#endif
if ( ! bodies.size() )
if ( ! bodies.size() )
{
// 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 ;
return Flavor() == FUNC_FLAVOR_HOOK ? new Val(true, TYPE_BOOL) : 0;
}
SegmentProfiler(segment_logger, location);

501
src/OpaqueVal.cc Normal file
View file

@ -0,0 +1,501 @@
#include "OpaqueVal.h"
#include "Reporter.h"
#include "Serializer.h"
bool HashVal::IsValid() const
{
return valid;
}
bool HashVal::Init()
{
if ( valid )
return false;
valid = DoInit();
return valid;
}
StringVal* HashVal::Get()
{
if ( ! valid )
return new StringVal("");
StringVal* result = DoGet();
valid = false;
return result;
}
bool HashVal::Feed(const void* data, size_t size)
{
if ( valid )
return DoFeed(data, size);
reporter->InternalError("invalid opaque hash value");
return false;
}
bool HashVal::DoInit()
{
assert(! "missing implementation of DoInit()");
return false;
}
bool HashVal::DoFeed(const void*, size_t)
{
assert(! "missing implementation of DoFeed()");
return false;
}
StringVal* HashVal::DoGet()
{
assert(! "missing implementation of DoGet()");
return new StringVal("");
}
HashVal::HashVal(OpaqueType* t) : OpaqueVal(t)
{
valid = false;
}
IMPLEMENT_SERIAL(HashVal, SER_HASH_VAL);
bool HashVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_HASH_VAL, OpaqueVal);
return SERIALIZE(valid);
}
bool HashVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(OpaqueVal);
return UNSERIALIZE(&valid);
}
void MD5Val::digest(val_list& vlist, u_char result[MD5_DIGEST_LENGTH])
{
MD5_CTX h;
md5_init(&h);
loop_over_list(vlist, i)
{
Val* v = vlist[i];
if ( v->Type()->Tag() == TYPE_STRING )
{
const BroString* str = v->AsString();
md5_update(&h, str->Bytes(), str->Len());
}
else
{
ODesc d(DESC_BINARY);
v->Describe(&d);
md5_update(&h, (const u_char *) d.Bytes(), d.Len());
}
}
md5_final(&h, result);
}
void MD5Val::hmac(val_list& vlist,
u_char key[MD5_DIGEST_LENGTH],
u_char result[MD5_DIGEST_LENGTH])
{
digest(vlist, result);
for ( int i = 0; i < MD5_DIGEST_LENGTH; ++i )
result[i] ^= key[i];
MD5(result, MD5_DIGEST_LENGTH, result);
}
bool MD5Val::DoInit()
{
assert(! IsValid());
md5_init(&ctx);
return true;
}
bool MD5Val::DoFeed(const void* data, size_t size)
{
if ( ! IsValid() )
return false;
md5_update(&ctx, data, size);
return true;
}
StringVal* MD5Val::DoGet()
{
if ( ! IsValid() )
return new StringVal("");
u_char digest[MD5_DIGEST_LENGTH];
md5_final(&ctx, digest);
return new StringVal(md5_digest_print(digest));
}
IMPLEMENT_SERIAL(MD5Val, SER_MD5_VAL);
bool MD5Val::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_MD5_VAL, HashVal);
if ( ! IsValid() )
return true;
if ( ! (SERIALIZE(ctx.A) &&
SERIALIZE(ctx.B) &&
SERIALIZE(ctx.C) &&
SERIALIZE(ctx.D) &&
SERIALIZE(ctx.Nl) &&
SERIALIZE(ctx.Nh)) )
return false;
for ( int i = 0; i < MD5_LBLOCK; ++i )
{
if ( ! SERIALIZE(ctx.data[i]) )
return false;
}
if ( ! SERIALIZE(ctx.num) )
return false;
return true;
}
bool MD5Val::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(HashVal);
if ( ! IsValid() )
return true;
if ( ! (UNSERIALIZE(&ctx.A) &&
UNSERIALIZE(&ctx.B) &&
UNSERIALIZE(&ctx.C) &&
UNSERIALIZE(&ctx.D) &&
UNSERIALIZE(&ctx.Nl) &&
UNSERIALIZE(&ctx.Nh)) )
return false;
for ( int i = 0; i < MD5_LBLOCK; ++i )
{
if ( ! UNSERIALIZE(&ctx.data[i]) )
return false;
}
if ( ! UNSERIALIZE(&ctx.num) )
return false;
return true;
}
void SHA1Val::digest(val_list& vlist, u_char result[SHA_DIGEST_LENGTH])
{
SHA_CTX h;
sha1_init(&h);
loop_over_list(vlist, i)
{
Val* v = vlist[i];
if ( v->Type()->Tag() == TYPE_STRING )
{
const BroString* str = v->AsString();
sha1_update(&h, str->Bytes(), str->Len());
}
else
{
ODesc d(DESC_BINARY);
v->Describe(&d);
sha1_update(&h, (const u_char *) d.Bytes(), d.Len());
}
}
sha1_final(&h, result);
}
bool SHA1Val::DoInit()
{
assert(! IsValid());
sha1_init(&ctx);
return true;
}
bool SHA1Val::DoFeed(const void* data, size_t size)
{
if ( ! IsValid() )
return false;
sha1_update(&ctx, data, size);
return true;
}
StringVal* SHA1Val::DoGet()
{
if ( ! IsValid() )
return new StringVal("");
u_char digest[SHA_DIGEST_LENGTH];
sha1_final(&ctx, digest);
return new StringVal(sha1_digest_print(digest));
}
IMPLEMENT_SERIAL(SHA1Val, SER_SHA1_VAL);
bool SHA1Val::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_SHA1_VAL, HashVal);
if ( ! IsValid() )
return true;
if ( ! (SERIALIZE(ctx.h0) &&
SERIALIZE(ctx.h1) &&
SERIALIZE(ctx.h2) &&
SERIALIZE(ctx.h3) &&
SERIALIZE(ctx.h4) &&
SERIALIZE(ctx.Nl) &&
SERIALIZE(ctx.Nh)) )
return false;
for ( int i = 0; i < SHA_LBLOCK; ++i )
{
if ( ! SERIALIZE(ctx.data[i]) )
return false;
}
if ( ! SERIALIZE(ctx.num) )
return false;
return true;
}
bool SHA1Val::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(HashVal);
if ( ! IsValid() )
return true;
if ( ! (UNSERIALIZE(&ctx.h0) &&
UNSERIALIZE(&ctx.h1) &&
UNSERIALIZE(&ctx.h2) &&
UNSERIALIZE(&ctx.h3) &&
UNSERIALIZE(&ctx.h4) &&
UNSERIALIZE(&ctx.Nl) &&
UNSERIALIZE(&ctx.Nh)) )
return false;
for ( int i = 0; i < SHA_LBLOCK; ++i )
{
if ( ! UNSERIALIZE(&ctx.data[i]) )
return false;
}
if ( ! UNSERIALIZE(&ctx.num) )
return false;
return true;
}
void SHA256Val::digest(val_list& vlist, u_char result[SHA256_DIGEST_LENGTH])
{
SHA256_CTX h;
sha256_init(&h);
loop_over_list(vlist, i)
{
Val* v = vlist[i];
if ( v->Type()->Tag() == TYPE_STRING )
{
const BroString* str = v->AsString();
sha256_update(&h, str->Bytes(), str->Len());
}
else
{
ODesc d(DESC_BINARY);
v->Describe(&d);
sha256_update(&h, (const u_char *) d.Bytes(), d.Len());
}
}
sha256_final(&h, result);
}
bool SHA256Val::DoInit()
{
assert( ! IsValid() );
sha256_init(&ctx);
return true;
}
bool SHA256Val::DoFeed(const void* data, size_t size)
{
if ( ! IsValid() )
return false;
sha256_update(&ctx, data, size);
return true;
}
StringVal* SHA256Val::DoGet()
{
if ( ! IsValid() )
return new StringVal("");
u_char digest[SHA256_DIGEST_LENGTH];
sha256_final(&ctx, digest);
return new StringVal(sha256_digest_print(digest));
}
IMPLEMENT_SERIAL(SHA256Val, SER_SHA256_VAL);
bool SHA256Val::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_SHA256_VAL, HashVal);
if ( ! IsValid() )
return true;
for ( int i = 0; i < 8; ++i )
{
if ( ! SERIALIZE(ctx.h[i]) )
return false;
}
if ( ! (SERIALIZE(ctx.Nl) &&
SERIALIZE(ctx.Nh)) )
return false;
for ( int i = 0; i < SHA_LBLOCK; ++i )
{
if ( ! SERIALIZE(ctx.data[i]) )
return false;
}
if ( ! (SERIALIZE(ctx.num) &&
SERIALIZE(ctx.md_len)) )
return false;
return true;
}
bool SHA256Val::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(HashVal);
if ( ! IsValid() )
return true;
for ( int i = 0; i < 8; ++i )
{
if ( ! UNSERIALIZE(&ctx.h[i]) )
return false;
}
if ( ! (UNSERIALIZE(&ctx.Nl) &&
UNSERIALIZE(&ctx.Nh)) )
return false;
for ( int i = 0; i < SHA_LBLOCK; ++i )
{
if ( ! UNSERIALIZE(&ctx.data[i]) )
return false;
}
if ( ! (UNSERIALIZE(&ctx.num) &&
UNSERIALIZE(&ctx.md_len)) )
return false;
return true;
}
bool EntropyVal::Feed(const void* data, size_t size)
{
state.add(data, size);
return true;
}
bool EntropyVal::Get(double *r_ent, double *r_chisq, double *r_mean,
double *r_montepicalc, double *r_scc)
{
state.end(r_ent, r_chisq, r_mean, r_montepicalc, r_scc);
return true;
}
IMPLEMENT_SERIAL(EntropyVal, SER_ENTROPY_VAL);
bool EntropyVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_ENTROPY_VAL, OpaqueVal);
for ( int i = 0; i < 256; ++i )
{
if ( ! SERIALIZE(state.ccount[i]) )
return false;
}
if ( ! (SERIALIZE(state.totalc) &&
SERIALIZE(state.mp) &&
SERIALIZE(state.sccfirst)) )
return false;
for ( int i = 0; i < RT_MONTEN; ++i )
{
if ( ! SERIALIZE(state.monte[i]) )
return false;
}
if ( ! (SERIALIZE(state.inmont) &&
SERIALIZE(state.mcount) &&
SERIALIZE(state.cexp) &&
SERIALIZE(state.montex) &&
SERIALIZE(state.montey) &&
SERIALIZE(state.montepi) &&
SERIALIZE(state.sccu0) &&
SERIALIZE(state.scclast) &&
SERIALIZE(state.scct1) &&
SERIALIZE(state.scct2) &&
SERIALIZE(state.scct3)) )
return false;
return true;
}
bool EntropyVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(OpaqueVal);
for ( int i = 0; i < 256; ++i )
{
if ( ! UNSERIALIZE(&state.ccount[i]) )
return false;
}
if ( ! (UNSERIALIZE(&state.totalc) &&
UNSERIALIZE(&state.mp) &&
UNSERIALIZE(&state.sccfirst)) )
return false;
for ( int i = 0; i < RT_MONTEN; ++i )
{
if ( ! UNSERIALIZE(&state.monte[i]) )
return false;
}
if ( ! (UNSERIALIZE(&state.inmont) &&
UNSERIALIZE(&state.mcount) &&
UNSERIALIZE(&state.cexp) &&
UNSERIALIZE(&state.montex) &&
UNSERIALIZE(&state.montey) &&
UNSERIALIZE(&state.montepi) &&
UNSERIALIZE(&state.sccu0) &&
UNSERIALIZE(&state.scclast) &&
UNSERIALIZE(&state.scct1) &&
UNSERIALIZE(&state.scct2) &&
UNSERIALIZE(&state.scct3)) )
return false;
return true;
}

110
src/OpaqueVal.h Normal file
View file

@ -0,0 +1,110 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef OPAQUEVAL_H
#define OPAQUEVAL_H
#include "RandTest.h"
#include "Val.h"
#include "digest.h"
class HashVal : public OpaqueVal {
public:
virtual bool IsValid() const;
virtual bool Init();
virtual bool Feed(const void* data, size_t size);
virtual StringVal* Get();
protected:
HashVal() { };
HashVal(OpaqueType* t);
virtual bool DoInit();
virtual bool DoFeed(const void* data, size_t size);
virtual StringVal* DoGet();
DECLARE_SERIAL(HashVal);
private:
// This flag exists because Get() can only be called once.
bool valid;
};
class MD5Val : public HashVal {
public:
static void digest(val_list& vlist, u_char result[MD5_DIGEST_LENGTH]);
static void hmac(val_list& vlist,
u_char key[MD5_DIGEST_LENGTH],
u_char result[MD5_DIGEST_LENGTH]);
MD5Val() : HashVal(new OpaqueType("md5")) { }
protected:
friend class Val;
virtual bool DoInit() /* override */;
virtual bool DoFeed(const void* data, size_t size) /* override */;
virtual StringVal* DoGet() /* override */;
DECLARE_SERIAL(MD5Val);
private:
MD5_CTX ctx;
};
class SHA1Val : public HashVal {
public:
static void digest(val_list& vlist, u_char result[SHA_DIGEST_LENGTH]);
SHA1Val() : HashVal(new OpaqueType("sha1")) { }
protected:
friend class Val;
virtual bool DoInit() /* override */;
virtual bool DoFeed(const void* data, size_t size) /* override */;
virtual StringVal* DoGet() /* override */;
DECLARE_SERIAL(SHA1Val);
private:
SHA_CTX ctx;
};
class SHA256Val : public HashVal {
public:
static void digest(val_list& vlist, u_char result[SHA256_DIGEST_LENGTH]);
SHA256Val() : HashVal(new OpaqueType("sha256")) { }
protected:
friend class Val;
virtual bool DoInit() /* override */;
virtual bool DoFeed(const void* data, size_t size) /* override */;
virtual StringVal* DoGet() /* override */;
DECLARE_SERIAL(SHA256Val);
private:
SHA256_CTX ctx;
};
class EntropyVal : public OpaqueVal {
public:
EntropyVal() : OpaqueVal(new OpaqueType("entropy")) { }
bool Feed(const void* data, size_t size);
bool Get(double *r_ent, double *r_chisq, double *r_mean,
double *r_montepicalc, double *r_scc);
protected:
friend class Val;
EntropyVal(OpaqueType* t);
DECLARE_SERIAL(EntropyVal);
private:
RandTest state;
};
#endif

View file

@ -12,7 +12,18 @@
Modified for Bro by Seth Hall - July 2010
*/
#include <RandTest.h>
#include <math.h>
#include "RandTest.h"
#define log2of10 3.32192809488736234787
/* RT_LOG2 -- Calculate log to the base 2 */
static double rt_log2(double x)
{
return log2of10 * log10(x);
}
// RT_INCIRC = pow(pow(256.0, (double) (RT_MONTEN / 2)) - 1, 2.0);
#define RT_INCIRC 281474943156225.0
RandTest::RandTest()
{
@ -28,9 +39,9 @@ RandTest::RandTest()
}
}
void RandTest::add(void *buf, int bufl)
void RandTest::add(const void *buf, int bufl)
{
unsigned char *bp = (unsigned char*)buf;
const unsigned char *bp = static_cast<const unsigned char*>(buf);
int oc;
while (bufl-- > 0)
@ -78,8 +89,8 @@ void RandTest::add(void *buf, int bufl)
}
}
void RandTest::end(double *r_ent, double *r_chisq,
double *r_mean, double *r_montepicalc, double *r_scc)
void RandTest::end(double* r_ent, double* r_chisq,
double* r_mean, double* r_montepicalc, double* r_scc)
{
int i;
double ent, chisq, scc, datasum;

View file

@ -1,34 +1,33 @@
#include <math.h>
#ifndef RANDTEST_H
#define RANDTEST_H
#define log2of10 3.32192809488736234787
/* RT_LOG2 -- Calculate log to the base 2 */
static double rt_log2(double x)
{
return log2of10 * log10(x);
}
#include "util.h"
#define RT_MONTEN 6 /* Bytes used as Monte Carlo
co-ordinates. This should be no more
bits than the mantissa of your "double"
floating point type. */
class EntropyVal;
// RT_INCIRC = pow(pow(256.0, (double) (RT_MONTEN / 2)) - 1, 2.0);
#define RT_INCIRC 281474943156225.0
class RandTest {
public:
RandTest();
void add(void *buf, int bufl);
void end(double *r_ent, double *r_chisq, double *r_mean,
double *r_montepicalc, double *r_scc);
void add(const void* buf, int bufl);
void end(double* r_ent, double* r_chisq, double* r_mean,
double* r_montepicalc, double* r_scc);
private:
long ccount[256]; /* Bins to count occurrences of values */
long totalc; /* Total bytes counted */
friend class EntropyVal;
int64 ccount[256]; /* Bins to count occurrences of values */
int64 totalc; /* Total bytes counted */
int mp;
int sccfirst;
unsigned int monte[RT_MONTEN];
long inmont, mcount;
int64 inmont, mcount;
double cexp, montex, montey, montepi,
sccu0, scclast, scct1, scct2, scct3;
};
};
#endif

View file

@ -98,6 +98,12 @@ SERIAL_VAL(RECORD_VAL, 10)
SERIAL_VAL(ENUM_VAL, 11)
SERIAL_VAL(VECTOR_VAL, 12)
SERIAL_VAL(MUTABLE_VAL, 13)
SERIAL_VAL(OPAQUE_VAL, 14)
SERIAL_VAL(HASH_VAL, 15)
SERIAL_VAL(MD5_VAL, 16)
SERIAL_VAL(SHA1_VAL, 17)
SERIAL_VAL(SHA256_VAL, 18)
SERIAL_VAL(ENTROPY_VAL, 19)
#define SERIAL_EXPR(name, val) SERIAL_CONST(name, val, EXPR)
SERIAL_EXPR(EXPR, 1)
@ -178,6 +184,7 @@ SERIAL_TYPE(SUBNET_TYPE, 8)
SERIAL_TYPE(FILE_TYPE, 9)
SERIAL_TYPE(ENUM_TYPE, 10)
SERIAL_TYPE(VECTOR_TYPE, 11)
SERIAL_TYPE(OPAQUE_TYPE, 12)
SERIAL_CONST2(ATTRIBUTES)
SERIAL_CONST2(EVENT_HANDLER)

View file

@ -592,6 +592,21 @@ Case::~Case()
void Case::Describe(ODesc* d) const
{
if ( ! Cases() )
{
if ( ! d->IsBinary() )
d->Add("default:");
d->AddCount(0);
d->PushIndent();
Body()->AccessStats(d);
Body()->Describe(d);
d->PopIndent();
return;
}
const expr_list& e = Cases()->Exprs();
if ( ! d->IsBinary() )
@ -658,13 +673,64 @@ bool Case::DoUnserialize(UnserialInfo* info)
return this->s != 0;
}
SwitchStmt::SwitchStmt(Expr* index, case_list* arg_cases) :
ExprStmt(STMT_SWITCH, index)
static void int_del_func(void* v)
{
cases = arg_cases;
delete (int*) v;
}
//### need to loop over cases and make sure their type matches
//### the index, and they're constant and not redundant
void SwitchStmt::Init()
{
TypeList* t = new TypeList();
t->Append(e->Type()->Ref());
comp_hash = new CompositeHash(t);
Unref(t);
case_label_map.SetDeleteFunc(int_del_func);
}
SwitchStmt::SwitchStmt(Expr* index, case_list* arg_cases) :
ExprStmt(STMT_SWITCH, index), cases(arg_cases), default_case_idx(-1)
{
Init();
if ( ! is_atomic_type(e->Type()) )
e->Error("switch expression must be of an atomic type");
loop_over_list(*cases, i)
{
const Case* c = (*cases)[i];
const ListExpr* le = c->Cases();
if ( le )
{
if ( ! le->Type()->AsTypeList()->AllMatch(e->Type(), false) )
{
le->Error("case expression type differs from switch type", e);
continue;
}
const expr_list& exprs = le->Exprs();
loop_over_list(exprs, j)
{
if ( ! exprs[j]->IsConst() )
exprs[j]->Error("case label expression isn't constant");
else
{
if ( ! AddCaseLabelMapping(exprs[j]->ExprVal(), i) )
exprs[j]->Error("duplicate case label");
}
}
}
else
{
if ( default_case_idx != -1 )
c->Error("multiple default labels", (*cases)[default_case_idx]);
else
default_case_idx = i;
}
}
}
SwitchStmt::~SwitchStmt()
@ -673,12 +739,80 @@ SwitchStmt::~SwitchStmt()
Unref((*cases)[i]);
delete cases;
delete comp_hash;
}
Val* SwitchStmt::DoExec(Frame* /* f */, Val* /* v */, stmt_flow_type& /* flow */) const
bool SwitchStmt::AddCaseLabelMapping(const Val* v, int idx)
{
printf("switch statement not implemented\n");
return 0;
HashKey* hk = comp_hash->ComputeHash(v, 1);
if ( ! hk )
{
reporter->PushLocation(e->GetLocationInfo());
reporter->InternalError("switch expression type mismatch (%s/%s)",
type_name(v->Type()->Tag()), type_name(e->Type()->Tag()));
}
int* label_idx = case_label_map.Lookup(hk);
if ( label_idx )
{
delete hk;
return false;
}
case_label_map.Insert(hk, new int(idx));
return true;
}
int SwitchStmt::FindCaseLabelMatch(const Val* v) const
{
HashKey* hk = comp_hash->ComputeHash(v, 1);
if ( ! hk )
{
reporter->PushLocation(e->GetLocationInfo());
reporter->InternalError("switch expression type mismatch (%s/%s)",
type_name(v->Type()->Tag()), type_name(e->Type()->Tag()));
}
int* label_idx = case_label_map.Lookup(hk);
delete hk;
if ( ! label_idx )
return default_case_idx;
else
return *label_idx;
}
Val* SwitchStmt::DoExec(Frame* f, Val* v, stmt_flow_type& flow) const
{
Val* rval = 0;
int matching_label_idx = FindCaseLabelMatch(v);
if ( matching_label_idx == -1 )
return 0;
for ( int i = matching_label_idx; i < cases->length(); ++i )
{
const Case* c = (*cases)[i];
flow = FLOW_NEXT;
rval = c->Body()->Exec(f, flow);
if ( flow == FLOW_BREAK )
{
flow = FLOW_NEXT;
break;
}
if ( flow == FLOW_RETURN )
break;
}
return rval;
}
Stmt* SwitchStmt::DoSimplify()
@ -697,7 +831,13 @@ Stmt* SwitchStmt::DoSimplify()
}
if ( e->IsConst() )
{ // ### go through cases and pull out the one it matches
{
// Could possibly remove all case labels before the one
// that will match, but may be tricky to tell if any
// subsequent ones can also be removed since it depends
// on the evaluation of the body executing a break/return
// statement. Then still need a way to bypass the lookup
// DoExec for it to be beneficial.
if ( ! optimize )
Warn("constant in switch");
}
@ -770,6 +910,9 @@ bool SwitchStmt::DoSerialize(SerialInfo* info) const
if ( ! (*cases)[i]->Serialize(info) )
return false;
if ( ! SERIALIZE(default_case_idx) )
return false;
return true;
}
@ -777,6 +920,8 @@ bool SwitchStmt::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(ExprStmt);
Init();
int len;
if ( ! UNSERIALIZE(&len) )
return false;
@ -790,6 +935,25 @@ bool SwitchStmt::DoUnserialize(UnserialInfo* info)
cases->append(c);
}
if ( ! UNSERIALIZE(&default_case_idx) )
return false;
loop_over_list(*cases, i)
{
const ListExpr* le = (*cases)[i]->Cases();
if ( ! le )
continue;
const expr_list& exprs = le->Exprs();
loop_over_list(exprs, j)
{
if ( ! AddCaseLabelMapping(exprs[j]->ExprVal(), i) )
return false;
}
}
return true;
}

View file

@ -17,6 +17,8 @@
class StmtList;
class ForStmt;
declare(PDict, int);
class Stmt : public BroObj {
public:
BroStmtTag Tag() const { return tag; }
@ -187,7 +189,8 @@ protected:
class Case : public BroObj {
public:
Case(ListExpr* c, Stmt* arg_s) { cases = c; s = arg_s; }
Case(ListExpr* c, Stmt* arg_s) :
cases(simplify_expr_list(c,SIMPLIFY_GENERAL)), s(arg_s) { }
~Case();
const ListExpr* Cases() const { return cases; }
@ -226,7 +229,7 @@ public:
protected:
friend class Stmt;
SwitchStmt() { cases = 0; }
SwitchStmt() { cases = 0; default_case_idx = -1; comp_hash = 0; }
Val* DoExec(Frame* f, Val* v, stmt_flow_type& flow) const;
Stmt* DoSimplify();
@ -234,7 +237,23 @@ protected:
DECLARE_SERIAL(SwitchStmt);
// Initialize composite hash and case label map.
void Init();
// Adds an entry in case_label_map for the given value to associate it
// with the given index in the cases list. If the entry already exists,
// returns false, else returns true.
bool AddCaseLabelMapping(const Val* v, int idx);
// Returns index of a case label that's equal to the value, or
// default_case_idx if no case label matches (which may be -1 if there's
// no default label).
int FindCaseLabelMatch(const Val* v) const;
case_list* cases;
int default_case_idx;
CompositeHash* comp_hash;
PDict(int) case_label_map;
};
class AddStmt : public ExprStmt {

View file

@ -382,7 +382,7 @@ void TCP_Analyzer::ProcessFIN(double t, TCP_Endpoint* endpoint,
endpoint->FIN_seq = base_seq - endpoint->StartSeq() + seq_len;
}
bool TCP_Analyzer::ProcessRST(double t, TCP_Endpoint* endpoint,
void TCP_Analyzer::ProcessRST(double t, TCP_Endpoint* endpoint,
const IP_Hdr* ip, uint32 base_seq,
int len, int& seq_len)
{
@ -406,11 +406,9 @@ bool TCP_Analyzer::ProcessRST(double t, TCP_Endpoint* endpoint,
}
PacketWithRST();
return true;
}
int TCP_Analyzer::ProcessFlags(double t,
void TCP_Analyzer::ProcessFlags(double t,
const IP_Hdr* ip, const struct tcphdr* tp,
uint32 tcp_hdr_len, int len, int& seq_len,
TCP_Endpoint* endpoint, TCP_Endpoint* peer,
@ -425,14 +423,11 @@ int TCP_Analyzer::ProcessFlags(double t,
if ( flags.FIN() )
ProcessFIN(t, endpoint, seq_len, base_seq);
if ( flags.RST() &&
! ProcessRST(t, endpoint, ip, base_seq, len, seq_len) )
return 0;
if ( flags.RST() )
ProcessRST(t, endpoint, ip, base_seq, len, seq_len);
if ( flags.ACK() )
ProcessACK(endpoint, peer, ack_seq, is_orig, flags);
return 1;
}
void TCP_Analyzer::TransitionFromInactive(double t, TCP_Endpoint* endpoint,
@ -825,10 +820,27 @@ void TCP_Analyzer::UpdateClosedState(double t, TCP_Endpoint* endpoint,
}
}
void TCP_Analyzer::UpdateResetState(int len, TCP_Flags flags)
void TCP_Analyzer::UpdateResetState(int len, TCP_Flags flags,
TCP_Endpoint* endpoint, uint32 base_seq,
uint32 last_seq)
{
if ( flags.SYN() )
{
Weird("SYN_after_reset");
if ( endpoint->prev_state == TCP_ENDPOINT_INACTIVE )
{
// Seq. numbers were initialized by a RST packet from this endpoint,
// but now that a SYN is seen from it, that could mean the earlier
// RST was spoofed/injected, so re-initialize. This mostly just
// helps prevent misrepresentations of payload sizes that are based
// on bad initial sequence values.
endpoint->InitStartSeq(base_seq);
endpoint->InitAckSeq(base_seq);
endpoint->InitLastSeq(last_seq);
}
}
if ( flags.FIN() )
Weird("FIN_after_reset");
@ -871,7 +883,7 @@ void TCP_Analyzer::UpdateStateMachine(double t,
break;
case TCP_ENDPOINT_RESET:
UpdateResetState(len, flags);
UpdateResetState(len, flags, endpoint, base_seq, last_seq);
break;
}
}
@ -996,10 +1008,8 @@ void TCP_Analyzer::DeliverPacket(int len, const u_char* data, bool is_orig,
int seq_len = len; // length in terms of sequence space
if ( ! ProcessFlags(t, ip, tp, tcp_hdr_len, len, seq_len,
endpoint, peer, base_seq, ack_seq,
orig_addr, is_orig, flags) )
return;
ProcessFlags(t, ip, tp, tcp_hdr_len, len, seq_len, endpoint, peer, base_seq,
ack_seq, orig_addr, is_orig, flags);
uint32 last_seq = base_seq + seq_len;

View file

@ -135,13 +135,13 @@ protected:
void ProcessFIN(double t, TCP_Endpoint* endpoint, int& seq_len,
uint32 base_seq);
bool ProcessRST(double t, TCP_Endpoint* endpoint, const IP_Hdr* ip,
void ProcessRST(double t, TCP_Endpoint* endpoint, const IP_Hdr* ip,
uint32 base_seq, int len, int& seq_len);
void ProcessACK(TCP_Endpoint* endpoint, TCP_Endpoint* peer,
uint32 ack_seq, int is_orig, TCP_Flags flags);
int ProcessFlags(double t, const IP_Hdr* ip, const struct tcphdr* tp,
void ProcessFlags(double t, const IP_Hdr* ip, const struct tcphdr* tp,
uint32 tcp_hdr_len, int len, int& seq_len,
TCP_Endpoint* endpoint, TCP_Endpoint* peer,
uint32 base_seq, uint32 ack_seq,
@ -186,7 +186,8 @@ protected:
int delta_last, TCP_Flags flags,
int& do_close);
void UpdateResetState(int len, TCP_Flags flags);
void UpdateResetState(int len, TCP_Flags flags, TCP_Endpoint* endpoint,
uint32 base_seq, uint32 last_seq);
void GeneratePacketEvent(TCP_Endpoint* endpoint, TCP_Endpoint* peer,
uint32 base_seq, uint32 ack_seq,

View file

@ -30,6 +30,7 @@ const char* type_name(TypeTag t)
"table", "union", "record", "types",
"func",
"file",
"opaque",
"vector",
"type",
"error",
@ -96,6 +97,7 @@ BroType::BroType(TypeTag t, bool arg_base_type)
case TYPE_LIST:
case TYPE_FUNC:
case TYPE_FILE:
case TYPE_OPAQUE:
case TYPE_VECTOR:
case TYPE_TYPE:
internal_tag = TYPE_INTERNAL_OTHER;
@ -114,8 +116,17 @@ BroType::~BroType()
delete [] type_id;
}
int BroType::MatchesIndex(ListExpr*& /* index */) const
int BroType::MatchesIndex(ListExpr*& index) const
{
if ( Tag() == TYPE_STRING )
{
if ( index->Exprs().length() != 1 && index->Exprs().length() != 2 )
return DOES_NOT_MATCH_INDEX;
if ( check_and_promote_exprs_to_type(index, ::base_type(TYPE_INT)) )
return MATCHES_INDEX_SCALAR;
}
return DOES_NOT_MATCH_INDEX;
}
@ -1262,6 +1273,41 @@ bool FileType::DoUnserialize(UnserialInfo* info)
return yield != 0;
}
OpaqueType::OpaqueType(const string& arg_name) : BroType(TYPE_OPAQUE)
{
name = arg_name;
}
void OpaqueType::Describe(ODesc* d) const
{
if ( d->IsReadable() )
d->AddSP("opaque of");
else
d->Add(int(Tag()));
d->Add(name.c_str());
}
IMPLEMENT_SERIAL(OpaqueType, SER_OPAQUE_TYPE);
bool OpaqueType::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_OPAQUE_TYPE, BroType);
return SERIALIZE(name);
}
bool OpaqueType::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroType);
char const* n;
if ( ! UNSERIALIZE_STR(&n, 0) )
return false;
name = n;
return true;
}
EnumType::EnumType(const string& arg_name)
: BroType(TYPE_ENUM)
{
@ -1716,6 +1762,13 @@ int same_type(const BroType* t1, const BroType* t2, int is_init)
case TYPE_FILE:
return same_type(t1->YieldType(), t2->YieldType(), is_init);
case TYPE_OPAQUE:
{
const OpaqueType* ot1 = (const OpaqueType*) t1;
const OpaqueType* ot2 = (const OpaqueType*) t2;
return ot1->Name() == ot2->Name() ? 1 : 0;
}
case TYPE_TYPE:
return same_type(t1, t2, is_init);
@ -1805,6 +1858,7 @@ int is_assignable(BroType* t)
case TYPE_VECTOR:
case TYPE_FILE:
case TYPE_OPAQUE:
case TYPE_TABLE:
case TYPE_TYPE:
return 1;
@ -2190,3 +2244,18 @@ BroType* init_type(Expr* init)
return new SetType(t->AsTypeList(), 0);
}
bool is_atomic_type(const BroType* t)
{
switch ( t->InternalType() ) {
case TYPE_INTERNAL_INT:
case TYPE_INTERNAL_UNSIGNED:
case TYPE_INTERNAL_DOUBLE:
case TYPE_INTERNAL_STRING:
case TYPE_INTERNAL_ADDR:
case TYPE_INTERNAL_SUBNET:
return true;
default:
return false;
}
}

View file

@ -29,6 +29,7 @@ typedef enum {
TYPE_LIST,
TYPE_FUNC,
TYPE_FILE,
TYPE_OPAQUE,
TYPE_VECTOR,
TYPE_TYPE,
TYPE_ERROR
@ -499,6 +500,23 @@ protected:
BroType* yield;
};
class OpaqueType : public BroType {
public:
OpaqueType(const string& name);
virtual ~OpaqueType() { };
const string& Name() const { return name; }
void Describe(ODesc* d) const;
protected:
OpaqueType() { }
DECLARE_SERIAL(OpaqueType)
string name;
};
class EnumType : public BroType {
public:
EnumType(const string& arg_name);
@ -625,6 +643,9 @@ BroType* merge_type_list(ListExpr* elements);
// Given an expression, infer its type when used for an initialization.
extern BroType* init_type(Expr* init);
// Returns true if argument is an atomic type.
bool is_atomic_type(const BroType* t);
// True if the given type tag corresponds to an integral type.
#define IsIntegral(t) (t == TYPE_INT || t == TYPE_COUNT || t == TYPE_COUNTER)

View file

@ -3114,6 +3114,27 @@ void VectorVal::ValDescribe(ODesc* d) const
d->Add("]");
}
OpaqueVal::OpaqueVal(OpaqueType* t) : Val(t)
{
}
OpaqueVal::~OpaqueVal()
{
}
IMPLEMENT_SERIAL(OpaqueVal, SER_OPAQUE_VAL);
bool OpaqueVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_OPAQUE_VAL, Val);
return true;
}
bool OpaqueVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Val);
return true;
}
Val* check_and_promote(Val* v, const BroType* t, int is_init)
{
@ -3209,17 +3230,7 @@ int same_val(const Val* /* v1 */, const Val* /* v2 */)
bool is_atomic_val(const Val* v)
{
switch ( v->Type()->InternalType() ) {
case TYPE_INTERNAL_INT:
case TYPE_INTERNAL_UNSIGNED:
case TYPE_INTERNAL_DOUBLE:
case TYPE_INTERNAL_STRING:
case TYPE_INTERNAL_ADDR:
case TYPE_INTERNAL_SUBNET:
return true;
default:
return false;
}
return is_atomic_type(v->Type());
}
int same_atomic_val(const Val* v1, const Val* v2)

View file

@ -1013,6 +1013,20 @@ protected:
VectorType* vector_type;
};
// Base class for values with types that are managed completely internally,
// with no further script-level operators provided (other than bif
// functions). See OpaqueVal.h for derived classes.
class OpaqueVal : public Val {
public:
OpaqueVal(OpaqueType* t);
virtual ~OpaqueVal();
protected:
friend class Val;
OpaqueVal() { }
DECLARE_SERIAL(OpaqueVal);
};
// Checks the given value for consistency with the given type. If an
// exact match, returns it. If promotable, returns the promoted version,

View file

@ -530,82 +530,7 @@ function piped_exec%(program: string, to_write: string%): bool
%}
%%{
static void hash_md5_val(val_list& vlist, unsigned char digest[16])
{
MD5_CTX h;
md5_init(&h);
loop_over_list(vlist, i)
{
Val* v = vlist[i];
if ( v->Type()->Tag() == TYPE_STRING )
{
const BroString* str = v->AsString();
md5_update(&h, str->Bytes(), str->Len());
}
else
{
ODesc d(DESC_BINARY);
v->Describe(&d);
md5_update(&h, (const u_char *) d.Bytes(), d.Len());
}
}
md5_final(&h, digest);
}
static void hmac_md5_val(val_list& vlist, unsigned char digest[16])
{
hash_md5_val(vlist, digest);
for ( int i = 0; i < 16; ++i )
digest[i] = digest[i] ^ shared_hmac_md5_key[i];
MD5(digest, 16, digest);
}
static void hash_sha1_val(val_list& vlist, unsigned char digest[20])
{
SHA_CTX h;
sha1_init(&h);
loop_over_list(vlist, i)
{
Val* v = vlist[i];
if ( v->Type()->Tag() == TYPE_STRING )
{
const BroString* str = v->AsString();
sha1_update(&h, str->Bytes(), str->Len());
}
else
{
ODesc d(DESC_BINARY);
v->Describe(&d);
sha1_update(&h, (const u_char *) d.Bytes(), d.Len());
}
}
sha1_final(&h, digest);
}
static void hash_sha256_val(val_list& vlist, unsigned char digest[32])
{
SHA256_CTX h;
sha256_init(&h);
loop_over_list(vlist, i)
{
Val* v = vlist[i];
if ( v->Type()->Tag() == TYPE_STRING )
{
const BroString* str = v->AsString();
sha256_update(&h, str->Bytes(), str->Len());
}
else
{
ODesc d(DESC_BINARY);
v->Describe(&d);
sha256_update(&h, (const u_char *) d.Bytes(), d.Len());
}
}
sha256_final(&h, digest);
}
#include "OpaqueVal.h"
%%}
## Computes the MD5 hash value of the provided list of arguments.
@ -623,8 +548,8 @@ static void hash_sha256_val(val_list& vlist, unsigned char digest[32])
## friends.
function md5_hash%(...%): string
%{
unsigned char digest[16];
hash_md5_val(@ARG@, digest);
unsigned char digest[MD5_DIGEST_LENGTH];
MD5Val::digest(@ARG@, digest);
return new StringVal(md5_digest_print(digest));
%}
@ -643,8 +568,8 @@ function md5_hash%(...%): string
## friends.
function sha1_hash%(...%): string
%{
unsigned char digest[20];
hash_sha1_val(@ARG@, digest);
unsigned char digest[SHA_DIGEST_LENGTH];
SHA1Val::digest(@ARG@, digest);
return new StringVal(sha1_digest_print(digest));
%}
@ -663,8 +588,8 @@ function sha1_hash%(...%): string
## friends.
function sha256_hash%(...%): string
%{
unsigned char digest[32];
hash_sha256_val(@ARG@, digest);
unsigned char digest[SHA256_DIGEST_LENGTH];
SHA256Val::digest(@ARG@, digest);
return new StringVal(sha256_digest_print(digest));
%}
@ -679,288 +604,183 @@ function sha256_hash%(...%): string
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function md5_hmac%(...%): string
%{
unsigned char digest[16];
hmac_md5_val(@ARG@, digest);
return new StringVal(md5_digest_print(digest));
unsigned char hmac[MD5_DIGEST_LENGTH];
MD5Val::hmac(@ARG@, shared_hmac_md5_key, hmac);
return new StringVal(md5_digest_print(hmac));
%}
%%{
static map<BroString, MD5_CTX> md5_states;
static map<BroString, SHA_CTX> sha1_states;
static map<BroString, SHA256_CTX> sha256_states;
BroString* convert_index_to_string(Val* index)
{
ODesc d;
index->Describe(&d);
BroString* s = new BroString(1, d.TakeBytes(), d.Len());
s->SetUseFreeToDelete(1);
return s;
}
%%}
## Initializes MD5 state to enable incremental hash computation. After
## initializing the MD5 state with this function, you can feed data to
## :bro:id:`md5_hash_update` and finally need to call :bro:id:`md5_hash_finish`
## to finish the computation and get the final hash value.
## Constructs an MD5 handle to enable incremental hash computation. You can
## feed data to the returned opaque value with :bro:id:`md5_hash_update` and
## eventually need to call :bro:id:`md5_hash_finish` to finish the computation
## and get the hash digest as result.
##
## For example, when computing incremental MD5 values of transferred files in
## multiple concurrent HTTP connections, one would call ``md5_hash_init(c$id)``
## once before invoking ``md5_hash_update(c$id, some_more_data)`` in the
## multiple concurrent HTTP connections, one keeps an optional handle in the
## HTTP session record. Then, one would call
## ``c$http$md5_handle = md5_hash_init()`` once before invoking
## ``md5_hash_update(c$http$md5_handle, some_more_data)`` in the
## :bro:id:`http_entity_data` event handler. When all data has arrived, a call
## to :bro:id:`md5_hash_finish` returns the final hash value.
##
## index: The unique identifier to associate with this hash computation.
##
## Returns: The opaque handle associated with this hash computation.
##
## .. bro:see:: md5_hmac md5_hash md5_hash_update md5_hash_finish
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function md5_hash_init%(index: any%): bool
function md5_hash_init%(%): opaque of md5
%{
BroString* s = convert_index_to_string(index);
int status = 0;
if ( md5_states.count(*s) < 1 )
{
MD5_CTX h;
md5_init(&h);
md5_states[*s] = h;
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL);
HashVal* digest = new MD5Val();
digest->Init();
return digest;
%}
## Initializes SHA1 state to enable incremental hash computation. After
## initializing the SHA1 state with this function, you can feed data to
## :bro:id:`sha1_hash_update` and finally need to call
## :bro:id:`sha1_hash_finish` to finish the computation and get the final hash
## value.
## Constructs an SHA1 handle to enable incremental hash computation. You can
## feed data to the returned opaque value with :bro:id:`sha1_hash_update` and
## finally need to call :bro:id:`sha1_hash_finish` to finish the computation
## and get the hash digest as result.
##
## For example, when computing incremental SHA1 values of transferred files in
## multiple concurrent HTTP connections, one would call ``sha1_hash_init(c$id)``
## once before invoking ``sha1_hash_update(c$id, some_more_data)`` in the
## multiple concurrent HTTP connections, one keeps an optional handle in the
## HTTP session record. Then, one would call
## ``c$http$sha1_handle = sha1_hash_init()`` ## once before invoking
## ``sha1_hash_update(c$http$sha1_handle, some_more_data)`` in the
## :bro:id:`http_entity_data` event handler. When all data has arrived, a call
## to :bro:id:`sha1_hash_finish` returns the final hash value.
##
## index: The unique identifier to associate with this hash computation.
## Returns: The opaque handle associated with this hash computation.
##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
## sha1_hash sha1_hash_update sha1_hash_finish
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function sha1_hash_init%(index: any%): bool
function sha1_hash_init%(%): opaque of sha1
%{
BroString* s = convert_index_to_string(index);
int status = 0;
if ( sha1_states.count(*s) < 1 )
{
SHA_CTX h;
sha1_init(&h);
sha1_states[*s] = h;
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL);
HashVal* digest = new SHA1Val();
digest->Init();
return digest;
%}
## Initializes SHA256 state to enable incremental hash computation. After
## initializing the SHA256 state with this function, you can feed data to
## :bro:id:`sha256_hash_update` and finally need to call
## :bro:id:`sha256_hash_finish` to finish the computation and get the final hash
## value.
## Constructs an SHA256 handle to enable incremental hash computation. You can
## feed data to the returned opaque value with :bro:id:`sha256_hash_update` and
## finally need to call :bro:id:`sha256_hash_finish` to finish the computation
## and get the hash digest as result.
##
## For example, when computing incremental SHA256 values of transferred files in
## multiple concurrent HTTP connections, one would call
## ``sha256_hash_init(c$id)`` once before invoking
## ``sha256_hash_update(c$id, some_more_data)`` in the
## multiple concurrent HTTP connections, one keeps an optional handle in the
## HTTP session record. Then, one would call
## ``c$http$sha256_handle = sha256_hash_init()`` ## once before invoking
## ``sha256_hash_update(c$http$sha256_handle, some_more_data)`` in the
## :bro:id:`http_entity_data` event handler. When all data has arrived, a call
## to :bro:id:`sha256_hash_finish` returns the final hash value.
##
## index: The unique identifier to associate with this hash computation.
## Returns: The opaque handle associated with this hash computation.
##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
## sha256_hash sha256_hash_update sha256_hash_finish
function sha256_hash_init%(index: any%): bool
function sha256_hash_init%(%): opaque of sha256
%{
BroString* s = convert_index_to_string(index);
int status = 0;
if ( sha256_states.count(*s) < 1 )
{
SHA256_CTX h;
sha256_init(&h);
sha256_states[*s] = h;
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL);
HashVal* digest = new SHA256Val();
digest->Init();
return digest;
%}
## Update the MD5 value associated with a given index. It is required to
## Updates the MD5 value associated with a given index. It is required to
## call :bro:id:`md5_hash_init` once before calling this
## function.
##
## index: The unique identifier to associate with this hash computation.
## handle: The opaque handle associated with this hash computation.
##
## data: The data to add to the hash computation.
##
## Returns: True on success.
##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_finish
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function md5_hash_update%(index: any, data: string%): bool
function md5_hash_update%(handle: opaque of md5, data: string%): bool
%{
BroString* s = convert_index_to_string(index);
int status = 0;
if ( md5_states.count(*s) > 0 )
{
md5_update(&md5_states[*s], data->Bytes(), data->Len());
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL);
bool rc = static_cast<HashVal*>(handle)->Feed(data->Bytes(), data->Len());
return new Val(rc, TYPE_BOOL);
%}
## Update the SHA1 value associated with a given index. It is required to
## Updates the SHA1 value associated with a given index. It is required to
## call :bro:id:`sha1_hash_init` once before calling this
## function.
##
## index: The unique identifier to associate with this hash computation.
## handle: The opaque handle associated with this hash computation.
##
## data: The data to add to the hash computation.
##
## Returns: True on success.
##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
## sha1_hash sha1_hash_init sha1_hash_finish
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function sha1_hash_update%(index: any, data: string%): bool
function sha1_hash_update%(handle: opaque of sha1, data: string%): bool
%{
BroString* s = convert_index_to_string(index);
int status = 0;
if ( sha1_states.count(*s) > 0 )
{
sha1_update(&sha1_states[*s], data->Bytes(), data->Len());
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL);
bool rc = static_cast<HashVal*>(handle)->Feed(data->Bytes(), data->Len());
return new Val(rc, TYPE_BOOL);
%}
## Update the SHA256 value associated with a given index. It is required to
## Updates the SHA256 value associated with a given index. It is required to
## call :bro:id:`sha256_hash_init` once before calling this
## function.
##
## index: The unique identifier to associate with this hash computation.
## handle: The opaque handle associated with this hash computation.
##
## data: The data to add to the hash computation.
##
## Returns: True on success.
##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
## sha256_hash sha256_hash_init sha256_hash_finish
function sha256_hash_update%(index: any, data: string%): bool
function sha256_hash_update%(handle: opaque of sha256, data: string%): bool
%{
BroString* s = convert_index_to_string(index);
int status = 0;
if ( sha256_states.count(*s) > 0 )
{
sha256_update(&sha256_states[*s], data->Bytes(), data->Len());
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL);
bool rc = static_cast<HashVal*>(handle)->Feed(data->Bytes(), data->Len());
return new Val(rc, TYPE_BOOL);
%}
## Returns the final MD5 digest of an incremental hash computation.
##
## index: The unique identifier of this hash computation.
## handle: The opaque handle associated with this hash computation.
##
## Returns: The hash value associated with the computation at *index*.
## Returns: The hash value associated with the computation of *handle*.
##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function md5_hash_finish%(index: any%): string
function md5_hash_finish%(handle: opaque of md5%): string
%{
BroString* s = convert_index_to_string(index);
StringVal* printable_digest;
if ( md5_states.count(*s) > 0 )
{
unsigned char digest[16];
md5_final(&md5_states[*s], digest);
md5_states.erase(*s);
printable_digest = new StringVal(md5_digest_print(digest));
}
else
printable_digest = new StringVal("");
delete s;
return printable_digest;
return static_cast<HashVal*>(handle)->Get();
%}
## Returns the final SHA1 digest of an incremental hash computation.
##
## index: The unique identifier of this hash computation.
## handle: The opaque handle associated with this hash computation.
##
## Returns: The hash value associated with the computation at *index*.
## Returns: The hash value associated with the computation of *handle*.
##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
## sha1_hash sha1_hash_init sha1_hash_update
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function sha1_hash_finish%(index: any%): string
function sha1_hash_finish%(handle: opaque of sha1%): string
%{
BroString* s = convert_index_to_string(index);
StringVal* printable_digest;
if ( sha1_states.count(*s) > 0 )
{
unsigned char digest[20];
sha1_final(&sha1_states[*s], digest);
sha1_states.erase(*s);
printable_digest = new StringVal(sha1_digest_print(digest));
}
else
printable_digest = new StringVal("");
delete s;
return printable_digest;
return static_cast<HashVal*>(handle)->Get();
%}
## Returns the final SHA256 digest of an incremental hash computation.
##
## index: The unique identifier of this hash computation.
## handle: The opaque handle associated with this hash computation.
##
## Returns: The hash value associated with the computation at *index*.
## Returns: The hash value associated with the computation of *handle*.
##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
## sha256_hash sha256_hash_init sha256_hash_update
function sha256_hash_finish%(index: any%): string
function sha256_hash_finish%(handle: opaque of sha256%): string
%{
BroString* s = convert_index_to_string(index);
StringVal* printable_digest;
if ( sha256_states.count(*s) > 0 )
{
unsigned char digest[32];
sha256_final(&sha256_states[*s], digest);
sha256_states.erase(*s);
printable_digest = new StringVal(sha256_digest_print(digest));
}
else
printable_digest = new StringVal("");
delete s;
return printable_digest;
return static_cast<HashVal*>(handle)->Get();
%}
## Generates a random number.
@ -1058,11 +878,6 @@ function identify_data%(data: string, return_mime: bool%): string
return new StringVal(descr);
%}
%%{
#include <RandTest.h>
static map<BroString, RandTest*> entropy_states;
%%}
## Performs an entropy test on the given data.
## See http://www.fourmilab.ch/random.
##
@ -1107,13 +922,11 @@ function find_entropy%(data: string%): entropy_test_result
%{
double montepi, scc, ent, mean, chisq;
montepi = scc = ent = mean = chisq = 0.0;
EntropyVal e;
e.Feed(data->Bytes(), data->Len());
e.Get(&ent, &chisq, &mean, &montepi, &scc);
RecordVal* ent_result = new RecordVal(entropy_test_result);
RandTest *rt = new RandTest();
rt->add((char*) data->Bytes(), data->Len());
rt->end(&ent, &chisq, &mean, &montepi, &scc);
delete rt;
ent_result->Assign(0, new Val(ent, TYPE_DOUBLE));
ent_result->Assign(1, new Val(chisq, TYPE_DOUBLE));
ent_result->Assign(2, new Val(mean, TYPE_DOUBLE));
@ -1124,85 +937,52 @@ function find_entropy%(data: string%): entropy_test_result
## Initializes data structures for incremental entropy calculation.
##
## index: An arbitrary unique value per distinct computation.
##
## Returns: True on success.
## Returns: An opaque handle to be used in subsequent operations.
##
## .. bro:see:: find_entropy entropy_test_add entropy_test_finish
function entropy_test_init%(index: any%): bool
function entropy_test_init%(%): opaque of entropy
%{
BroString* s = convert_index_to_string(index);
int status = 0;
if ( entropy_states.count(*s) < 1 )
{
entropy_states[*s] = new RandTest();
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL);
return new EntropyVal();
%}
## Adds data to an incremental entropy calculation. Before using this function,
## one needs to invoke :bro:id:`entropy_test_init`.
## Adds data to an incremental entropy calculation.
##
## handle: The opaque handle representing the entropy calculation state.
##
## data: The data to add to the entropy calculation.
##
## index: An arbitrary unique value that identifies a particular entropy
## computation.
##
## Returns: True on success.
##
## .. bro:see:: find_entropy entropy_test_add entropy_test_finish
function entropy_test_add%(index: any, data: string%): bool
function entropy_test_add%(handle: opaque of entropy, data: string%): bool
%{
BroString* s = convert_index_to_string(index);
int status = 0;
if ( entropy_states.count(*s) > 0 )
{
entropy_states[*s]->add((char*) data->Bytes(), data->Len());
status = 1;
}
delete s;
bool status = static_cast<EntropyVal*>(handle)->Feed(data->Bytes(),
data->Len());
return new Val(status, TYPE_BOOL);
%}
## Finishes an incremental entropy calculation. Before using this function,
## one needs to initialize the computation with :bro:id:`entropy_test_init` and
## one needs to obtain an opaque handle with :bro:id:`entropy_test_init` and
## add data to it via :bro:id:`entropy_test_add`.
##
## index: An arbitrary unique value that identifies a particular entropy
## computation.
## handle: The opaque handle representing the entropy calculation state.
##
## Returns: The result of the entropy test. See :bro:id:`find_entropy` for a
## description of the individual components.
##
## .. bro:see:: find_entropy entropy_test_init entropy_test_add
function entropy_test_finish%(index: any%): entropy_test_result
function entropy_test_finish%(handle: opaque of entropy%): entropy_test_result
%{
BroString* s = convert_index_to_string(index);
double montepi, scc, ent, mean, chisq;
montepi = scc = ent = mean = chisq = 0.0;
static_cast<EntropyVal*>(handle)->Get(&ent, &chisq, &mean, &montepi, &scc);
RecordVal* ent_result = new RecordVal(entropy_test_result);
if ( entropy_states.count(*s) > 0 )
{
RandTest *rt = entropy_states[*s];
rt->end(&ent, &chisq, &mean, &montepi, &scc);
entropy_states.erase(*s);
delete rt;
}
ent_result->Assign(0, new Val(ent, TYPE_DOUBLE));
ent_result->Assign(1, new Val(chisq, TYPE_DOUBLE));
ent_result->Assign(2, new Val(mean, TYPE_DOUBLE));
ent_result->Assign(3, new Val(montepi, TYPE_DOUBLE));
ent_result->Assign(4, new Val(scc, TYPE_DOUBLE));
delete s;
return ent_result;
%}
@ -2684,8 +2464,9 @@ function to_port%(s: string%): port
if ( s->Len() < 10 )
{
char* slash;
errno = 0;
port = strtol(s->CheckString(), &slash, 10);
if ( port )
if ( ! errno )
{
++slash;
if ( streq(slash, "tcp") )

View file

@ -74,7 +74,9 @@ HEX [0-9a-fA-F]+
"set" return check_c_mode(TOK_SET);
"table" return check_c_mode(TOK_TABLE);
"vector" return check_c_mode(TOK_VECTOR);
"module" return check_c_mode(TOK_MODULE);
"of" return check_c_mode(TOK_OF);
"opaque" return check_c_mode(TOK_OPAQUE);
"module" return check_c_mode(TOK_MODULE);
"@ARG@" return TOK_ARG;
"@ARGS@" return TOK_ARGS;

View file

@ -269,15 +269,15 @@ void print_event_c_body(FILE *fp)
%token TOK_LPP TOK_RPP TOK_LPB TOK_RPB TOK_LPPB TOK_RPPB TOK_VAR_ARG
%token TOK_BOOL
%token TOK_FUNCTION TOK_EVENT TOK_CONST TOK_ENUM
%token TOK_TYPE TOK_RECORD TOK_SET TOK_VECTOR TOK_TABLE TOK_MODULE
%token TOK_FUNCTION TOK_EVENT TOK_CONST TOK_ENUM TOK_OF
%token TOK_TYPE TOK_RECORD TOK_SET TOK_VECTOR TOK_OPAQUE TOK_TABLE TOK_MODULE
%token TOK_ARGS TOK_ARG TOK_ARGC
%token TOK_ID TOK_ATTR TOK_CSTR TOK_LF TOK_WS TOK_COMMENT
%token TOK_ATOM TOK_INT TOK_C_TOKEN
%left ',' ':'
%type <str> TOK_C_TOKEN TOK_ID TOK_CSTR TOK_WS TOK_COMMENT TOK_ATTR TOK_INT opt_ws
%type <str> TOK_C_TOKEN TOK_ID TOK_CSTR TOK_WS TOK_COMMENT TOK_ATTR TOK_INT opt_ws type
%type <val> TOK_ATOM TOK_BOOL
%union {
@ -584,7 +584,17 @@ args_1: args_1 ',' opt_ws arg opt_ws
{ /* empty */ }
;
arg: TOK_ID opt_ws ':' opt_ws TOK_ID
// TODO: Migrate all other compound types to this rule. Once the BiF language
// can parse all regular Bro types, we can throw out the unnecessary
// boilerplate typedefs for addr_set, string_set, etc.
type:
TOK_OPAQUE opt_ws TOK_OF opt_ws TOK_ID
{ $$ = concat("opaque of ", $5); }
| TOK_ID
{ $$ = $1; }
;
arg: TOK_ID opt_ws ':' opt_ws type
{ args.push_back(new BuiltinFuncArg($1, $5)); }
| TOK_VAR_ARG
{
@ -594,7 +604,7 @@ arg: TOK_ID opt_ws ':' opt_ws TOK_ID
}
;
return_type: ':' opt_ws TOK_ID opt_ws
return_type: ':' opt_ws type opt_ws
{
BuiltinFuncArg* ret = new BuiltinFuncArg("", $3);
ret->PrintBro(fp_bro_init);

View file

@ -11,7 +11,7 @@
%token TOK_DOUBLE TOK_ELSE TOK_ENUM TOK_EVENT TOK_EXPORT TOK_FILE TOK_FOR
%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_NEXT TOK_OF TOK_OPAQUE TOK_PATTERN TOK_PATTERN_TEXT
%token TOK_PORT TOK_PRINT TOK_RECORD TOK_REDEF
%token TOK_REMOVE_FROM TOK_RETURN TOK_SCHEDULE TOK_SET
%token TOK_STRING TOK_SUBNET TOK_SWITCH TOK_TABLE
@ -899,6 +899,12 @@ type:
$$ = new FileType(base_type(TYPE_STRING));
}
| TOK_OPAQUE TOK_OF TOK_ID
{
set_location(@1, @3);
$$ = new OpaqueType($3);
}
| resolve_id
{
if ( ! $1 || ! ($$ = $1->AsType()) )

View file

@ -298,6 +298,7 @@ local return TOK_LOCAL;
module return TOK_MODULE;
next return TOK_NEXT;
of return TOK_OF;
opaque return TOK_OPAQUE;
pattern return TOK_PATTERN;
port return TOK_PORT;
print return TOK_PRINT;

View file

@ -1,6 +1,9 @@
123/tcp
123/udp
123/icmp
0/tcp
0/udp
0/icmp
0/unknown
256/tcp
256/udp

View file

@ -0,0 +1,3 @@
[orig_h=1.2.0.2, orig_p=2527/tcp, resp_h=1.2.0.3, resp_p=6649/tcp]
orig:, [size=175, state=1, num_pkts=4, num_bytes_ip=395, flow_label=0]
resp:, [size=0, state=6, num_pkts=5, num_bytes_ip=236, flow_label=0]

View file

@ -0,0 +1,4 @@
acbd18db4cc2f85cedef654fccc4a4d8
0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
[entropy=0.918296, chi_square=423.666667, mean=108.0, monte_carlo_pi=nan, serial_correlation=-0.5]

View file

@ -0,0 +1,4 @@
acbd18db4cc2f85cedef654fccc4a4d8
0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
[entropy=0.918296, chi_square=423.666667, mean=108.0, monte_carlo_pi=nan, serial_correlation=-0.5]

View file

@ -3,6 +3,7 @@ myhook return F
myhook return T
myhook, &priority=5, [a=37, b=goobye world]
F
T
myhook3, 8
T
myhook4, 2

View file

@ -0,0 +1,13 @@
1
12
123456
0123456789
8
789
9
9
9
2
1

View file

@ -0,0 +1 @@
done

View file

@ -0,0 +1 @@
error in /home/jsiwek/bro/testing/btest/.tmp/language.type-type-error/type-type-error.bro, line 13: not a record (r$a)

Binary file not shown.

View file

@ -5,20 +5,14 @@
event bro_init()
{
local a = "dh3Hie02uh^s#Sdf9L3frd243h$d78r2G4cM6*Q05d(7rh46f!0|4-f";
if ( entropy_test_init(1) != T )
local handle = entropy_test_init();
if ( ! entropy_test_add(handle, a) )
exit(1);
if ( entropy_test_add(1, a) != T )
exit(1);
print entropy_test_finish(1);
print entropy_test_finish(handle);
local b = "0011000aaabbbbcccc000011111000000000aaaabbbbcccc0000000";
if ( entropy_test_init(2) != T )
handle = entropy_test_init();
if ( ! entropy_test_add(handle, b) )
exit(1);
if ( entropy_test_add(2, b) != T )
exit(1);
print entropy_test_finish(2);
print entropy_test_finish(handle);
}

View file

@ -4,16 +4,16 @@
print md5_hash("one");
print md5_hash("one", "two", "three");
md5_hash_init("a");
md5_hash_init("b");
local a = md5_hash_init();
local b = md5_hash_init();
md5_hash_update("a", "one");
md5_hash_update("b", "one");
md5_hash_update("b", "two");
md5_hash_update("b", "three");
md5_hash_update(a, "one");
md5_hash_update(b, "one");
md5_hash_update(b, "two");
md5_hash_update(b, "three");
print md5_hash_finish("a");
print md5_hash_finish("b");
print md5_hash_finish(a);
print md5_hash_finish(b);
print md5_hmac("one");
print md5_hmac("one", "two", "three");

View file

@ -4,13 +4,13 @@
print sha1_hash("one");
print sha1_hash("one", "two", "three");
sha1_hash_init("a");
sha1_hash_init("b");
local a = sha1_hash_init();
local b = sha1_hash_init();
sha1_hash_update("a", "one");
sha1_hash_update("b", "one");
sha1_hash_update("b", "two");
sha1_hash_update("b", "three");
sha1_hash_update(a, "one");
sha1_hash_update(b, "one");
sha1_hash_update(b, "two");
sha1_hash_update(b, "three");
print sha1_hash_finish("a");
print sha1_hash_finish("b");
print sha1_hash_finish(a);
print sha1_hash_finish(b);

View file

@ -4,13 +4,13 @@
print sha256_hash("one");
print sha256_hash("one", "two", "three");
sha256_hash_init("a");
sha256_hash_init("b");
local a = sha256_hash_init();
local b = sha256_hash_init();
sha256_hash_update("a", "one");
sha256_hash_update("b", "one");
sha256_hash_update("b", "two");
sha256_hash_update("b", "three");
sha256_hash_update(a, "one");
sha256_hash_update(b, "one");
sha256_hash_update(b, "two");
sha256_hash_update(b, "three");
print sha256_hash_finish("a");
print sha256_hash_finish("b");
print sha256_hash_finish(a);
print sha256_hash_finish(b);

View file

@ -7,6 +7,9 @@ event bro_init()
print to_port("123/tcp");
print to_port("123/udp");
print to_port("123/icmp");
print to_port("0/tcp");
print to_port("0/udp");
print to_port("0/icmp");
print to_port("not a port");
local a: transport_proto = tcp;

View file

@ -0,0 +1,26 @@
# Needs perftools support.
#
# @TEST-GROUP: leaks
#
# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks
#
# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local bro -m -b -r $TRACES/wikipedia.trace %INPUT
event new_connection(c: connection)
{
local s = "0123456789";
print s[1];
print s[1,2];
print s[1,6];
print s[0,20];
print s[-2];
print s[-3,-1];
print s[-1,-10];
print s[-1,0];
print s[-1,5];
print s[20, 23];
print s[-20, 23];
print s[0,5][2];
print s[0,5][1,3][0];
}

View file

@ -0,0 +1,289 @@
# Needs perftools support.
#
# @TEST-GROUP: leaks
#
# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks
#
# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local bro -m -b -r $TRACES/wikipedia.trace %INPUT
type MyEnum: enum {
RED,
GREEN,
BLUE,
PINK,
};
function switch_bool(v: bool): string
{
switch (v) {
case T:
return "true";
case F:
return "false";
}
return "n/a";
}
function switch_int(v: int): string
{
switch (v) {
case +1:
return "one";
case +2:
return "two";
case -3:
return "minus three";
}
return "n/a";
}
function switch_enum(v: MyEnum): string
{
switch (v) {
case RED:
return "red";
case GREEN:
return "green";
case BLUE:
return "blue";
}
return "n/a";
}
function switch_count(v: count): string
{
switch (v) {
case 1:
return "1";
case 2:
return "2";
case 3:
return "3";
}
return "n/a";
}
function switch_port(v: port): string
{
switch (v) {
case 22/tcp:
return "ssh";
case 53/udp:
return "dns";
case 0/icmp:
return "echo";
}
return "n/a";
}
function switch_double(v: double): string
{
switch (v) {
case 1.1:
return "1.1";
case 2.2:
return "2.2";
case 3.3:
return "3.3";
}
return "n/a";
}
function switch_interval(v: interval): string
{
switch (v) {
case 1sec:
return "1sec";
case 2day:
return "2day";
case 3min:
return "3min";
}
return "n/a";
}
function switch_string(v: string): string
{
switch (v) {
case "one":
return "first";
case "two":
return "second";
case "three":
return "third";
}
return "n/a";
}
function switch_addr(v: addr): string
{
switch (v) {
case 1.2.3.4:
return "ipv4";
case [fe80::1]:
return "ipv6";
case 0.0.0.0:
return "unspec";
}
return "n/a";
}
function switch_subnet(v: subnet): string
{
switch (v) {
case 1.2.3.0/24:
return "1.2.3.0/24";
case [fe80::0]/96:
return "[fe80::0]";
case 192.168.0.0/16:
return "192.168.0.0/16";
}
return "n/a";
}
function switch_empty(v: count): string
{
switch ( v ) {
}
return "n/a";
}
function switch_break(v: count): string
{
local rval = "";
switch ( v ) {
case 1:
rval += "test";
case 2:
rval += "testing";
break;
case 3:
rval += "tested";
}
return rval + "return";
}
function switch_default(v: count): string
{
local rval = "";
switch ( v ) {
case 1:
rval += "1";
case 2:
rval += "2";
case 3:
rval += "3";
default:
rval += "d";
}
return rval + "r";
}
function switch_default_placement(v: count): string
{
local rval = "";
switch ( v ) {
case 1:
rval += "1";
default:
rval += "d";
case 2:
rval += "2";
break;
case 3:
rval += "3";
}
return rval + "r";
}
function switch_case_list(v: count): string
{
switch ( v ) {
case 1, 2:
return "1,2";
case 3, 4, 5:
return "3,4,5";
case 6, 7, 8, 9:
return "6,7,8,9";
}
return "n/a";
}
function test_switch(actual: string, expect: string)
{
if ( actual != expect )
print fmt("%s != %s", actual, expect);
}
event new_connection(c: connection)
{
test_switch( switch_bool(T) , "true" );
test_switch( switch_bool(F) , "false" );
test_switch( switch_int(+1) , "one" );
test_switch( switch_int(+2) , "two" );
test_switch( switch_int(-3) , "minus three" );
test_switch( switch_int(40) , "n/a" );
test_switch( switch_enum(RED) , "red" );
test_switch( switch_enum(BLUE) , "blue" );
test_switch( switch_enum(GREEN) , "green" );
test_switch( switch_enum(PINK) , "n/a" );
test_switch( switch_count(1) , "1" );
test_switch( switch_count(2) , "2" );
test_switch( switch_count(3) , "3" );
test_switch( switch_count(100) , "n/a" );
test_switch( switch_port(22/tcp) , "ssh" );
test_switch( switch_port(53/udp) , "dns" );
test_switch( switch_port(0/icmp) , "echo" );
test_switch( switch_port(1000/tcp) , "n/a" );
test_switch( switch_double(1.1) , "1.1" );
test_switch( switch_double(2.2) , "2.2" );
test_switch( switch_double(3.3) , "3.3" );
test_switch( switch_interval(1sec) , "1sec" );
test_switch( switch_interval(2day) , "2day" );
test_switch( switch_interval(3min) , "3min" );
test_switch( switch_string("one") , "first" );
test_switch( switch_string("two") , "second" );
test_switch( switch_string("three") , "third" );
test_switch( switch_addr(1.2.3.4) , "ipv4" );
test_switch( switch_addr([fe80::1]) , "ipv6" );
test_switch( switch_addr(0.0.0.0) , "unspec" );
test_switch( switch_subnet(1.2.3.4/24) , "1.2.3.0/24" );
test_switch( switch_subnet([fe80::1]/96) , "[fe80::0]" );
test_switch( switch_subnet(192.168.1.100/16) , "192.168.0.0/16" );
test_switch( switch_empty(2) , "n/a" );
test_switch( switch_break(1) , "testtestingreturn" );
test_switch( switch_break(2) , "testingreturn" );
test_switch( switch_break(3) , "testedreturn" );
test_switch( switch_default(1) , "123dr" );
test_switch( switch_default(2) , "23dr" );
test_switch( switch_default(3) , "3dr" );
test_switch( switch_default(4) , "dr" );
test_switch( switch_default_placement(1) , "1d2r" );
test_switch( switch_default_placement(2) , "2r" );
test_switch( switch_default_placement(3) , "3r" );
test_switch( switch_default_placement(4) , "d2r" );
local v = vector(0,1,2,3,4,5,6,7,9,10);
local expect: string;
for ( i in v )
{
switch ( v[i] ) {
case 1, 2:
expect = "1,2";
break;
case 3, 4, 5:
expect = "3,4,5";
break;
case 6, 7, 8, 9:
expect = "6,7,8,9";
break;
default:
expect = "n/a";
break;
}
test_switch( switch_case_list(v[i]) , expect );
}
print "done";
}

View file

@ -0,0 +1,12 @@
# @TEST-EXEC: bro -b -r $TRACES/tcp/rst-inject-rae.trace %INPUT
# @TEST-EXEC: btest-diff .stdout
# Mostly just checking that c$resp$size isn't huge due to the injected
# RST packet being used to initialize sequence number in TCP analyzer.
event connection_state_remove(c: connection)
{
print c$id;
print "orig:", c$orig;
print "resp:", c$resp;
}

View file

@ -0,0 +1,77 @@
#
# @TEST-EXEC: bro -r $TRACES/empty.trace write.bro
# @TEST-EXEC: bro read.bro
# @TEST-EXEC: btest-diff expected.log
# @TEST-EXEC: btest-diff output.log
# @TEST-EXEC: cmp output.log expected.log
@TEST-START-FILE read.bro
global md5_handle: opaque of md5 &persistent &synchronized;
global sha1_handle: opaque of sha1 &persistent &synchronized;
global sha256_handle: opaque of sha256 &persistent &synchronized;
global entropy_handle: opaque of entropy &persistent &synchronized;
event bro_done()
{
local out = open("output.log");
# Finish incremental operations started by a previous Bro.
if ( md5_hash_update(md5_handle, "oo") )
print out, md5_hash_finish(md5_handle);
else
print out, "md5_hash_update() failed";
if ( sha1_hash_update(sha1_handle, "oo") )
print out, sha1_hash_finish(sha1_handle);
else
print out, "sha1_hash_update() failed";
if ( sha256_hash_update(sha256_handle, "oo") )
print out, sha256_hash_finish(sha256_handle);
else
print out, "sha256_hash_update() failed";
if ( entropy_test_add(entropy_handle, "oo") )
print out, entropy_test_finish(entropy_handle);
else
print out, "entropy_test_add() failed";
}
@TEST-END-FILE
@TEST-START-FILE write.bro
global md5_handle: opaque of md5 &persistent &synchronized;
global sha1_handle: opaque of sha1 &persistent &synchronized;
global sha256_handle: opaque of sha256 &persistent &synchronized;
global entropy_handle: opaque of entropy &persistent &synchronized;
event bro_init()
{
local out = open("expected.log");
print out, md5_hash("foo");
print out, sha1_hash("foo");
print out, sha256_hash("foo");
print out, find_entropy("foo");
# Begin incremental operations. Our goal is to feed the data string "foo" to
# the computation, but split into "f" and "oo" in two instances..
md5_handle = md5_hash_init();
if ( ! md5_hash_update(md5_handle, "f") )
print out, "md5_hash_update() failed";
sha1_handle = sha1_hash_init();
if ( ! sha1_hash_update(sha1_handle, "f") )
print out, "sha1_hash_update() failed";
sha256_handle = sha256_hash_init();
if ( ! sha256_hash_update(sha256_handle, "f") )
print out, "sha256_hash_update() failed";
entropy_handle = entropy_test_init();
if ( ! entropy_test_add(entropy_handle, "f") )
print out, "entropy_test_add() failed";
}
@TEST-END-FILE

View file

@ -0,0 +1,17 @@
# @TEST-EXEC: bro -b %INPUT >out
# @TEST-EXEC: btest-diff out
local s = "0123456789";
print s[1];
print s[1,2];
print s[1,6];
print s[0,20];
print s[-2];
print s[-3,-1];
print s[-1,-10];
print s[-1,0];
print s[-1,5];
print s[20, 23];
print s[-20, 23];
print s[0,5][2];
print s[0,5][1,3][0];

View file

@ -0,0 +1,284 @@
# @TEST-EXEC: bro -b %INPUT >out
# @TEST-EXEC: btest-diff out
type MyEnum: enum {
RED,
GREEN,
BLUE,
PINK,
};
function switch_bool(v: bool): string
{
switch (v) {
case T:
return "true";
case F:
return "false";
}
return "n/a";
}
function switch_int(v: int): string
{
switch (v) {
case +1:
return "one";
case +2:
return "two";
case -3:
return "minus three";
}
return "n/a";
}
function switch_enum(v: MyEnum): string
{
switch (v) {
case RED:
return "red";
case GREEN:
return "green";
case BLUE:
return "blue";
}
return "n/a";
}
function switch_count(v: count): string
{
switch (v) {
case 1:
return "1";
case 2:
return "2";
case 3:
return "3";
}
return "n/a";
}
function switch_port(v: port): string
{
switch (v) {
case 22/tcp:
return "ssh";
case 53/udp:
return "dns";
case 0/icmp:
return "echo";
}
return "n/a";
}
function switch_double(v: double): string
{
switch (v) {
case 1.1:
return "1.1";
case 2.2:
return "2.2";
case 3.3:
return "3.3";
}
return "n/a";
}
function switch_interval(v: interval): string
{
switch (v) {
case 1sec:
return "1sec";
case 2day:
return "2day";
case 3min:
return "3min";
}
return "n/a";
}
function switch_string(v: string): string
{
switch (v) {
case "one":
return "first";
case "two":
return "second";
case "three":
return "third";
}
return "n/a";
}
function switch_addr(v: addr): string
{
switch (v) {
case 1.2.3.4:
return "ipv4";
case [fe80::1]:
return "ipv6";
case 0.0.0.0:
return "unspec";
}
return "n/a";
}
function switch_subnet(v: subnet): string
{
switch (v) {
case 1.2.3.0/24:
return "1.2.3.0/24";
case [fe80::0]/96:
return "[fe80::0]";
case 192.168.0.0/16:
return "192.168.0.0/16";
}
return "n/a";
}
function switch_empty(v: count): string
{
switch ( v ) {
}
return "n/a";
}
function switch_break(v: count): string
{
local rval = "";
switch ( v ) {
case 1:
rval += "test";
case 2:
rval += "testing";
break;
case 3:
rval += "tested";
}
return rval + "return";
}
function switch_default(v: count): string
{
local rval = "";
switch ( v ) {
case 1:
rval += "1";
case 2:
rval += "2";
case 3:
rval += "3";
default:
rval += "d";
}
return rval + "r";
}
function switch_default_placement(v: count): string
{
local rval = "";
switch ( v ) {
case 1:
rval += "1";
default:
rval += "d";
case 2:
rval += "2";
break;
case 3:
rval += "3";
}
return rval + "r";
}
function switch_case_list(v: count): string
{
switch ( v ) {
case 1, 2:
return "1,2";
case 3, 4, 5:
return "3,4,5";
case 6, 7, 8, 9:
return "6,7,8,9";
}
return "n/a";
}
function test_switch(actual: string, expect: string)
{
if ( actual != expect )
print fmt("%s != %s", actual, expect);
}
event bro_init()
{
test_switch( switch_bool(T) , "true" );
test_switch( switch_bool(F) , "false" );
test_switch( switch_int(+1) , "one" );
test_switch( switch_int(+2) , "two" );
test_switch( switch_int(-3) , "minus three" );
test_switch( switch_int(40) , "n/a" );
test_switch( switch_enum(RED) , "red" );
test_switch( switch_enum(BLUE) , "blue" );
test_switch( switch_enum(GREEN) , "green" );
test_switch( switch_enum(PINK) , "n/a" );
test_switch( switch_count(1) , "1" );
test_switch( switch_count(2) , "2" );
test_switch( switch_count(3) , "3" );
test_switch( switch_count(100) , "n/a" );
test_switch( switch_port(22/tcp) , "ssh" );
test_switch( switch_port(53/udp) , "dns" );
test_switch( switch_port(0/icmp) , "echo" );
test_switch( switch_port(1000/tcp) , "n/a" );
test_switch( switch_double(1.1) , "1.1" );
test_switch( switch_double(2.2) , "2.2" );
test_switch( switch_double(3.3) , "3.3" );
test_switch( switch_interval(1sec) , "1sec" );
test_switch( switch_interval(2day) , "2day" );
test_switch( switch_interval(3min) , "3min" );
test_switch( switch_string("one") , "first" );
test_switch( switch_string("two") , "second" );
test_switch( switch_string("three") , "third" );
test_switch( switch_addr(1.2.3.4) , "ipv4" );
test_switch( switch_addr([fe80::1]) , "ipv6" );
test_switch( switch_addr(0.0.0.0) , "unspec" );
test_switch( switch_subnet(1.2.3.4/24) , "1.2.3.0/24" );
test_switch( switch_subnet([fe80::1]/96) , "[fe80::0]" );
test_switch( switch_subnet(192.168.1.100/16) , "192.168.0.0/16" );
test_switch( switch_empty(2) , "n/a" );
test_switch( switch_break(1) , "testtestingreturn" );
test_switch( switch_break(2) , "testingreturn" );
test_switch( switch_break(3) , "testedreturn" );
test_switch( switch_default(1) , "123dr" );
test_switch( switch_default(2) , "23dr" );
test_switch( switch_default(3) , "3dr" );
test_switch( switch_default(4) , "dr" );
test_switch( switch_default_placement(1) , "1d2r" );
test_switch( switch_default_placement(2) , "2r" );
test_switch( switch_default_placement(3) , "3r" );
test_switch( switch_default_placement(4) , "d2r" );
local v = vector(0,1,2,3,4,5,6,7,9,10);
local expect: string;
for ( i in v )
{
switch ( v[i] ) {
case 1, 2:
expect = "1,2";
break;
case 3, 4, 5:
expect = "3,4,5";
break;
case 6, 7, 8, 9:
expect = "6,7,8,9";
break;
default:
expect = "n/a";
break;
}
test_switch( switch_case_list(v[i]) , expect );
}
print "done";
}

View file

@ -0,0 +1,14 @@
# @TEST-EXEC-FAIL: bro -b %INPUT
# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff .stderr
type r: record {
a: string;
};
event bro_init()
{
# This should generate a parse error indicating that the type identifier
# is incorrectly used in an expression expecting a real value and not
# a value of type TypeType.
print r$a;
}