mirror of
https://github.com/zeek/zeek.git
synced 2025-10-16 05:28:20 +00:00
Merge branch 'master' into topic/jsiwek/brofiler
Conflicts: src/main.cc
This commit is contained in:
commit
1181444f37
291 changed files with 17420 additions and 6314 deletions
|
@ -15,6 +15,8 @@
|
|||
#include <sys/ethernet.h>
|
||||
#elif defined(HAVE_NETINET_IF_ETHER_H)
|
||||
#include <netinet/if_ether.h>
|
||||
#elif defined(HAVE_NET_ETHERTYPES_H)
|
||||
#include <net/ethertypes.h>
|
||||
#endif
|
||||
|
||||
#ifndef arp_pkthdr
|
||||
|
|
17
src/Attr.cc
17
src/Attr.cc
|
@ -60,16 +60,19 @@ void Attr::DescribeReST(ODesc* d) const
|
|||
d->Add("=");
|
||||
d->SP();
|
||||
|
||||
if ( expr->Type()->Tag() == TYPE_FUNC )
|
||||
d->Add(":bro:type:`func`");
|
||||
|
||||
else if ( expr->Type()->Tag() == TYPE_ENUM )
|
||||
if ( expr->Tag() == EXPR_NAME )
|
||||
{
|
||||
d->Add(":bro:enum:`");
|
||||
d->Add(":bro:see:`");
|
||||
expr->Describe(d);
|
||||
d->Add("`");
|
||||
}
|
||||
|
||||
else if ( expr->Type()->Tag() == TYPE_FUNC )
|
||||
{
|
||||
d->Add(":bro:type:`func`");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
d->Add("``");
|
||||
|
@ -481,7 +484,11 @@ bool Attributes::DoSerialize(SerialInfo* info) const
|
|||
loop_over_list((*attrs), i)
|
||||
{
|
||||
Attr* a = (*attrs)[i];
|
||||
SERIALIZE_OPTIONAL(a->AttrExpr())
|
||||
|
||||
// Broccoli doesn't support expressions.
|
||||
Expr* e = (! info->broccoli_peer) ? a->AttrExpr() : 0;
|
||||
SERIALIZE_OPTIONAL(e);
|
||||
|
||||
if ( ! SERIALIZE(char(a->Tag())) )
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -170,13 +170,26 @@ void BroDoc::WriteDocFile() const
|
|||
{
|
||||
WriteToDoc(".. Automatically generated. Do not edit.\n\n");
|
||||
|
||||
WriteToDoc(":tocdepth: 3\n\n");
|
||||
|
||||
WriteSectionHeading(doc_title.c_str(), '=');
|
||||
|
||||
WriteToDoc("\n:download:`Original Source File <%s>`\n\n",
|
||||
downloadable_filename.c_str());
|
||||
WriteStringList(".. bro:namespace:: %s\n", modules);
|
||||
|
||||
WriteSectionHeading("Overview", '-');
|
||||
WriteStringList("%s\n", "%s\n\n", summary);
|
||||
WriteToDoc("\n");
|
||||
|
||||
// WriteSectionHeading("Overview", '-');
|
||||
WriteStringList("%s\n", summary);
|
||||
|
||||
WriteToDoc("\n");
|
||||
|
||||
if ( ! modules.empty() )
|
||||
{
|
||||
WriteToDoc(":Namespace%s: ", (modules.size() > 1 ? "s" : ""));
|
||||
// WriteStringList(":bro:namespace:`%s`", modules);
|
||||
WriteStringList("``%s``, ", "``%s``", modules);
|
||||
WriteToDoc("\n");
|
||||
}
|
||||
|
||||
if ( ! imports.empty() )
|
||||
{
|
||||
|
@ -196,37 +209,38 @@ void BroDoc::WriteDocFile() const
|
|||
WriteToDoc("\n");
|
||||
}
|
||||
|
||||
WriteToDoc(":Source File: :download:`%s`\n",
|
||||
downloadable_filename.c_str());
|
||||
|
||||
WriteToDoc("\n");
|
||||
|
||||
WriteInterface("Summary", '~', '#', true, true);
|
||||
|
||||
if ( ! modules.empty() )
|
||||
{
|
||||
WriteSectionHeading("Namespaces", '~');
|
||||
WriteStringList(".. bro:namespace:: %s\n", modules);
|
||||
WriteToDoc("\n");
|
||||
}
|
||||
|
||||
if ( ! notices.empty() )
|
||||
WriteBroDocObjList(notices, "Notices", '~');
|
||||
WriteBroDocObjList(notices, "Notices", '#');
|
||||
|
||||
WriteInterface("Public Interface", '-', '~', true, false);
|
||||
if ( port_analysis.size() || packet_filter.size() )
|
||||
WriteSectionHeading("Configuration Changes", '#');
|
||||
|
||||
if ( ! port_analysis.empty() )
|
||||
{
|
||||
WriteSectionHeading("Port Analysis", '-');
|
||||
WriteToDoc(":ref:`More Information <common_port_analysis_doc>`\n\n");
|
||||
WriteStringList("%s", port_analysis);
|
||||
WriteSectionHeading("Port Analysis", '^');
|
||||
WriteToDoc("Loading this script makes the following changes to "
|
||||
":bro:see:`dpd_config`.\n\n");
|
||||
WriteStringList("%s, ", "%s", port_analysis);
|
||||
}
|
||||
|
||||
if ( ! packet_filter.empty() )
|
||||
{
|
||||
WriteSectionHeading("Packet Filter", '-');
|
||||
WriteToDoc(":ref:`More Information <common_packet_filter_doc>`\n\n");
|
||||
WriteSectionHeading("Packet Filter", '^');
|
||||
WriteToDoc("Loading this script makes the following changes to "
|
||||
":bro:see:`capture_filters`.\n\n");
|
||||
WriteToDoc("Filters added::\n\n");
|
||||
WriteToDoc("%s\n", packet_filter.c_str());
|
||||
}
|
||||
|
||||
WriteInterface("Detailed Interface", '~', '#', true, false);
|
||||
|
||||
#if 0 // Disabled for now.
|
||||
BroDocObjList::const_iterator it;
|
||||
bool hasPrivateIdentifiers = false;
|
||||
|
@ -241,7 +255,7 @@ void BroDoc::WriteDocFile() const
|
|||
}
|
||||
|
||||
if ( hasPrivateIdentifiers )
|
||||
WriteInterface("Private Interface", '-', '~', false, false);
|
||||
WriteInterface("Private Interface", '~', '#', false, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,12 @@
|
|||
#include "ID.h"
|
||||
#include "BroDocObj.h"
|
||||
|
||||
BroDocObj* BroDocObj::last = 0;
|
||||
|
||||
BroDocObj::BroDocObj(const ID* id, std::list<std::string>*& reST,
|
||||
bool is_fake)
|
||||
{
|
||||
last = this;
|
||||
broID = id;
|
||||
reST_doc_strings = reST;
|
||||
reST = 0;
|
||||
|
|
|
@ -103,6 +103,20 @@ public:
|
|||
*/
|
||||
int LongestShortDescLen() const;
|
||||
|
||||
/**
|
||||
* Adds a reST documentation string to this BroDocObj's list.
|
||||
* @param s the documentation string to append.
|
||||
*/
|
||||
void AddDocString(const std::string& s)
|
||||
{
|
||||
if ( ! reST_doc_strings )
|
||||
reST_doc_strings = new std::list<std::string>();
|
||||
reST_doc_strings->push_back(s);
|
||||
FormulateShortDesc();
|
||||
}
|
||||
|
||||
static BroDocObj* last;
|
||||
|
||||
protected:
|
||||
std::list<std::string>* reST_doc_strings;
|
||||
std::list<std::string> short_desc;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}
|
||||
include_directories(BEFORE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
|
|
|
@ -595,8 +595,6 @@ void DNS_Mgr::Resolve()
|
|||
}
|
||||
else
|
||||
--num_pending;
|
||||
|
||||
delete dr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
80
src/Desc.cc
80
src/Desc.cc
|
@ -41,8 +41,7 @@ ODesc::ODesc(desc_type t, BroFile* arg_f)
|
|||
do_flush = 1;
|
||||
include_stats = 0;
|
||||
indent_with_spaces = 0;
|
||||
escape = 0;
|
||||
escape_len = 0;
|
||||
escape = false;
|
||||
}
|
||||
|
||||
ODesc::~ODesc()
|
||||
|
@ -56,10 +55,9 @@ ODesc::~ODesc()
|
|||
free(base);
|
||||
}
|
||||
|
||||
void ODesc::SetEscape(const char* arg_escape, int len)
|
||||
void ODesc::EnableEscaping()
|
||||
{
|
||||
escape = arg_escape;
|
||||
escape_len = len;
|
||||
escape = true;
|
||||
}
|
||||
|
||||
void ODesc::PushIndent()
|
||||
|
@ -228,6 +226,25 @@ static const char* find_first_unprintable(ODesc* d, const char* bytes, unsigned
|
|||
return 0;
|
||||
}
|
||||
|
||||
pair<const char*, size_t> ODesc::FirstEscapeLoc(const char* bytes, size_t n)
|
||||
{
|
||||
pair<const char*, size_t> p(find_first_unprintable(this, bytes, n), 1);
|
||||
|
||||
string str(bytes, n);
|
||||
list<string>::const_iterator it;
|
||||
for ( it = escape_sequences.begin(); it != escape_sequences.end(); ++it )
|
||||
{
|
||||
size_t pos = str.find(*it);
|
||||
if ( pos != string::npos && (p.first == 0 || bytes + pos < p.first) )
|
||||
{
|
||||
p.first = bytes + pos;
|
||||
p.second = it->size();
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void ODesc::AddBytes(const void* bytes, unsigned int n)
|
||||
{
|
||||
if ( ! escape )
|
||||
|
@ -241,45 +258,30 @@ void ODesc::AddBytes(const void* bytes, unsigned int n)
|
|||
|
||||
while ( s < e )
|
||||
{
|
||||
const char* t1 = (const char*) memchr(s, escape[0], e - s);
|
||||
|
||||
if ( ! t1 )
|
||||
t1 = e;
|
||||
|
||||
const char* t2 = find_first_unprintable(this, s, t1 - s);
|
||||
|
||||
if ( t2 && t2 < t1 )
|
||||
pair<const char*, size_t> p = FirstEscapeLoc(s, e - s);
|
||||
if ( p.first )
|
||||
{
|
||||
AddBytesRaw(s, t2 - s);
|
||||
|
||||
char hex[6] = "\\x00";
|
||||
hex[2] = hex_chars[((*t2) & 0xf0) >> 4];
|
||||
hex[3] = hex_chars[(*t2) & 0x0f];
|
||||
AddBytesRaw(hex, 4);
|
||||
|
||||
s = t2 + 1;
|
||||
continue;
|
||||
AddBytesRaw(s, p.first - s);
|
||||
if ( p.second == 1 )
|
||||
{
|
||||
char hex[6] = "\\x00";
|
||||
hex[2] = hex_chars[((*p.first) & 0xf0) >> 4];
|
||||
hex[3] = hex_chars[(*p.first) & 0x0f];
|
||||
AddBytesRaw(hex, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
string esc_str = get_escaped_string(string(p.first, p.second), true);
|
||||
AddBytesRaw(esc_str.c_str(), esc_str.size());
|
||||
}
|
||||
s = p.first + p.second;
|
||||
}
|
||||
|
||||
if ( memcmp(t1, escape, escape_len) != 0 )
|
||||
break;
|
||||
|
||||
AddBytesRaw(s, t1 - s);
|
||||
|
||||
for ( int i = 0; i < escape_len; ++i )
|
||||
else
|
||||
{
|
||||
char hex[5] = "\\x00";
|
||||
hex[2] = hex_chars[((*t1) & 0xf0) >> 4];
|
||||
hex[3] = hex_chars[(*t1) & 0x0f];
|
||||
AddBytesRaw(hex, 4);
|
||||
++t1;
|
||||
AddBytesRaw(s, e - s);
|
||||
break;
|
||||
}
|
||||
|
||||
s = t1;
|
||||
}
|
||||
|
||||
if ( s < e )
|
||||
AddBytesRaw(s, e - s);
|
||||
}
|
||||
|
||||
void ODesc::AddBytesRaw(const void* bytes, unsigned int n)
|
||||
|
|
28
src/Desc.h
28
src/Desc.h
|
@ -4,6 +4,8 @@
|
|||
#define descriptor_h
|
||||
|
||||
#include <stdio.h>
|
||||
#include <list>
|
||||
#include <utility>
|
||||
#include "BroString.h"
|
||||
|
||||
typedef enum {
|
||||
|
@ -48,8 +50,13 @@ public:
|
|||
|
||||
void SetFlush(int arg_do_flush) { do_flush = arg_do_flush; }
|
||||
|
||||
// The string passed in must remain valid as long as this object lives.
|
||||
void SetEscape(const char* escape, int len);
|
||||
void EnableEscaping();
|
||||
void AddEscapeSequence(const char* s) { escape_sequences.push_back(s); }
|
||||
void AddEscapeSequence(const char* s, size_t n)
|
||||
{ escape_sequences.push_back(string(s, n)); }
|
||||
void RemoveEscapeSequence(const char* s) { escape_sequences.remove(s); }
|
||||
void RemoveEscapeSequence(const char* s, size_t n)
|
||||
{ escape_sequences.remove(string(s, n)); }
|
||||
|
||||
void PushIndent();
|
||||
void PopIndent();
|
||||
|
@ -133,6 +140,19 @@ protected:
|
|||
|
||||
void OutOfMemory();
|
||||
|
||||
/**
|
||||
* Returns the location of the first place in the bytes to be hex-escaped.
|
||||
*
|
||||
* @param bytes the starting memory address to start searching for
|
||||
* escapable character.
|
||||
* @param n the maximum number of bytes to search.
|
||||
* @return a pair whose first element represents a starting memory address
|
||||
* to be escaped up to the number of characters indicated by the
|
||||
* second element. The first element may be 0 if nothing is
|
||||
* to be escaped.
|
||||
*/
|
||||
pair<const char*, size_t> FirstEscapeLoc(const char* bytes, size_t n);
|
||||
|
||||
desc_type type;
|
||||
desc_style style;
|
||||
|
||||
|
@ -140,8 +160,8 @@ protected:
|
|||
unsigned int offset; // where we are in the buffer
|
||||
unsigned int size; // size of buffer in bytes
|
||||
|
||||
int escape_len; // number of bytes in to escape sequence
|
||||
const char* escape; // bytes to escape on output
|
||||
bool escape; // escape unprintable characters in output?
|
||||
list<string> escape_sequences; // additional sequences of chars to escape
|
||||
|
||||
BroFile* f; // or the file we're using.
|
||||
|
||||
|
|
12
src/Expr.cc
12
src/Expr.cc
|
@ -359,7 +359,7 @@ bool NameExpr::DoUnserialize(UnserialInfo* info)
|
|||
if ( id )
|
||||
::Ref(id);
|
||||
else
|
||||
reporter->Warning("unserialized unknown global name");
|
||||
reporter->Warning("configuration changed: unserialized unknown global name from persistent state");
|
||||
|
||||
delete [] name;
|
||||
}
|
||||
|
@ -4053,7 +4053,15 @@ Val* RecordCoerceExpr::Fold(Val* v) const
|
|||
val->Assign(i, rhs);
|
||||
}
|
||||
else
|
||||
val->Assign(i, 0);
|
||||
{
|
||||
const Attr* def =
|
||||
Type()->AsRecordType()->FieldDecl(i)->FindAttr(ATTR_DEFAULT);
|
||||
|
||||
if ( def )
|
||||
val->Assign(i, def->AttrExpr()->Eval(0));
|
||||
else
|
||||
val->Assign(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
|
|
|
@ -81,16 +81,18 @@ struct LogMgr::Stream {
|
|||
bool LogField::Read(SerializationFormat* fmt)
|
||||
{
|
||||
int t;
|
||||
int st;
|
||||
|
||||
bool success = (fmt->Read(&name, "name") && fmt->Read(&t, "type"));
|
||||
bool success = (fmt->Read(&name, "name") && fmt->Read(&t, "type") && fmt->Read(&st, "subtype") );
|
||||
type = (TypeTag) t;
|
||||
subtype = (TypeTag) st;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool LogField::Write(SerializationFormat* fmt) const
|
||||
{
|
||||
return (fmt->Write(name, "name") && fmt->Write((int)type, "type"));
|
||||
return (fmt->Write(name, "name") && fmt->Write((int)type, "type") && fmt->Write((int)subtype, "subtype"));
|
||||
}
|
||||
|
||||
LogVal::~LogVal()
|
||||
|
@ -157,7 +159,7 @@ bool LogVal::IsCompatibleType(BroType* t, bool atomic_only)
|
|||
if ( atomic_only )
|
||||
return false;
|
||||
|
||||
return IsCompatibleType(t->AsVectorType()->YieldType());
|
||||
return IsCompatibleType(t->AsVectorType()->YieldType(), true);
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -707,6 +709,14 @@ bool LogMgr::TraverseRecord(Stream* stream, Filter* filter, RecordType* rt,
|
|||
LogField* field = new LogField();
|
||||
field->name = new_path;
|
||||
field->type = t->Tag();
|
||||
if ( field->type == TYPE_TABLE )
|
||||
{
|
||||
field->subtype = t->AsSetType()->Indices()->PureType()->Tag();
|
||||
}
|
||||
else if ( field->type == TYPE_VECTOR )
|
||||
{
|
||||
field->subtype = t->AsVectorType()->YieldType()->Tag();
|
||||
}
|
||||
filter->fields[filter->num_fields - 1] = field;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,10 +15,12 @@ class SerializationFormat;
|
|||
struct LogField {
|
||||
string name;
|
||||
TypeTag type;
|
||||
// inner type of sets
|
||||
TypeTag subtype;
|
||||
|
||||
LogField() { }
|
||||
LogField() { subtype = TYPE_VOID; }
|
||||
LogField(const LogField& other)
|
||||
: name(other.name), type(other.type) { }
|
||||
: name(other.name), type(other.type), subtype(other.subtype) { }
|
||||
|
||||
// (Un-)serialize.
|
||||
bool Read(SerializationFormat* fmt);
|
||||
|
|
|
@ -6,27 +6,6 @@
|
|||
#include "LogWriterAscii.h"
|
||||
#include "NetVar.h"
|
||||
|
||||
/**
|
||||
* Takes a string, escapes each character into its equivalent hex code (\x##), and
|
||||
* returns a string containing all escaped values.
|
||||
*
|
||||
* @param str string to escape
|
||||
* @return A std::string containing a list of escaped hex values of the form \x##
|
||||
*/
|
||||
static string get_escaped_string(const std::string& str)
|
||||
{
|
||||
char tbuf[16];
|
||||
string esc = "";
|
||||
|
||||
for ( size_t i = 0; i < str.length(); ++i )
|
||||
{
|
||||
snprintf(tbuf, sizeof(tbuf), "\\x%02x", str[i]);
|
||||
esc += tbuf;
|
||||
}
|
||||
|
||||
return esc;
|
||||
}
|
||||
|
||||
LogWriterAscii::LogWriterAscii()
|
||||
{
|
||||
file = 0;
|
||||
|
@ -59,7 +38,8 @@ LogWriterAscii::LogWriterAscii()
|
|||
memcpy(header_prefix, BifConst::LogAscii::header_prefix->Bytes(),
|
||||
header_prefix_len);
|
||||
|
||||
desc.SetEscape(separator, separator_len);
|
||||
desc.EnableEscaping();
|
||||
desc.AddEscapeSequence(separator, separator_len);
|
||||
}
|
||||
|
||||
LogWriterAscii::~LogWriterAscii()
|
||||
|
@ -88,7 +68,7 @@ bool LogWriterAscii::DoInit(string path, int num_fields,
|
|||
if ( output_to_stdout )
|
||||
path = "/dev/stdout";
|
||||
|
||||
fname = IsSpecial(path) ? path : path + ".log";
|
||||
fname = IsSpecial(path) ? path : path + "." + LogExt();
|
||||
|
||||
if ( ! (file = fopen(fname.c_str(), "w")) )
|
||||
{
|
||||
|
@ -102,13 +82,19 @@ bool LogWriterAscii::DoInit(string path, int num_fields,
|
|||
{
|
||||
string str = string(header_prefix, header_prefix_len)
|
||||
+ "separator " // Always use space as separator here.
|
||||
+ get_escaped_string(string(separator, separator_len))
|
||||
+ get_escaped_string(string(separator, separator_len), false)
|
||||
+ "\n";
|
||||
|
||||
if( fwrite(str.c_str(), str.length(), 1, file) != 1 )
|
||||
goto write_error;
|
||||
|
||||
if ( ! WriteHeaderField("path", path) )
|
||||
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))) )
|
||||
goto write_error;
|
||||
|
||||
string names;
|
||||
|
@ -125,6 +111,12 @@ bool LogWriterAscii::DoInit(string path, int num_fields,
|
|||
const LogField* field = fields[i];
|
||||
names += field->name;
|
||||
types += type_name(field->type);
|
||||
if ( (field->type == TYPE_TABLE) || (field->type == TYPE_VECTOR) )
|
||||
{
|
||||
types += "[";
|
||||
types += type_name(field->subtype);
|
||||
types += "]";
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! (WriteHeaderField("fields", names)
|
||||
|
@ -200,10 +192,33 @@ bool LogWriterAscii::DoWriteOne(ODesc* desc, LogVal* val, const LogField* field)
|
|||
case TYPE_FUNC:
|
||||
{
|
||||
int size = val->val.string_val->size();
|
||||
if ( size )
|
||||
desc->AddN(val->val.string_val->data(), val->val.string_val->size());
|
||||
else
|
||||
const char* data = val->val.string_val->data();
|
||||
|
||||
if ( ! size )
|
||||
{
|
||||
desc->AddN(empty_field, empty_field_len);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( size == unset_field_len && memcmp(data, unset_field, size) == 0 )
|
||||
{
|
||||
// The value we'd write out would match exactly the
|
||||
// place-holder we use for unset optional fields. We
|
||||
// escape the first character so that the output
|
||||
// won't be ambigious.
|
||||
static const char hex_chars[] = "0123456789abcdef";
|
||||
char hex[6] = "\\x00";
|
||||
hex[2] = hex_chars[((*data) & 0xf0) >> 4];
|
||||
hex[3] = hex_chars[(*data) & 0x0f];
|
||||
desc->AddRaw(hex, 4);
|
||||
|
||||
++data;
|
||||
--size;
|
||||
}
|
||||
|
||||
if ( size )
|
||||
desc->AddN(data, size);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -215,14 +230,19 @@ bool LogWriterAscii::DoWriteOne(ODesc* desc, LogVal* val, const LogField* field)
|
|||
break;
|
||||
}
|
||||
|
||||
desc->AddEscapeSequence(set_separator, set_separator_len);
|
||||
for ( int j = 0; j < val->val.set_val.size; j++ )
|
||||
{
|
||||
if ( j > 0 )
|
||||
desc->AddN(set_separator, set_separator_len);
|
||||
desc->AddRaw(set_separator, set_separator_len);
|
||||
|
||||
if ( ! DoWriteOne(desc, val->val.set_val.vals[j], field) )
|
||||
{
|
||||
desc->RemoveEscapeSequence(set_separator, set_separator_len);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
desc->RemoveEscapeSequence(set_separator, set_separator_len);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -235,14 +255,19 @@ bool LogWriterAscii::DoWriteOne(ODesc* desc, LogVal* val, const LogField* field)
|
|||
break;
|
||||
}
|
||||
|
||||
desc->AddEscapeSequence(set_separator, set_separator_len);
|
||||
for ( int j = 0; j < val->val.vector_val.size; j++ )
|
||||
{
|
||||
if ( j > 0 )
|
||||
desc->AddN(set_separator, set_separator_len);
|
||||
desc->AddRaw(set_separator, set_separator_len);
|
||||
|
||||
if ( ! DoWriteOne(desc, val->val.vector_val.vals[j], field) )
|
||||
{
|
||||
desc->RemoveEscapeSequence(set_separator, set_separator_len);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
desc->RemoveEscapeSequence(set_separator, set_separator_len);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -297,7 +322,7 @@ bool LogWriterAscii::DoRotate(string rotated_path, double open,
|
|||
fclose(file);
|
||||
file = 0;
|
||||
|
||||
string nname = rotated_path + ".log";
|
||||
string nname = rotated_path + "." + LogExt();
|
||||
rename(fname.c_str(), nname.c_str());
|
||||
|
||||
if ( ! FinishedRotation(nname, fname, open, close, terminating) )
|
||||
|
@ -315,4 +340,9 @@ bool LogWriterAscii::DoSetBuf(bool enabled)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
string LogWriterAscii::LogExt()
|
||||
{
|
||||
const char* ext = getenv("BRO_LOG_SUFFIX");
|
||||
if ( ! ext ) ext = "log";
|
||||
return ext;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ public:
|
|||
~LogWriterAscii();
|
||||
|
||||
static LogWriter* Instantiate() { return new LogWriterAscii; }
|
||||
static string LogExt();
|
||||
|
||||
protected:
|
||||
virtual bool DoInit(string path, int num_fields,
|
||||
|
|
|
@ -16,7 +16,9 @@ RecordType* pcap_packet;
|
|||
RecordType* signature_state;
|
||||
EnumType* transport_proto;
|
||||
TableType* string_set;
|
||||
TableType* string_array;
|
||||
TableType* count_set;
|
||||
VectorType* string_vec;
|
||||
|
||||
int watchdog_interval;
|
||||
|
||||
|
@ -328,6 +330,8 @@ void init_net_var()
|
|||
pcap_packet = internal_type("pcap_packet")->AsRecordType();
|
||||
transport_proto = internal_type("transport_proto")->AsEnumType();
|
||||
string_set = internal_type("string_set")->AsTableType();
|
||||
string_array = internal_type("string_array")->AsTableType();
|
||||
string_vec = internal_type("string_vec")->AsVectorType();
|
||||
|
||||
ignore_checksums = opt_internal_int("ignore_checksums");
|
||||
partial_connection_ok = opt_internal_int("partial_connection_ok");
|
||||
|
|
|
@ -19,7 +19,9 @@ extern RecordType* SYN_packet;
|
|||
extern RecordType* pcap_packet;
|
||||
extern EnumType* transport_proto;
|
||||
extern TableType* string_set;
|
||||
extern TableType* string_array;
|
||||
extern TableType* count_set;
|
||||
extern VectorType* string_vec;
|
||||
|
||||
extern int watchdog_interval;
|
||||
|
||||
|
|
|
@ -385,6 +385,9 @@ inline void RemoteSerializer::SetupSerialInfo(SerialInfo* info, Peer* peer)
|
|||
peer->phase == Peer::RUNNING )
|
||||
info->new_cache_strategy = true;
|
||||
|
||||
if ( (peer->caps & Peer::BROCCOLI_PEER) )
|
||||
info->broccoli_peer = true;
|
||||
|
||||
info->include_locations = false;
|
||||
}
|
||||
|
||||
|
@ -1457,7 +1460,7 @@ void RemoteSerializer::Finish()
|
|||
Poll(true);
|
||||
while ( io->CanWrite() );
|
||||
|
||||
loop_over_list(peers, i)
|
||||
loop_over_list(peers, i)
|
||||
{
|
||||
CloseConnection(peers[i]);
|
||||
}
|
||||
|
@ -2113,6 +2116,9 @@ bool RemoteSerializer::HandshakeDone(Peer* peer)
|
|||
if ( (peer->caps & Peer::NEW_CACHE_STRATEGY) )
|
||||
Log(LogInfo, "peer supports keep-in-cache; using that", peer);
|
||||
|
||||
if ( (peer->caps & Peer::BROCCOLI_PEER) )
|
||||
Log(LogInfo, "peer is a Broccoli", peer);
|
||||
|
||||
if ( peer->logs_requested )
|
||||
log_mgr->SendAllWritersTo(peer->id);
|
||||
|
||||
|
@ -2365,6 +2371,9 @@ bool RemoteSerializer::ProcessSerialization()
|
|||
current_peer->phase == Peer::RUNNING )
|
||||
info.new_cache_strategy = true;
|
||||
|
||||
if ( current_peer->caps & Peer::BROCCOLI_PEER )
|
||||
info.broccoli_peer = true;
|
||||
|
||||
if ( ! forward_remote_state_changes )
|
||||
ignore_accesses = true;
|
||||
|
||||
|
@ -2923,25 +2932,37 @@ void RemoteSerializer::Log(LogLevel level, const char* msg)
|
|||
void RemoteSerializer::Log(LogLevel level, const char* msg, Peer* peer,
|
||||
LogSrc src)
|
||||
{
|
||||
if ( peer )
|
||||
{
|
||||
val_list* vl = new val_list();
|
||||
vl->append(peer->val->Ref());
|
||||
vl->append(new Val(level, TYPE_COUNT));
|
||||
vl->append(new Val(src, TYPE_COUNT));
|
||||
vl->append(new StringVal(msg));
|
||||
mgr.QueueEvent(remote_log_peer, vl);
|
||||
}
|
||||
else
|
||||
{
|
||||
val_list* vl = new val_list();
|
||||
vl->append(new Val(level, TYPE_COUNT));
|
||||
vl->append(new Val(src, TYPE_COUNT));
|
||||
vl->append(new StringVal(msg));
|
||||
mgr.QueueEvent(remote_log, vl);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
const int BUFSIZE = 1024;
|
||||
char buffer[BUFSIZE];
|
||||
|
||||
int len = 0;
|
||||
|
||||
if ( peer )
|
||||
len += snprintf(buffer + len, sizeof(buffer) - len,
|
||||
"[#%d/%s:%d] ", int(peer->id), ip2a(peer->ip),
|
||||
peer->port);
|
||||
len += snprintf(buffer + len, sizeof(buffer) - len, "[#%d/%s:%d] ",
|
||||
int(peer->id), ip2a(peer->ip), peer->port);
|
||||
|
||||
len += safe_snprintf(buffer + len, sizeof(buffer) - len, "%s", msg);
|
||||
|
||||
val_list* vl = new val_list();
|
||||
vl->append(new Val(level, TYPE_COUNT));
|
||||
vl->append(new Val(src, TYPE_COUNT));
|
||||
vl->append(new StringVal(buffer));
|
||||
mgr.QueueEvent(remote_log, vl);
|
||||
|
||||
DEBUG_COMM(fmt("parent: %.6f %s", current_time(), buffer));
|
||||
#endif
|
||||
}
|
||||
|
||||
void RemoteSerializer::RaiseEvent(EventHandlerPtr event, Peer* peer,
|
||||
|
|
|
@ -198,6 +198,7 @@ protected:
|
|||
static const int NO_CACHING = 2;
|
||||
static const int PID_64BIT = 4;
|
||||
static const int NEW_CACHE_STRATEGY = 8;
|
||||
static const int BROCCOLI_PEER = 16;
|
||||
|
||||
// Constants to remember to who did something.
|
||||
static const int NONE = 0;
|
||||
|
|
944
src/SSLv2.cc
944
src/SSLv2.cc
|
@ -1,944 +0,0 @@
|
|||
#include "SSLv2.h"
|
||||
#include "SSLv3.h"
|
||||
|
||||
// --- Initalization of static variables --------------------------------------
|
||||
|
||||
uint SSLv2_Interpreter::totalConnections = 0;
|
||||
uint SSLv2_Interpreter::analyzedConnections = 0;
|
||||
uint SSLv2_Interpreter::openedConnections = 0;
|
||||
uint SSLv2_Interpreter::failedConnections = 0;
|
||||
uint SSLv2_Interpreter::weirdConnections = 0;
|
||||
uint SSLv2_Interpreter::totalRecords = 0;
|
||||
uint SSLv2_Interpreter::clientHelloRecords = 0;
|
||||
uint SSLv2_Interpreter::serverHelloRecords = 0;
|
||||
uint SSLv2_Interpreter::clientMasterKeyRecords = 0;
|
||||
uint SSLv2_Interpreter::errorRecords = 0;
|
||||
|
||||
|
||||
// --- SSLv2_Interpreter -------------------------------------------------------
|
||||
|
||||
/*!
|
||||
* The Constructor.
|
||||
*
|
||||
* \param proxy Pointer to the SSLProxy_Analyzer who created this instance.
|
||||
*/
|
||||
SSLv2_Interpreter::SSLv2_Interpreter(SSLProxy_Analyzer* proxy)
|
||||
: SSL_Interpreter(proxy)
|
||||
{
|
||||
++totalConnections;
|
||||
records = 0;
|
||||
bAnalyzedCounted = false;
|
||||
connState = START;
|
||||
|
||||
pServerCipherSpecs = 0;
|
||||
pClientCipherSpecs = 0;
|
||||
bClientWantsCachedSession = false;
|
||||
usedCipherSpec = (SSLv2_CipherSpec) 0;
|
||||
|
||||
pConnectionId = 0;
|
||||
pChallenge = 0;
|
||||
pSessionId = 0;
|
||||
pMasterClearKey = 0;
|
||||
pMasterEncryptedKey = 0;
|
||||
pClientReadKey = 0;
|
||||
pServerReadKey = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* The Destructor.
|
||||
*/
|
||||
SSLv2_Interpreter::~SSLv2_Interpreter()
|
||||
{
|
||||
if ( connState != CLIENT_MASTERKEY_SEEN &&
|
||||
connState != CACHED_SESSION &&
|
||||
connState != START && // we only complain if we saw some data
|
||||
connState != ERROR_SEEN )
|
||||
++failedConnections;
|
||||
|
||||
if ( connState != CLIENT_MASTERKEY_SEEN && connState != CACHED_SESSION )
|
||||
++weirdConnections;
|
||||
|
||||
delete pServerCipherSpecs;
|
||||
delete pClientCipherSpecs;
|
||||
delete pConnectionId;
|
||||
delete pChallenge;
|
||||
delete pSessionId;
|
||||
delete pMasterClearKey;
|
||||
delete pMasterEncryptedKey;
|
||||
delete pClientReadKey;
|
||||
delete pServerReadKey;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method implements SSL_Interpreter::BuildInterpreterEndpoints()
|
||||
*/
|
||||
void SSLv2_Interpreter::BuildInterpreterEndpoints()
|
||||
{
|
||||
orig = new SSLv2_Endpoint(this, 1);
|
||||
resp = new SSLv2_Endpoint(this, 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method prints some counters.
|
||||
*/
|
||||
void SSLv2_Interpreter::printStats()
|
||||
{
|
||||
printf("SSLv2:\n");
|
||||
printf("totalConnections = %u\n", totalConnections);
|
||||
printf("analyzedConnections = %u\n", analyzedConnections);
|
||||
printf("openedConnections = %u\n", openedConnections);
|
||||
printf("failedConnections = %u\n", failedConnections);
|
||||
printf("weirdConnections = %u\n", weirdConnections);
|
||||
|
||||
printf("totalRecords = %u\n", totalRecords);
|
||||
printf("clientHelloRecords = %u\n", clientHelloRecords);
|
||||
printf("serverHelloRecords = %u\n", serverHelloRecords);
|
||||
printf("clientMasterKeyRecords = %u\n", clientMasterKeyRecords);
|
||||
printf("errorRecords = %u\n", errorRecords);
|
||||
|
||||
printf("SSL_RecordBuilder::maxAllocCount = %u\n", SSL_RecordBuilder::maxAllocCount);
|
||||
printf("SSL_RecordBuilder::maxFragmentCount = %u\n", SSL_RecordBuilder::maxFragmentCount);
|
||||
printf("SSL_RecordBuilder::fragmentedHeaders = %u\n", SSL_RecordBuilder::fragmentedHeaders);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \return the current state of the ssl connection
|
||||
*/
|
||||
SSLv2_States SSLv2_Interpreter::ConnState()
|
||||
{
|
||||
return connState;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method is called by SSLv2_Endpoint::Deliver(). It is the main entry
|
||||
* point of this class. The header of the given SSLV2 record is analyzed and
|
||||
* its contents are then passed to the corresponding analyzer method. After
|
||||
* the record has been analyzed, the ssl connection state is updated.
|
||||
*
|
||||
* \param s Pointer to the endpoint which sent the record
|
||||
* \param length length of SSLv2 record
|
||||
* \param data pointer to SSLv2 record to analyze
|
||||
*/
|
||||
void SSLv2_Interpreter::NewSSLRecord(SSL_InterpreterEndpoint* s,
|
||||
int length, const u_char* data)
|
||||
{
|
||||
++records;
|
||||
++totalRecords;
|
||||
|
||||
if ( ! bAnalyzedCounted )
|
||||
{
|
||||
++analyzedConnections;
|
||||
bAnalyzedCounted = true;
|
||||
}
|
||||
|
||||
// We should see a maximum of 4 cleartext records.
|
||||
if ( records == 5 )
|
||||
{ // so this should never happen
|
||||
Weird("SSLv2: Saw more than 4 records, skipping connection...");
|
||||
proxy->SetSkip(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// SSLv2 record header analysis
|
||||
uint32 recordLength = 0; // data length of SSLv2 record
|
||||
bool isEscape = false;
|
||||
uint8 padding = 0;
|
||||
const u_char* contents;
|
||||
|
||||
if ( (data[0] & 0x80) > 0 )
|
||||
{ // we have a two-byte record header
|
||||
recordLength = ((data[0] & 0x7f) << 8) | data[1];
|
||||
contents = data + 2;
|
||||
if ( recordLength + 2 != uint32(length) )
|
||||
{
|
||||
// This should never happen, otherwise
|
||||
// we have a bug in the SSL_RecordBuilder.
|
||||
Weird("SSLv2: FATAL: recordLength doesn't match data block length!");
|
||||
connState = ERROR_REQUIRED;
|
||||
proxy->SetSkip(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // We have a three-byte record header.
|
||||
recordLength = ((data[0] & 0x3f) << 8) | data[1];
|
||||
isEscape = (data[0] & 0x40) != 0;
|
||||
padding = data[2];
|
||||
contents = data + 3;
|
||||
if ( recordLength + 3 != uint32(length) )
|
||||
{
|
||||
// This should never happen, otherwise
|
||||
// we have a bug in the SSL_RecordBuilder.
|
||||
Weird("SSLv2: FATAL: recordLength doesn't match data block length!");
|
||||
connState = ERROR_REQUIRED;
|
||||
proxy->SetSkip(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( padding == 0 && ! isEscape )
|
||||
Weird("SSLv2: 3 Byte record header, but no escape, no padding!");
|
||||
}
|
||||
|
||||
if ( recordLength == 0 )
|
||||
{
|
||||
Weird("SSLv2: Record length is zero (no record data)!");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isEscape )
|
||||
Weird("SSLv2: Record has escape bit set (security escape)!");
|
||||
|
||||
if ( padding > 0 && connState != CACHED_SESSION &&
|
||||
connState != CLIENT_MASTERKEY_SEEN )
|
||||
Weird("SSLv2 record with padding > 0 in cleartext!");
|
||||
|
||||
// MISSING:
|
||||
// A final consistency check is done when a block cipher is used
|
||||
// and the protocol is using encryption. The amount of data present
|
||||
// in a record (RECORD-LENGTH))must be a multiple of the cipher's
|
||||
// block size. If the received record is not a multiple of the
|
||||
// cipher's block size then the record is considered damaged, and it
|
||||
// is to be treated as if an "I/O Error" had occurred (i.e. an
|
||||
// unrecoverable error is asserted and the connection is closed).
|
||||
|
||||
switch ( connState ) {
|
||||
case START:
|
||||
// Only CLIENT-HELLLOs allowed here.
|
||||
if ( contents[0] != SSLv2_MT_CLIENT_HELLO )
|
||||
{
|
||||
Weird("SSLv2: First packet is not a CLIENT-HELLO!");
|
||||
analyzeRecord(s, recordLength, contents);
|
||||
connState = ERROR_REQUIRED;
|
||||
}
|
||||
else
|
||||
connState = ClientHelloRecord(s, recordLength, contents);
|
||||
break;
|
||||
|
||||
case CLIENT_HELLO_SEEN:
|
||||
// Only SERVER-HELLOs or ERRORs allowed here.
|
||||
if ( contents[0] == SSLv2_MT_SERVER_HELLO )
|
||||
connState = ServerHelloRecord(s, recordLength, contents);
|
||||
else if ( contents[0] == SSLv2_MT_ERROR )
|
||||
connState = ErrorRecord(s, recordLength, contents);
|
||||
else
|
||||
{
|
||||
Weird("SSLv2: State violation in CLIENT_HELLO_SEEN!");
|
||||
analyzeRecord(s, recordLength, contents);
|
||||
connState = ERROR_REQUIRED;
|
||||
}
|
||||
break;
|
||||
|
||||
case NEW_SESSION:
|
||||
// We expect a client master key.
|
||||
if ( contents[0] == SSLv2_MT_CLIENT_MASTER_KEY )
|
||||
connState = ClientMasterKeyRecord(s, recordLength, contents);
|
||||
else if ( contents[0] == SSLv2_MT_ERROR )
|
||||
connState = ErrorRecord(s, recordLength, contents);
|
||||
else
|
||||
{
|
||||
Weird("SSLv2: State violation in NEW_SESSION or encrypted record!");
|
||||
analyzeRecord(s, recordLength, contents);
|
||||
connState = ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
delete pServerCipherSpecs;
|
||||
pServerCipherSpecs = 0;
|
||||
break;
|
||||
|
||||
case CACHED_SESSION:
|
||||
delete pServerCipherSpecs;
|
||||
pServerCipherSpecs = 0;
|
||||
// No break here.
|
||||
|
||||
case CLIENT_MASTERKEY_SEEN:
|
||||
// If no error record, no further analysis.
|
||||
if ( contents[0] == SSLv2_MT_ERROR &&
|
||||
recordLength == SSLv2_ERROR_RECORD_SIZE )
|
||||
connState = ErrorRecord(s, recordLength, contents);
|
||||
else
|
||||
{
|
||||
// So we finished the cleartext handshake.
|
||||
// Skip all further data.
|
||||
|
||||
proxy->SetSkip(1);
|
||||
++openedConnections;
|
||||
}
|
||||
break;
|
||||
|
||||
case ERROR_REQUIRED:
|
||||
if ( contents[0] == SSLv2_MT_ERROR )
|
||||
connState = ErrorRecord(s, recordLength, contents);
|
||||
else
|
||||
{
|
||||
// We lost tracking: this should not happen.
|
||||
Weird("SSLv2: State inconsistency in ERROR_REQUIRED (lost tracking!)!");
|
||||
analyzeRecord(s, recordLength, contents);
|
||||
connState = ERROR_REQUIRED;
|
||||
}
|
||||
break;
|
||||
|
||||
case ERROR_SEEN:
|
||||
// We don't have recoverable errors in cleartext phase,
|
||||
// so we shouldn't see anymore packets.
|
||||
Weird("SSLv2: Traffic after error record!");
|
||||
analyzeRecord(s, recordLength, contents);
|
||||
break;
|
||||
|
||||
default:
|
||||
reporter->InternalError("SSLv2: unknown state");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method is called whenever the connection tracking failed. It calls
|
||||
* the corresponding analyzer method for the given SSLv2 record, but does not
|
||||
* update the ssl connection state.
|
||||
*
|
||||
* \param s Pointer to the endpoint which sent the record
|
||||
* \param length length of SSLv2 record
|
||||
* \param data pointer to SSLv2 record to analyze
|
||||
*/
|
||||
void SSLv2_Interpreter::analyzeRecord(SSL_InterpreterEndpoint* s,
|
||||
int length, const u_char* data)
|
||||
{
|
||||
switch ( data[0] ) {
|
||||
case SSLv2_MT_ERROR:
|
||||
ErrorRecord(s, length, data);
|
||||
break;
|
||||
|
||||
case SSLv2_MT_CLIENT_HELLO:
|
||||
ClientHelloRecord(s, length, data);
|
||||
break;
|
||||
|
||||
case SSLv2_MT_CLIENT_MASTER_KEY:
|
||||
ClientMasterKeyRecord(s, length, data);
|
||||
break;
|
||||
|
||||
case SSLv2_MT_SERVER_HELLO:
|
||||
ServerHelloRecord(s, length, data);
|
||||
break;
|
||||
|
||||
case SSLv2_MT_CLIENT_FINISHED:
|
||||
case SSLv2_MT_SERVER_VERIFY:
|
||||
case SSLv2_MT_SERVER_FINISHED:
|
||||
case SSLv2_MT_REQUEST_CERTIFICATE:
|
||||
case SSLv2_MT_CLIENT_CERTIFICATE:
|
||||
Weird("SSLv2: Encrypted record type seems to be in cleartext");
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unknown record type.
|
||||
Weird("SSLv2: Unknown record type or encrypted record");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method analyses a SSLv2 CLIENT-HELLO record.
|
||||
*
|
||||
* \param s Pointer to the endpoint which sent the record
|
||||
* \param length length of SSLv2 CLIENT-HELLO record
|
||||
* \param data pointer to SSLv2 CLIENT-HELLO record to analyze
|
||||
*
|
||||
* \return the updated state of the current ssl connection
|
||||
*/
|
||||
SSLv2_States SSLv2_Interpreter::ClientHelloRecord(SSL_InterpreterEndpoint* s,
|
||||
int recordLength, const u_char* recordData)
|
||||
{
|
||||
// This method gets the record's data (without the header).
|
||||
++clientHelloRecords;
|
||||
|
||||
if ( s != orig )
|
||||
Weird("SSLv2: CLIENT-HELLO record from server!");
|
||||
|
||||
// There should not be any pending data in the SSLv2 reassembler,
|
||||
// because the client should wait for a server response.
|
||||
if ( ((SSLv2_Endpoint*) s)->isDataPending() )
|
||||
Weird("SSLv2: Pending data in SSL_RecordBuilder after CLIENT-HELLO!");
|
||||
|
||||
// Client hello minimum header size check.
|
||||
if ( recordLength < SSLv2_CLIENT_HELLO_HEADER_SIZE )
|
||||
{
|
||||
Weird("SSLv2: CLIENT-HELLO is too small!");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// Extract the data of the client hello header.
|
||||
SSLv2_ClientHelloHeader ch;
|
||||
ch.clientVersion = uint16(recordData[1] << 8) | recordData[2];
|
||||
ch.cipherSpecLength = uint16(recordData[3] << 8) | recordData[4];
|
||||
ch.sessionIdLength = uint16(recordData[5] << 8) | recordData[6];
|
||||
ch.challengeLength = uint16(recordData[7] << 8) | recordData[8];
|
||||
|
||||
if ( ch.clientVersion != SSLProxy_Analyzer::SSLv20 &&
|
||||
ch.clientVersion != SSLProxy_Analyzer::SSLv30 &&
|
||||
ch.clientVersion != SSLProxy_Analyzer::SSLv31 )
|
||||
{
|
||||
Weird("SSLv2: Unsupported SSL-Version in CLIENT-HELLO");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
if ( ch.challengeLength + ch.cipherSpecLength + ch.sessionIdLength +
|
||||
SSLv2_CLIENT_HELLO_HEADER_SIZE != recordLength )
|
||||
{
|
||||
Weird("SSLv2: Size inconsistency in CLIENT-HELLO");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// The CIPHER-SPECS-LENGTH must be > 0 and a multiple of 3.
|
||||
if ( ch.cipherSpecLength == 0 || ch.cipherSpecLength % 3 != 0 )
|
||||
{
|
||||
Weird("SSLv2: Nonconform CIPHER-SPECS-LENGTH in CLIENT-HELLO.");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// The SESSION-ID-LENGTH must either be zero or 16.
|
||||
if ( ch.sessionIdLength != 0 && ch.sessionIdLength != 16 )
|
||||
Weird("SSLv2: Nonconform SESSION-ID-LENGTH in CLIENT-HELLO.");
|
||||
|
||||
if ( (ch.challengeLength < 16) || (ch.challengeLength > 32))
|
||||
Weird("SSLv2: Nonconform CHALLENGE-LENGTH in CLIENT-HELLO.");
|
||||
|
||||
const u_char* ptr = recordData;
|
||||
ptr += SSLv2_CLIENT_HELLO_HEADER_SIZE + ch.cipherSpecLength;
|
||||
|
||||
pSessionId = new SSL_DataBlock(ptr, ch.sessionIdLength);
|
||||
|
||||
// If decrypting, store the challenge.
|
||||
if ( ssl_store_key_material && ch.challengeLength <= 32 )
|
||||
pChallenge = new SSL_DataBlock(ptr, ch.challengeLength);
|
||||
|
||||
bClientWantsCachedSession = ch.sessionIdLength != 0;
|
||||
|
||||
TableVal* currentCipherSuites =
|
||||
analyzeCiphers(s, ch.cipherSpecLength,
|
||||
recordData + SSLv2_CLIENT_HELLO_HEADER_SIZE);
|
||||
|
||||
fire_ssl_conn_attempt(ch.clientVersion, currentCipherSuites);
|
||||
|
||||
return CLIENT_HELLO_SEEN;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method analyses a SSLv2 SERVER-HELLO record.
|
||||
*
|
||||
* \param s Pointer to the endpoint which sent the record
|
||||
* \param length length of SSLv2 SERVER-HELLO record
|
||||
* \param data pointer to SSLv2 SERVER-HELLO record to analyze
|
||||
*
|
||||
* \return the updated state of the current ssl connection
|
||||
*/
|
||||
SSLv2_States SSLv2_Interpreter::ServerHelloRecord(SSL_InterpreterEndpoint* s,
|
||||
int recordLength, const u_char* recordData)
|
||||
{
|
||||
++serverHelloRecords;
|
||||
TableVal* currentCipherSuites = NULL;
|
||||
|
||||
if ( s != resp )
|
||||
Weird("SSLv2: SERVER-HELLO from client!");
|
||||
|
||||
if ( recordLength < SSLv2_SERVER_HELLO_HEADER_SIZE )
|
||||
{
|
||||
Weird("SSLv2: SERVER-HELLO is too small!");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// Extract the data of the client hello header.
|
||||
SSLv2_ServerHelloHeader sh;
|
||||
sh.sessionIdHit = recordData[1];
|
||||
sh.certificateType = recordData[2];
|
||||
sh.serverVersion = uint16(recordData[3] << 8) | recordData[4];
|
||||
sh.certificateLength = uint16(recordData[5] << 8) | recordData[6];
|
||||
sh.cipherSpecLength = uint16(recordData[7] << 8) | recordData[8];
|
||||
sh.connectionIdLength = uint16(recordData[9] << 8) | recordData[10];
|
||||
|
||||
if ( sh.serverVersion != SSLProxy_Analyzer::SSLv20 )
|
||||
{
|
||||
Weird("SSLv2: Unsupported SSL-Version in SERVER-HELLO");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
if ( sh.certificateLength + sh.cipherSpecLength +
|
||||
sh.connectionIdLength +
|
||||
SSLv2_SERVER_HELLO_HEADER_SIZE != recordLength )
|
||||
{
|
||||
Weird("SSLv2: Size inconsistency in SERVER-HELLO");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// The length of the CONNECTION-ID must be between 16 and 32 bytes.
|
||||
if ( sh.connectionIdLength < 16 || sh.connectionIdLength > 32 )
|
||||
Weird("SSLv2: Nonconform CONNECTION-ID-LENGTH in SERVER-HELLO");
|
||||
|
||||
// If decrypting, store the connection ID.
|
||||
if ( ssl_store_key_material && sh.connectionIdLength <= 32 )
|
||||
{
|
||||
const u_char* ptr = recordData;
|
||||
|
||||
ptr += SSLv2_SERVER_HELLO_HEADER_SIZE + sh.cipherSpecLength +
|
||||
sh.certificateLength;
|
||||
|
||||
pConnectionId = new SSL_DataBlock(ptr, sh.connectionIdLength);
|
||||
}
|
||||
|
||||
if ( sh.sessionIdHit == 0 )
|
||||
{
|
||||
// Generating reusing-connection event.
|
||||
EventHandlerPtr event = ssl_session_insertion;
|
||||
|
||||
if ( event )
|
||||
{
|
||||
TableVal* sessionIDTable =
|
||||
MakeSessionID(
|
||||
recordData +
|
||||
SSLv2_SERVER_HELLO_HEADER_SIZE +
|
||||
sh.certificateLength +
|
||||
sh.cipherSpecLength,
|
||||
sh.connectionIdLength);
|
||||
|
||||
val_list* vl = new val_list;
|
||||
vl->append(proxy->BuildConnVal());
|
||||
vl->append(sessionIDTable);
|
||||
|
||||
proxy->ConnectionEvent(ssl_session_insertion, vl);
|
||||
}
|
||||
}
|
||||
|
||||
SSLv2_States nextState;
|
||||
|
||||
if ( sh.sessionIdHit != 0 )
|
||||
{ // we're using a cached session
|
||||
|
||||
// There should not be any pending data in the SSLv2
|
||||
// reassembler, because the server should wait for a
|
||||
// client response.
|
||||
if ( ((SSLv2_Endpoint*) s)->isDataPending() )
|
||||
{
|
||||
// But turns out some SSL Implementations do this
|
||||
// when using a cached session.
|
||||
}
|
||||
|
||||
// Consistency check for SESSION-ID-HIT.
|
||||
if ( ! bClientWantsCachedSession )
|
||||
Weird("SSLv2: SESSION-ID hit in SERVER-HELLO, but no SESSION-ID in CLIENT-HELLO!");
|
||||
|
||||
// If the SESSION-ID-HIT flag is non-zero then the
|
||||
// CERTIFICATE-TYPE, CERTIFICATE-LENGTH and
|
||||
// CIPHER-SPECS-LENGTH fields will be zero.
|
||||
if ( sh.certificateType != 0 || sh.certificateLength != 0 ||
|
||||
sh.cipherSpecLength != 0 )
|
||||
Weird("SSLv2: SESSION-ID-HIT, but session data in SERVER-HELLO");
|
||||
|
||||
// Generate reusing-connection event.
|
||||
if ( pSessionId )
|
||||
{
|
||||
fire_ssl_conn_reused(pSessionId);
|
||||
delete pSessionId;
|
||||
pSessionId = 0;
|
||||
}
|
||||
|
||||
nextState = CACHED_SESSION;
|
||||
}
|
||||
else
|
||||
{ // we're starting a new session
|
||||
|
||||
// There should not be any pending data in the SSLv2
|
||||
// reassembler, because the server should wait for
|
||||
// a client response.
|
||||
if ( ((SSLv2_Endpoint*) s)->isDataPending() )
|
||||
Weird("SSLv2: Pending data in SSL_RecordBuilder after SERVER-HELLO (new session)!");
|
||||
|
||||
// TODO: check certificate length ???
|
||||
if ( sh.certificateLength == 0 )
|
||||
Weird("SSLv2: No certificate in SERVER-HELLO!");
|
||||
|
||||
// The CIPHER-SPECS-LENGTH must be > zero and a multiple of 3.
|
||||
if ( sh.cipherSpecLength == 0 )
|
||||
Weird("SSLv2: No CIPHER-SPECS in SERVER-HELLO!");
|
||||
|
||||
if ( sh.cipherSpecLength % 3 != 0 )
|
||||
{
|
||||
Weird("SSLv2: Nonconform CIPHER-SPECS-LENGTH in SERVER-HELLO");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
const u_char* ptr = recordData;
|
||||
ptr += sh.certificateLength + SSLv2_SERVER_HELLO_HEADER_SIZE;
|
||||
currentCipherSuites = analyzeCiphers(s, sh.cipherSpecLength, ptr);
|
||||
|
||||
nextState = NEW_SESSION;
|
||||
}
|
||||
|
||||
// Check if at least one cipher is supported by the client.
|
||||
if ( pClientCipherSpecs && pServerCipherSpecs )
|
||||
{
|
||||
bool bFound = false;
|
||||
for ( int i = 0; i < pClientCipherSpecs->len; i += 3 )
|
||||
{
|
||||
for ( int j = 0; j < pServerCipherSpecs->len; j += 3 )
|
||||
{
|
||||
if ( memcmp(pClientCipherSpecs + i,
|
||||
pServerCipherSpecs + j, 3) == 0 )
|
||||
{
|
||||
bFound = true;
|
||||
i = pClientCipherSpecs->len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! bFound )
|
||||
{
|
||||
Weird("SSLv2: Client's and server's CIPHER-SPECS don't match!");
|
||||
nextState = ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
delete pClientCipherSpecs;
|
||||
pClientCipherSpecs = 0;
|
||||
}
|
||||
|
||||
// Certificate analysis.
|
||||
if ( sh.certificateLength > 0 && ssl_analyze_certificates != 0 )
|
||||
{
|
||||
analyzeCertificate(s, recordData + SSLv2_SERVER_HELLO_HEADER_SIZE,
|
||||
sh.certificateLength, sh.certificateType, false);
|
||||
}
|
||||
|
||||
if ( nextState == NEW_SESSION )
|
||||
// generate server-reply event
|
||||
fire_ssl_conn_server_reply(sh.serverVersion, currentCipherSuites);
|
||||
|
||||
else if ( nextState == CACHED_SESSION )
|
||||
{ // generate server-reply event
|
||||
fire_ssl_conn_server_reply(sh.serverVersion, currentCipherSuites);
|
||||
// Generate a connection-established event with a dummy
|
||||
// cipher suite, since we can't remember session information
|
||||
// (yet).
|
||||
// Note: A new session identifier is sent encrypted in SSLv2!
|
||||
fire_ssl_conn_established(sh.serverVersion, 0xABCD);
|
||||
}
|
||||
else
|
||||
// Unref, since the table is not delivered to any event.
|
||||
Unref(currentCipherSuites);
|
||||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method analyses a SSLv2 CLIENT-MASTER-KEY record.
|
||||
*
|
||||
* \param s Pointer to the endpoint which sent the record
|
||||
* \param length length of SSLv2 CLIENT-MASTER-KEY record
|
||||
* \param data pointer to SSLv2 CLIENT-MASTER-KEY record to analyze
|
||||
*
|
||||
* \return the updated state of the current ssl connection
|
||||
*/
|
||||
SSLv2_States SSLv2_Interpreter::
|
||||
ClientMasterKeyRecord(SSL_InterpreterEndpoint* s, int recordLength,
|
||||
const u_char* recordData)
|
||||
{
|
||||
++clientMasterKeyRecords;
|
||||
SSLv2_States nextState = CLIENT_MASTERKEY_SEEN;
|
||||
|
||||
if ( s != orig )
|
||||
Weird("SSLv2: CLIENT-MASTER-KEY from server!");
|
||||
|
||||
if ( recordLength < SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE )
|
||||
{
|
||||
Weird("SSLv2: CLIENT-MASTER-KEY is too small!");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// Extract the data of the client master key header.
|
||||
SSLv2_ClientMasterKeyHeader cmk;
|
||||
cmk.cipherKind =
|
||||
((recordData[1] << 16) | recordData[2] << 8) | recordData[3];
|
||||
cmk.clearKeyLength = uint16(recordData[4] << 8) | recordData[5];
|
||||
cmk.encryptedKeyLength = uint16(recordData[6] << 8) | recordData[7];
|
||||
cmk.keyArgLength = uint16(recordData[8] << 8) | recordData[9];
|
||||
|
||||
if ( cmk.clearKeyLength + cmk.encryptedKeyLength + cmk.keyArgLength +
|
||||
SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE != recordLength )
|
||||
{
|
||||
Weird("SSLv2: Size inconsistency in CLIENT-MASTER-KEY");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// Check if cipher is supported by the server.
|
||||
if ( pServerCipherSpecs )
|
||||
{
|
||||
bool bFound = false;
|
||||
for ( int i = 0; i < pServerCipherSpecs->len; i += 3 )
|
||||
{
|
||||
uint32 cipherSpec =
|
||||
((pServerCipherSpecs->data[i] << 16) |
|
||||
pServerCipherSpecs->data[i+1] << 8) |
|
||||
pServerCipherSpecs->data[i+2];
|
||||
|
||||
if ( cmk.cipherKind == cipherSpec )
|
||||
{
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! bFound )
|
||||
{
|
||||
Weird("SSLv2: Client chooses unadvertised cipher in CLIENT-MASTER-KEY!");
|
||||
nextState = ERROR_REQUIRED;
|
||||
}
|
||||
else
|
||||
nextState = CLIENT_MASTERKEY_SEEN;
|
||||
|
||||
delete pServerCipherSpecs;
|
||||
pServerCipherSpecs = 0;
|
||||
}
|
||||
|
||||
// TODO: check if cipher has been advertised before.
|
||||
|
||||
SSL_CipherSpec* pCipherSpecTemp = 0;
|
||||
|
||||
HashKey h(static_cast<bro_uint_t>(cmk.cipherKind));
|
||||
pCipherSpecTemp = (SSL_CipherSpec*) SSL_CipherSpecDict.Lookup(&h);
|
||||
if ( ! pCipherSpecTemp || ! (pCipherSpecTemp->flags & SSL_FLAG_SSLv20) )
|
||||
Weird("SSLv2: Unknown CIPHER-SPEC in CLIENT-MASTER-KEY!");
|
||||
else
|
||||
{ // check for conistency of clearKeyLength
|
||||
if ( cmk.clearKeyLength * 8 != pCipherSpecTemp->clearKeySize )
|
||||
{
|
||||
Weird("SSLv2: Inconsistency of clearKeyLength in CLIENT-MASTER-KEY!");
|
||||
// nextState = ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// TODO: check for consistency of encryptedKeyLength.
|
||||
// TODO: check for consistency of keyArgLength.
|
||||
// switch ( cmk.cipherKind )
|
||||
// {
|
||||
// case SSL_CK_RC4_128_WITH_MD5:
|
||||
// case SSL_CK_RC4_128_EXPORT40_WITH_MD5:
|
||||
// if ( cmk.keyArgLength != 0 )
|
||||
// {
|
||||
// Weird("SSLv2: Inconsistency of keyArgLength in CLIENT-MASTER-KEY!");
|
||||
// //nextState = ERROR_REQUIRED;
|
||||
// }
|
||||
// break;
|
||||
// case SSL_CK_DES_64_CBC_WITH_MD5:
|
||||
// case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:
|
||||
// case SSL_CK_RC2_128_CBC_WITH_MD5:
|
||||
// case SSL_CK_IDEA_128_CBC_WITH_MD5:
|
||||
// case SSL_CK_DES_192_EDE3_CBC_WITH_MD5:
|
||||
// if ( cmk.keyArgLength != 8 )
|
||||
// {
|
||||
// Weird("SSLv2: Inconsistency of keyArgLength in CLIENT-MASTER-KEY!");
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
|
||||
// Remember the used cipher spec.
|
||||
usedCipherSpec = SSLv2_CipherSpec(cmk.cipherKind);
|
||||
|
||||
// If decrypting, store the clear key part of the master key.
|
||||
if ( ssl_store_key_material /* && cmk.clearKeyLength == 11 */ )
|
||||
{
|
||||
pMasterClearKey =
|
||||
new SSL_DataBlock((recordData + SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE), cmk.clearKeyLength);
|
||||
|
||||
pMasterEncryptedKey =
|
||||
new SSL_DataBlock((recordData + SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE + cmk.clearKeyLength ), cmk.encryptedKeyLength);
|
||||
}
|
||||
|
||||
if ( nextState == CLIENT_MASTERKEY_SEEN )
|
||||
fire_ssl_conn_established(SSLProxy_Analyzer::SSLv20,
|
||||
cmk.cipherKind);
|
||||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* This method analyses a SSLv2 ERROR record.
|
||||
*
|
||||
* \param s Pointer to the endpoint which sent the record
|
||||
* \param length length of SSLv2 ERROR record
|
||||
* \param data pointer to SSLv2 ERROR record to analyze
|
||||
*
|
||||
* \return the updated state of the current ssl connection
|
||||
*/
|
||||
SSLv2_States SSLv2_Interpreter::ErrorRecord(SSL_InterpreterEndpoint* s,
|
||||
int recordLength, const u_char* recordData)
|
||||
{
|
||||
++errorRecords;
|
||||
|
||||
if ( unsigned(recordLength) != SSLv2_ERROR_RECORD_SIZE )
|
||||
{
|
||||
Weird("SSLv2: Size mismatch in Error Record!");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
SSLv2_ErrorRecord er;
|
||||
er.errorCode = (recordData[1] << 8) | recordData[2];
|
||||
SSL3x_AlertLevel al = SSL3x_AlertLevel(255);
|
||||
|
||||
switch ( er.errorCode ) {
|
||||
case SSLv2_PE_NO_CIPHER:
|
||||
// The client doesn't support a cipher which the server
|
||||
// supports. Only from client to server and not recoverable!
|
||||
al = SSL3x_ALERT_LEVEL_FATAL;
|
||||
break;
|
||||
|
||||
case SSLv2_PE_NO_CERTIFICATE:
|
||||
if ( s == orig )
|
||||
// from client to server: not recoverable
|
||||
al = SSL3x_ALERT_LEVEL_FATAL;
|
||||
else
|
||||
// from server to client: recoverable
|
||||
al = SSL3x_ALERT_LEVEL_WARNING;
|
||||
break;
|
||||
|
||||
case SSLv2_PE_BAD_CERTIFICATE:
|
||||
if ( s == orig )
|
||||
// from client to server: not recoverable
|
||||
al = SSL3x_ALERT_LEVEL_FATAL;
|
||||
else
|
||||
// from server to client: recoverable
|
||||
al = SSL3x_ALERT_LEVEL_WARNING;
|
||||
break;
|
||||
|
||||
case SSLv2_PE_UNSUPPORTED_CERTIFICATE_TYPE:
|
||||
if ( s == orig )
|
||||
// from client to server: not recoverable
|
||||
al = SSL3x_ALERT_LEVEL_FATAL;
|
||||
else
|
||||
// from server to client: recoverable
|
||||
al = SSL3x_ALERT_LEVEL_WARNING;
|
||||
break;
|
||||
|
||||
default:
|
||||
al = SSL3x_ALERT_LEVEL_FATAL;
|
||||
break;
|
||||
}
|
||||
|
||||
fire_ssl_conn_alert(SSLProxy_Analyzer::SSLv20, al, er.errorCode);
|
||||
|
||||
return ERROR_SEEN;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method analyses a set of SSLv2 cipher suites.
|
||||
*
|
||||
* \param s Pointer to the endpoint which sent the cipher suites
|
||||
* \param length length of cipher suites
|
||||
* \param data pointer to cipher suites to analyze
|
||||
*
|
||||
* \return a pointer to a Bro TableVal (of type cipher_suites_list) which contains
|
||||
* the cipher suites list of the current analyzed record
|
||||
*/
|
||||
TableVal* SSLv2_Interpreter::analyzeCiphers(SSL_InterpreterEndpoint* s,
|
||||
int length, const u_char* data)
|
||||
{
|
||||
if ( length > MAX_CIPHERSPEC_SIZE )
|
||||
{
|
||||
if ( s == orig )
|
||||
Weird("SSLv2: Client has CipherSpecs > MAX_CIPHERSPEC_SIZE");
|
||||
else
|
||||
Weird("SSLv2: Server has CipherSpecs > MAX_CIPHERSPEC_SIZE");
|
||||
}
|
||||
else
|
||||
{ // cipher specs are not too big
|
||||
if ( ssl_compare_cipherspecs )
|
||||
{ // store cipher specs for state analysis
|
||||
if ( s == resp )
|
||||
pServerCipherSpecs =
|
||||
new SSL_DataBlock(data, length);
|
||||
else
|
||||
pClientCipherSpecs =
|
||||
new SSL_DataBlock(data, length);
|
||||
}
|
||||
}
|
||||
|
||||
const u_char* pCipher = data;
|
||||
bool bExtractCipherSuite = false;
|
||||
TableVal* pCipherTable = 0;
|
||||
|
||||
// We only extract the cipher suite when the corresponding
|
||||
// ssl events are defined (otherwise we do work for nothing
|
||||
// and suffer a memory leak).
|
||||
// FIXME: This check needs to be done only once!
|
||||
if ( (s == orig && ssl_conn_attempt) ||
|
||||
(s == resp && ssl_conn_server_reply) )
|
||||
{
|
||||
pCipherTable = new TableVal(cipher_suites_list);
|
||||
bExtractCipherSuite = true;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < length; i += 3 )
|
||||
{
|
||||
SSL_CipherSpec* pCurrentCipherSpec;
|
||||
uint32 cipherSpecID =
|
||||
((pCipher[0] << 16) | pCipher[1] << 8) | pCipher[2];
|
||||
|
||||
// Check for unknown cipher specs.
|
||||
HashKey h(static_cast<bro_uint_t>(cipherSpecID));
|
||||
pCurrentCipherSpec =
|
||||
(SSL_CipherSpec*) SSL_CipherSpecDict.Lookup(&h);
|
||||
|
||||
if ( ! pCurrentCipherSpec )
|
||||
{
|
||||
if ( s == orig )
|
||||
Weird("SSLv2: Unknown CIPHER-SPEC in CLIENT-HELLO!");
|
||||
else
|
||||
Weird("SSLv2: Unknown CIPHER-SPEC in SERVER-HELLO!");
|
||||
}
|
||||
|
||||
if ( bExtractCipherSuite )
|
||||
{
|
||||
Val* index = new Val(cipherSpecID, TYPE_COUNT);
|
||||
pCipherTable->Assign(index, 0);
|
||||
Unref(index);
|
||||
}
|
||||
|
||||
pCipher += 3;
|
||||
}
|
||||
|
||||
return pCipherTable;
|
||||
}
|
||||
|
||||
// --- SSLv2_EndPoint ---------------------------------------------------------
|
||||
|
||||
/*!
|
||||
* The constructor.
|
||||
*
|
||||
* \param interpreter Pointer to the SSLv2 interpreter to whom this endpoint belongs to
|
||||
* \param is_orig true if this is the originating endpoint of the ssl connection,
|
||||
* false otherwise
|
||||
*/
|
||||
SSLv2_Endpoint::SSLv2_Endpoint(SSLv2_Interpreter* interpreter, int is_orig)
|
||||
: SSL_InterpreterEndpoint(interpreter, is_orig)
|
||||
{
|
||||
sentRecords = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* The destructor.
|
||||
*/
|
||||
SSLv2_Endpoint::~SSLv2_Endpoint()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method is called by the SSLProxy_Analyzer with a complete reassembled
|
||||
* SSLv2 record. It passes the record to SSLv2_Interpreter::NewSSLRecord().
|
||||
*
|
||||
* \param t <b>reserved</b> (always zero)
|
||||
* \param seq <b>reserved</b> (always zero)
|
||||
* \param len length of the data block containing the ssl record
|
||||
* \param data pointer to the data block containing the ssl record
|
||||
*/
|
||||
void SSLv2_Endpoint::Deliver(int len, const u_char* data)
|
||||
{
|
||||
++((SSLv2_Endpoint*)peer)->sentRecords;
|
||||
|
||||
((SSLv2_Interpreter*)interpreter)->NewSSLRecord(this, len, data);
|
||||
}
|
|
@ -15,6 +15,7 @@ public:
|
|||
pid_32bit = false;
|
||||
include_locations = true;
|
||||
new_cache_strategy = false;
|
||||
broccoli_peer = false;
|
||||
}
|
||||
|
||||
SerialInfo(const SerialInfo& info)
|
||||
|
@ -28,6 +29,7 @@ public:
|
|||
pid_32bit = info.pid_32bit;
|
||||
include_locations = info.include_locations;
|
||||
new_cache_strategy = info.new_cache_strategy;
|
||||
broccoli_peer = info.broccoli_peer;
|
||||
}
|
||||
|
||||
// Parameters that control serialization.
|
||||
|
@ -46,6 +48,11 @@ public:
|
|||
// If true, we support keeping objs in cache permanently.
|
||||
bool new_cache_strategy;
|
||||
|
||||
// If true, we're connecting to a Broccoli. If so, serialization
|
||||
// specifics may be adapted for functionality Broccoli does not
|
||||
// support.
|
||||
bool broccoli_peer;
|
||||
|
||||
ChunkedIO::Chunk* chunk; // chunk written right before the serialization
|
||||
|
||||
// Attributes set during serialization.
|
||||
|
@ -70,6 +77,7 @@ public:
|
|||
print = 0;
|
||||
pid_32bit = false;
|
||||
new_cache_strategy = false;
|
||||
broccoli_peer = false;
|
||||
}
|
||||
|
||||
UnserialInfo(const UnserialInfo& info)
|
||||
|
@ -86,6 +94,7 @@ public:
|
|||
print = info.print;
|
||||
pid_32bit = info.pid_32bit;
|
||||
new_cache_strategy = info.new_cache_strategy;
|
||||
broccoli_peer = info.broccoli_peer;
|
||||
}
|
||||
|
||||
// Parameters that control unserialization.
|
||||
|
@ -106,6 +115,11 @@ public:
|
|||
// If true, we support keeping objs in cache permanently.
|
||||
bool new_cache_strategy;
|
||||
|
||||
// If true, we're connecting to a Broccoli. If so, serialization
|
||||
// specifics may be adapted for functionality Broccoli does not
|
||||
// support.
|
||||
bool broccoli_peer;
|
||||
|
||||
// If a global ID already exits, of these policies is used.
|
||||
enum {
|
||||
Keep, // keep the old ID and ignore the new
|
||||
|
|
|
@ -121,7 +121,7 @@ protected:
|
|||
|
||||
// This will be increased whenever there is an incompatible change
|
||||
// in the data format.
|
||||
static const uint32 DATA_FORMAT_VERSION = 20;
|
||||
static const uint32 DATA_FORMAT_VERSION = 21;
|
||||
|
||||
ChunkedIO* io;
|
||||
|
||||
|
|
154
src/Type.cc
154
src/Type.cc
|
@ -876,74 +876,12 @@ void CommentedTypeDecl::DescribeReST(ODesc* d) const
|
|||
}
|
||||
}
|
||||
|
||||
RecordField::RecordField(int arg_base, int arg_offset, int arg_total_offset)
|
||||
{
|
||||
base = arg_base;
|
||||
offset = arg_offset;
|
||||
total_offset = arg_total_offset;
|
||||
}
|
||||
|
||||
RecordType::RecordType(type_decl_list* arg_types) : BroType(TYPE_RECORD)
|
||||
{
|
||||
types = arg_types;
|
||||
base = 0;
|
||||
fields = 0;
|
||||
num_fields = types ? types->length() : 0;
|
||||
}
|
||||
|
||||
RecordType::RecordType(TypeList* arg_base, type_decl_list* refinements)
|
||||
: BroType(TYPE_RECORD)
|
||||
{
|
||||
if ( refinements )
|
||||
arg_base->Append(new RecordType(refinements));
|
||||
|
||||
Init(arg_base);
|
||||
}
|
||||
|
||||
void RecordType::Init(TypeList* arg_base)
|
||||
{
|
||||
assert(false); // Is this ever used?
|
||||
|
||||
base = arg_base;
|
||||
|
||||
if ( ! base )
|
||||
Internal("empty RecordType");
|
||||
|
||||
fields = new PDict(RecordField)(ORDERED);
|
||||
types = 0;
|
||||
|
||||
type_list* t = base->Types();
|
||||
|
||||
loop_over_list(*t, i)
|
||||
{
|
||||
BroType* ti = (*t)[i];
|
||||
|
||||
if ( ti->Tag() != TYPE_RECORD )
|
||||
(*t)[i]->Error("non-record in base type list");
|
||||
|
||||
RecordType* rti = ti->AsRecordType();
|
||||
int n = rti->NumFields();
|
||||
|
||||
for ( int j = 0; j < n; ++j )
|
||||
{
|
||||
const TypeDecl* tdij = rti->FieldDecl(j);
|
||||
|
||||
if ( fields->Lookup(tdij->id) )
|
||||
{
|
||||
reporter->Error("duplicate field %s", tdij->id);
|
||||
continue;
|
||||
}
|
||||
|
||||
RecordField* rf = new RecordField(i, j, fields->Length());
|
||||
|
||||
if ( fields->Insert(tdij->id, rf) )
|
||||
Internal("duplicate field when constructing record");
|
||||
}
|
||||
}
|
||||
|
||||
num_fields = fields->Length();
|
||||
}
|
||||
|
||||
RecordType::~RecordType()
|
||||
{
|
||||
if ( types )
|
||||
|
@ -953,9 +891,6 @@ RecordType::~RecordType()
|
|||
|
||||
delete types;
|
||||
}
|
||||
|
||||
delete fields;
|
||||
Unref(base);
|
||||
}
|
||||
|
||||
int RecordType::HasField(const char* field) const
|
||||
|
@ -971,17 +906,7 @@ BroType* RecordType::FieldType(const char* field) const
|
|||
|
||||
BroType* RecordType::FieldType(int field) const
|
||||
{
|
||||
if ( types )
|
||||
return (*types)[field]->type;
|
||||
else
|
||||
{
|
||||
RecordField* rf = fields->NthEntry(field);
|
||||
if ( ! rf )
|
||||
Internal("missing field in RecordType::FieldType");
|
||||
BroType* bt = (*base->Types())[rf->base];
|
||||
RecordType* rbt = bt->AsRecordType();
|
||||
return rbt->FieldType(rf->offset);
|
||||
}
|
||||
return (*types)[field]->type;
|
||||
}
|
||||
|
||||
Val* RecordType::FieldDefault(int field) const
|
||||
|
@ -998,26 +923,14 @@ Val* RecordType::FieldDefault(int field) const
|
|||
|
||||
int RecordType::FieldOffset(const char* field) const
|
||||
{
|
||||
if ( types )
|
||||
loop_over_list(*types, i)
|
||||
{
|
||||
loop_over_list(*types, i)
|
||||
{
|
||||
TypeDecl* td = (*types)[i];
|
||||
if ( streq(td->id, field) )
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
TypeDecl* td = (*types)[i];
|
||||
if ( streq(td->id, field) )
|
||||
return i;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
RecordField* rf = fields->Lookup(field);
|
||||
if ( ! rf )
|
||||
return -1;
|
||||
else
|
||||
return rf->total_offset;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* RecordType::FieldName(int field) const
|
||||
|
@ -1027,33 +940,12 @@ const char* RecordType::FieldName(int field) const
|
|||
|
||||
const TypeDecl* RecordType::FieldDecl(int field) const
|
||||
{
|
||||
if ( types )
|
||||
return (*types)[field];
|
||||
else
|
||||
{
|
||||
RecordField* rf = fields->NthEntry(field);
|
||||
if ( ! rf )
|
||||
reporter->InternalError("missing field in RecordType::FieldDecl");
|
||||
|
||||
BroType* bt = (*base->Types())[rf->base];
|
||||
RecordType* rbt = bt->AsRecordType();
|
||||
return rbt->FieldDecl(rf->offset);
|
||||
}
|
||||
return (*types)[field];
|
||||
}
|
||||
|
||||
TypeDecl* RecordType::FieldDecl(int field)
|
||||
{
|
||||
if ( types )
|
||||
return (*types)[field];
|
||||
else
|
||||
{
|
||||
RecordField* rf = fields->NthEntry(field);
|
||||
if ( ! rf )
|
||||
Internal("missing field in RecordType::FieldDecl");
|
||||
BroType* bt = (*base->Types())[rf->base];
|
||||
RecordType* rbt = bt->AsRecordType();
|
||||
return rbt->FieldDecl(rf->offset);
|
||||
}
|
||||
return (*types)[field];
|
||||
}
|
||||
|
||||
void RecordType::Describe(ODesc* d) const
|
||||
|
@ -1151,11 +1043,6 @@ void RecordType::DescribeFields(ODesc* d) const
|
|||
d->SP();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
d->AddCount(1);
|
||||
base->Describe(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1208,9 +1095,6 @@ bool RecordType::DoSerialize(SerialInfo* info) const
|
|||
else if ( ! SERIALIZE(false) )
|
||||
return false;
|
||||
|
||||
SERIALIZE_OPTIONAL(base);
|
||||
|
||||
// We don't serialize the fields as we can reconstruct them.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1245,13 +1129,6 @@ bool RecordType::DoUnserialize(UnserialInfo* info)
|
|||
else
|
||||
types = 0;
|
||||
|
||||
BroType* type;
|
||||
UNSERIALIZE_OPTIONAL(type, BroType::Unserialize(info, TYPE_LIST));
|
||||
base = (TypeList*) type;
|
||||
|
||||
if ( base )
|
||||
Init(base);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1594,21 +1471,6 @@ bool VectorType::DoUnserialize(UnserialInfo* info)
|
|||
return yield_type != 0;
|
||||
}
|
||||
|
||||
BroType* refine_type(TypeList* base, type_decl_list* refinements)
|
||||
{
|
||||
type_list* t = base->Types();
|
||||
|
||||
if ( t->length() == 1 && ! refinements )
|
||||
{ // Just a direct reference to a single type.
|
||||
BroType* rt = (*t)[0]->Ref();
|
||||
Unref(base);
|
||||
return rt;
|
||||
}
|
||||
|
||||
return new RecordType(base, refinements);
|
||||
}
|
||||
|
||||
|
||||
BroType* base_type(TypeTag tag)
|
||||
{
|
||||
static BroType* base_types[NUM_TYPES];
|
||||
|
|
21
src/Type.h
21
src/Type.h
|
@ -426,20 +426,9 @@ public:
|
|||
std::list<std::string>* comments;
|
||||
};
|
||||
|
||||
class RecordField {
|
||||
public:
|
||||
RecordField(int arg_base, int arg_offset, int arg_total_offset);
|
||||
|
||||
int base; // which base element it belongs to
|
||||
int offset; // where it is in that base
|
||||
int total_offset; // where it is in the aggregate record
|
||||
};
|
||||
declare(PDict,RecordField);
|
||||
|
||||
class RecordType : public BroType {
|
||||
public:
|
||||
RecordType(type_decl_list* types);
|
||||
RecordType(TypeList* base, type_decl_list* refinements);
|
||||
|
||||
~RecordType();
|
||||
|
||||
|
@ -473,15 +462,11 @@ public:
|
|||
void DescribeFieldsReST(ODesc* d, bool func_args) const;
|
||||
|
||||
protected:
|
||||
RecordType() { fields = 0; base = 0; types = 0; }
|
||||
|
||||
void Init(TypeList* arg_base);
|
||||
RecordType() { types = 0; }
|
||||
|
||||
DECLARE_SERIAL(RecordType)
|
||||
|
||||
int num_fields;
|
||||
PDict(RecordField)* fields;
|
||||
TypeList* base;
|
||||
type_decl_list* types;
|
||||
};
|
||||
|
||||
|
@ -587,10 +572,6 @@ protected:
|
|||
BroType* yield_type;
|
||||
};
|
||||
|
||||
|
||||
// Returns the given type refinement, or error_type() if it's illegal.
|
||||
extern BroType* refine_type(TypeList* base, type_decl_list* refinements);
|
||||
|
||||
// Returns the BRO basic (non-parameterized) type with the given type.
|
||||
extern BroType* base_type(TypeTag tag);
|
||||
|
||||
|
|
6300
src/bro.bif
6300
src/bro.bif
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,6 @@
|
|||
# Documentation and default values for these are located in policy/bro.init.
|
||||
##! Declaration of various scripting-layer constants that the Bro core uses
|
||||
##! internally. Documentation and default values for the scripting-layer
|
||||
##! variables themselves are found in :doc:`/scripts/base/init-bare`.
|
||||
|
||||
const ignore_keep_alive_rexmit: bool;
|
||||
const skip_http_data: bool;
|
||||
|
|
6086
src/event.bif
6086
src/event.bif
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
|||
# Internal functions and types used by the logging framework.
|
||||
##! Internal functions and types used by the logging framework.
|
||||
|
||||
module Log;
|
||||
|
||||
|
|
12
src/main.cc
12
src/main.cc
|
@ -48,6 +48,7 @@ extern "C" void OPENSSL_add_all_algorithms_conf(void);
|
|||
#include "DPM.h"
|
||||
#include "BroDoc.h"
|
||||
#include "Brofiler.h"
|
||||
#include "LogWriterAscii.h"
|
||||
|
||||
#include "binpac_bro.h"
|
||||
|
||||
|
@ -99,7 +100,7 @@ extern char version[];
|
|||
char* command_line_policy = 0;
|
||||
vector<string> params;
|
||||
char* proc_status_file = 0;
|
||||
int snaplen = 65535; // really want "capture entire packet"
|
||||
int snaplen = 0; // this gets set from the scripting-layer's value
|
||||
|
||||
int FLAGS_use_binpac = false;
|
||||
|
||||
|
@ -147,7 +148,6 @@ void usage()
|
|||
fprintf(stderr, " -g|--dump-config | dump current config into .state dir\n");
|
||||
fprintf(stderr, " -h|--help|-? | command line help\n");
|
||||
fprintf(stderr, " -i|--iface <interface> | read from given interface\n");
|
||||
fprintf(stderr, " -l|--snaplen <snaplen> | number of bytes per packet to capture from interfaces (default 65535)\n");
|
||||
fprintf(stderr, " -p|--prefix <prefix> | add given prefix to policy file resolution\n");
|
||||
fprintf(stderr, " -r|--readfile <readfile> | read from given tcpdump file\n");
|
||||
fprintf(stderr, " -y|--flowfile <file>[=<ident>] | read from given flow file\n");
|
||||
|
@ -197,6 +197,7 @@ void usage()
|
|||
fprintf(stderr, " $BRO_PREFIXES | prefix list (%s)\n", bro_prefixes());
|
||||
fprintf(stderr, " $BRO_DNS_FAKE | disable DNS lookups (%s)\n", bro_dns_fake());
|
||||
fprintf(stderr, " $BRO_SEED_FILE | file to load seeds from (not set)\n");
|
||||
fprintf(stderr, " $BRO_LOG_SUFFIX | ASCII log file extension (.%s)\n", LogWriterAscii::LogExt().c_str());
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
@ -376,7 +377,6 @@ int main(int argc, char** argv)
|
|||
{"filter", required_argument, 0, 'f'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"iface", required_argument, 0, 'i'},
|
||||
{"snaplen", required_argument, 0, 'l'},
|
||||
{"doc-scripts", no_argument, 0, 'Z'},
|
||||
{"prefix", required_argument, 0, 'p'},
|
||||
{"readfile", required_argument, 0, 'r'},
|
||||
|
@ -485,10 +485,6 @@ int main(int argc, char** argv)
|
|||
interfaces.append(optarg);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
snaplen = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
prefixes.append(optarg);
|
||||
break;
|
||||
|
@ -837,6 +833,8 @@ int main(int argc, char** argv)
|
|||
}
|
||||
}
|
||||
|
||||
snaplen = internal_val("snaplen")->AsCount();
|
||||
|
||||
// Initialize the secondary path, if it's needed.
|
||||
secondary_path = new SecondaryPath();
|
||||
|
||||
|
|
15
src/parse.y
15
src/parse.y
|
@ -2,7 +2,7 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
%}
|
||||
|
||||
%expect 88
|
||||
%expect 87
|
||||
|
||||
%token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY
|
||||
%token TOK_ATENDIF TOK_ATELSE TOK_ATIF TOK_ATIFDEF TOK_ATIFNDEF
|
||||
|
@ -53,7 +53,7 @@
|
|||
%type <expr> expr init anonymous_function
|
||||
%type <event_expr> event
|
||||
%type <stmt> stmt stmt_list func_body for_head
|
||||
%type <type> type opt_type refined_type enum_body
|
||||
%type <type> type opt_type enum_body
|
||||
%type <func_type> func_hdr func_params
|
||||
%type <type_l> type_list
|
||||
%type <type_decl> type_decl formal_args_decl
|
||||
|
@ -1106,7 +1106,7 @@ decl:
|
|||
}
|
||||
}
|
||||
|
||||
| TOK_TYPE global_id ':' refined_type opt_attr ';'
|
||||
| TOK_TYPE global_id ':' type opt_attr ';'
|
||||
{
|
||||
add_type($2, $4, $5, 0);
|
||||
|
||||
|
@ -1136,7 +1136,7 @@ decl:
|
|||
}
|
||||
}
|
||||
|
||||
| TOK_EVENT event_id ':' refined_type opt_attr ';'
|
||||
| TOK_EVENT event_id ':' type_list opt_attr ';'
|
||||
{
|
||||
add_type($2, $4, $5, 1);
|
||||
|
||||
|
@ -1222,13 +1222,6 @@ func_params:
|
|||
{ $$ = new FuncType($2, base_type(TYPE_VOID), 0); }
|
||||
;
|
||||
|
||||
refined_type:
|
||||
type_list '{' type_decl_list '}'
|
||||
{ $$ = refine_type($1, $3); }
|
||||
| type_list
|
||||
{ $$ = refine_type($1, 0); }
|
||||
;
|
||||
|
||||
opt_type:
|
||||
':' type
|
||||
{ $$ = $2; }
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
##! The reporter built-in functions allow for the scripting layer to
|
||||
##! generate messages of varying severity. If no event handlers
|
||||
##! exist for reporter messages, the messages are output to stderr.
|
||||
##! If event handlers do exist, it's assumed they take care of determining
|
||||
##! how/where to output the messages.
|
||||
##!
|
||||
##! See :doc:`/scripts/base/frameworks/reporter/main` for a convenient
|
||||
##! reporter message logging framework.
|
||||
|
||||
module Reporter;
|
||||
|
||||
|
@ -5,6 +13,13 @@ module Reporter;
|
|||
#include "NetVar.h"
|
||||
%%}
|
||||
|
||||
## Generates an informational message.
|
||||
##
|
||||
## msg: The informational message to report.
|
||||
##
|
||||
## Returns: Always true.
|
||||
##
|
||||
## .. bro:see:: reporter_info
|
||||
function Reporter::info%(msg: string%): bool
|
||||
%{
|
||||
reporter->PushLocation(frame->GetCall()->GetLocationInfo());
|
||||
|
@ -13,6 +28,13 @@ function Reporter::info%(msg: string%): bool
|
|||
return new Val(1, TYPE_BOOL);
|
||||
%}
|
||||
|
||||
## Generates a message that warns of a potential problem.
|
||||
##
|
||||
## msg: The warning message to report.
|
||||
##
|
||||
## Returns: Always true.
|
||||
##
|
||||
## .. bro:see:: reporter_warning
|
||||
function Reporter::warning%(msg: string%): bool
|
||||
%{
|
||||
reporter->PushLocation(frame->GetCall()->GetLocationInfo());
|
||||
|
@ -21,6 +43,14 @@ function Reporter::warning%(msg: string%): bool
|
|||
return new Val(1, TYPE_BOOL);
|
||||
%}
|
||||
|
||||
## Generates a non-fatal error indicative of a definite problem that should
|
||||
## be addressed. Program execution does not terminate.
|
||||
##
|
||||
## msg: The error message to report.
|
||||
##
|
||||
## Returns: Always true.
|
||||
##
|
||||
## .. bro:see:: reporter_error
|
||||
function Reporter::error%(msg: string%): bool
|
||||
%{
|
||||
reporter->PushLocation(frame->GetCall()->GetLocationInfo());
|
||||
|
@ -29,6 +59,11 @@ function Reporter::error%(msg: string%): bool
|
|||
return new Val(1, TYPE_BOOL);
|
||||
%}
|
||||
|
||||
## Generates a fatal error on stderr and terminates program execution.
|
||||
##
|
||||
## msg: The error message to report.
|
||||
##
|
||||
## Returns: Always true.
|
||||
function Reporter::fatal%(msg: string%): bool
|
||||
%{
|
||||
reporter->PushLocation(frame->GetCall()->GetLocationInfo());
|
||||
|
|
|
@ -167,7 +167,7 @@ ESCSEQ (\\([^\n]|[0-7]+|x[[:xdigit:]]+))
|
|||
return TOK_POST_DOC;
|
||||
}
|
||||
|
||||
<DOC>##{OWS}{ID}:.* {
|
||||
<DOC>##{OWS}{ID}:{WS}.* {
|
||||
const char* id_start = skip_whitespace(yytext + 2);
|
||||
yylval.str = copy_string(canon_doc_func_param(id_start).c_str());
|
||||
return TOK_DOC;
|
||||
|
@ -181,7 +181,7 @@ ESCSEQ (\\([^\n]|[0-7]+|x[[:xdigit:]]+))
|
|||
}
|
||||
}
|
||||
|
||||
##{OWS}{ID}:.* {
|
||||
##{OWS}{ID}:{WS}.* {
|
||||
if ( generate_documentation )
|
||||
{
|
||||
// Comment is documenting either a function parameter or return type,
|
||||
|
@ -201,6 +201,11 @@ ESCSEQ (\\([^\n]|[0-7]+|x[[:xdigit:]]+))
|
|||
}
|
||||
}
|
||||
|
||||
##<.* {
|
||||
if ( generate_documentation && BroDocObj::last )
|
||||
BroDocObj::last->AddDocString(canon_doc_comment(yytext + 3));
|
||||
}
|
||||
|
||||
##.* {
|
||||
if ( generate_documentation && (yytext[2] != '#') )
|
||||
{
|
||||
|
|
|
@ -22,11 +22,17 @@
|
|||
}
|
||||
};
|
||||
|
||||
string orig_label(bool is_orig);
|
||||
void free_X509(void *);
|
||||
X509* d2i_X509_binpac(X509** px, const uint8** in, int len);
|
||||
%}
|
||||
|
||||
%code{
|
||||
string orig_label(bool is_orig)
|
||||
{
|
||||
return string(is_orig ? "originator" :"responder");
|
||||
}
|
||||
|
||||
void free_X509(void* cert)
|
||||
{
|
||||
X509_free((X509*) cert);
|
||||
|
@ -117,14 +123,14 @@ refine connection SSL_Conn += {
|
|||
function proc_alert(rec: SSLRecord, level : int, desc : int) : bool
|
||||
%{
|
||||
BifEvent::generate_ssl_alert(bro_analyzer(), bro_analyzer()->Conn(),
|
||||
level, desc);
|
||||
${rec.is_orig}, level, desc);
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_client_hello(rec: SSLRecord,
|
||||
version : uint16, ts : double,
|
||||
session_id : uint8[],
|
||||
cipher_suites16 : uint16[],
|
||||
cipher_suites16 : uint16[],
|
||||
cipher_suites24 : uint24[]) : bool
|
||||
%{
|
||||
if ( state_ == STATE_TRACK_LOST )
|
||||
|
@ -138,7 +144,7 @@ refine connection SSL_Conn += {
|
|||
if ( ssl_client_hello )
|
||||
{
|
||||
vector<int>* cipher_suites = new vector<int>();
|
||||
if ( cipher_suites16 )
|
||||
if ( cipher_suites16 )
|
||||
std::copy(cipher_suites16->begin(), cipher_suites16->end(), std::back_inserter(*cipher_suites));
|
||||
else
|
||||
std::transform(cipher_suites24->begin(), cipher_suites24->end(), std::back_inserter(*cipher_suites), to_int());
|
||||
|
@ -150,15 +156,15 @@ refine connection SSL_Conn += {
|
|||
cipher_set->Assign(ciph, 0);
|
||||
Unref(ciph);
|
||||
}
|
||||
|
||||
|
||||
BifEvent::generate_ssl_client_hello(bro_analyzer(), bro_analyzer()->Conn(),
|
||||
version, ts,
|
||||
to_string_val(session_id),
|
||||
cipher_set);
|
||||
|
||||
|
||||
delete cipher_suites;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
@ -187,24 +193,36 @@ refine connection SSL_Conn += {
|
|||
std::copy(cipher_suites16->begin(), cipher_suites16->end(), std::back_inserter(*ciphers));
|
||||
else
|
||||
std::transform(cipher_suites24->begin(), cipher_suites24->end(), std::back_inserter(*ciphers), to_int());
|
||||
|
||||
|
||||
BifEvent::generate_ssl_server_hello(bro_analyzer(),
|
||||
bro_analyzer()->Conn(),
|
||||
version, ts,
|
||||
to_string_val(session_id),
|
||||
ciphers->size()==0 ? 0 : ciphers->at(0), comp_method);
|
||||
|
||||
|
||||
delete ciphers;
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_session_ticket_handshake(rec: SessionTicketHandshake, is_orig: bool): bool
|
||||
%{
|
||||
if ( ssl_session_ticket_handshake )
|
||||
{
|
||||
BifEvent::generate_ssl_session_ticket_handshake(bro_analyzer(),
|
||||
bro_analyzer()->Conn(),
|
||||
${rec.ticket_lifetime_hint},
|
||||
new StringVal(${rec.data}.length(), (const char*) ${rec.data}.data()));
|
||||
}
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_ssl_extension(type: int, data: bytestring) : bool
|
||||
function proc_ssl_extension(rec: SSLRecord, type: int, data: bytestring) : bool
|
||||
%{
|
||||
if ( ssl_extension )
|
||||
BifEvent::generate_ssl_extension(bro_analyzer(),
|
||||
bro_analyzer()->Conn(), type,
|
||||
bro_analyzer()->Conn(), ${rec.is_orig}, type,
|
||||
new StringVal(data.length(), (const char*) data.data()));
|
||||
return true;
|
||||
%}
|
||||
|
@ -222,7 +240,7 @@ refine connection SSL_Conn += {
|
|||
if ( x509_certificate )
|
||||
{
|
||||
STACK_OF(X509)* untrusted_certs = 0;
|
||||
|
||||
|
||||
for ( unsigned int i = 0; i < certificates->size(); ++i )
|
||||
{
|
||||
const bytestring& cert = (*certificates)[i];
|
||||
|
@ -231,7 +249,7 @@ refine connection SSL_Conn += {
|
|||
if ( ! pTemp )
|
||||
{
|
||||
BifEvent::generate_x509_error(bro_analyzer(), bro_analyzer()->Conn(),
|
||||
ERR_get_error());
|
||||
${rec.is_orig}, ERR_get_error());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -257,19 +275,20 @@ refine connection SSL_Conn += {
|
|||
StringVal* der_cert = new StringVal(cert.length(), (const char*) cert.data());
|
||||
|
||||
BifEvent::generate_x509_certificate(bro_analyzer(), bro_analyzer()->Conn(),
|
||||
${rec.is_orig},
|
||||
pX509Cert,
|
||||
! ${rec.is_orig},
|
||||
i, certificates->size(),
|
||||
der_cert);
|
||||
|
||||
// Are there any X509 extensions?
|
||||
//printf("Number of x509 extensions: %d\n", X509_get_ext_count(pTemp));
|
||||
if ( x509_extension && X509_get_ext_count(pTemp) > 0 )
|
||||
{
|
||||
int num_ext = X509_get_ext_count(pTemp);
|
||||
for ( int k = 0; k < num_ext; ++k )
|
||||
{
|
||||
unsigned char *pBuffer = 0;
|
||||
int length = 0;
|
||||
uint length = 0;
|
||||
|
||||
X509_EXTENSION* ex = X509_get_ext(pTemp, k);
|
||||
if (ex)
|
||||
|
@ -277,14 +296,14 @@ refine connection SSL_Conn += {
|
|||
ASN1_STRING *pString = X509_EXTENSION_get_data(ex);
|
||||
length = ASN1_STRING_to_UTF8(&pBuffer, pString);
|
||||
//i2t_ASN1_OBJECT(&pBuffer, length, obj)
|
||||
|
||||
// printf("extension length: %u\n", length);
|
||||
// -1 indicates an error.
|
||||
if ( length < 0 )
|
||||
continue;
|
||||
|
||||
StringVal* value = new StringVal(length, (char*)pBuffer);
|
||||
BifEvent::generate_x509_extension(bro_analyzer(),
|
||||
bro_analyzer()->Conn(), value);
|
||||
bro_analyzer()->Conn(), ${rec.is_orig}, value);
|
||||
OPENSSL_free(pBuffer);
|
||||
}
|
||||
}
|
||||
|
@ -436,6 +455,10 @@ refine typeattr Handshake += &let {
|
|||
proc : bool = $context.connection.proc_handshake(this, rec.is_orig);
|
||||
};
|
||||
|
||||
refine typeattr SessionTicketHandshake += &let {
|
||||
proc : bool = $context.connection.proc_session_ticket_handshake(this, rec.is_orig);
|
||||
}
|
||||
|
||||
refine typeattr UnknownRecord += &let {
|
||||
proc : bool = $context.connection.proc_unknown_record(rec);
|
||||
};
|
||||
|
@ -445,5 +468,5 @@ refine typeattr CiphertextRecord += &let {
|
|||
}
|
||||
|
||||
refine typeattr SSLExtension += &let {
|
||||
proc : bool = $context.connection.proc_ssl_extension(type, data);
|
||||
proc : bool = $context.connection.proc_ssl_extension(rec, type, data);
|
||||
};
|
||||
|
|
|
@ -22,7 +22,6 @@ type uint24 = record {
|
|||
};
|
||||
|
||||
string state_label(int state_nr);
|
||||
string orig_label(bool is_orig);
|
||||
double get_time_from_asn1(const ASN1_TIME * atime);
|
||||
string handshake_type_label(int type);
|
||||
%}
|
||||
|
@ -35,7 +34,7 @@ type SSLRecord(is_orig: bool) = record {
|
|||
head2 : uint8;
|
||||
head3 : uint8;
|
||||
head4 : uint8;
|
||||
rec : RecordText(this, is_orig)[] &length=length, &requires(content_type);
|
||||
rec : RecordText(this)[] &length=length, &requires(content_type);
|
||||
} &length = length+5, &byteorder=bigendian,
|
||||
&let {
|
||||
version : int =
|
||||
|
@ -54,25 +53,18 @@ type SSLRecord(is_orig: bool) = record {
|
|||
};
|
||||
};
|
||||
|
||||
type RecordText(rec: SSLRecord, is_orig: bool) = case $context.connection.state() of {
|
||||
type RecordText(rec: SSLRecord) = case $context.connection.state() of {
|
||||
STATE_ABBREV_SERVER_ENCRYPTED, STATE_CLIENT_ENCRYPTED,
|
||||
STATE_COMM_ENCRYPTED, STATE_CONN_ESTABLISHED
|
||||
-> ciphertext : CiphertextRecord(rec, is_orig);
|
||||
-> ciphertext : CiphertextRecord(rec);
|
||||
default
|
||||
-> plaintext : PlaintextRecord(rec, is_orig);
|
||||
-> plaintext : PlaintextRecord(rec);
|
||||
};
|
||||
|
||||
type PossibleEncryptedHandshake(rec: SSLRecord, is_orig: bool) = case $context.connection.state() of {
|
||||
# Deal with encrypted handshakes before the server cipher spec change.
|
||||
STATE_CLIENT_FINISHED, STATE_CLIENT_ENCRYPTED
|
||||
-> ct : CiphertextRecord(rec, is_orig);
|
||||
default -> hs : Handshake(rec);
|
||||
};
|
||||
|
||||
type PlaintextRecord(rec: SSLRecord, is_orig: bool) = case rec.content_type of {
|
||||
type PlaintextRecord(rec: SSLRecord) = case rec.content_type of {
|
||||
CHANGE_CIPHER_SPEC -> ch_cipher : ChangeCipherSpec(rec);
|
||||
ALERT -> alert : Alert(rec);
|
||||
HANDSHAKE -> handshake : PossibleEncryptedHandshake(rec, is_orig);
|
||||
HANDSHAKE -> handshake : Handshake(rec);
|
||||
APPLICATION_DATA -> app_data : ApplicationData(rec);
|
||||
V2_ERROR -> v2_error : V2Error(rec);
|
||||
V2_CLIENT_HELLO -> v2_client_hello : V2ClientHello(rec);
|
||||
|
@ -81,7 +73,7 @@ type PlaintextRecord(rec: SSLRecord, is_orig: bool) = case rec.content_type of {
|
|||
default -> unknown_record : UnknownRecord(rec);
|
||||
};
|
||||
|
||||
type SSLExtension = record {
|
||||
type SSLExtension(rec: SSLRecord) = record {
|
||||
type: uint16;
|
||||
data_len: uint16;
|
||||
data: bytestring &length=data_len;
|
||||
|
@ -156,10 +148,6 @@ enum AnalyzerState {
|
|||
}
|
||||
}
|
||||
|
||||
string orig_label(bool is_orig)
|
||||
{
|
||||
return string(is_orig ? "originator" :"responder");
|
||||
}
|
||||
|
||||
double get_time_from_asn1(const ASN1_TIME * atime)
|
||||
{
|
||||
|
@ -265,18 +253,19 @@ enum AnalyzerState {
|
|||
######################################################################
|
||||
|
||||
enum HandshakeType {
|
||||
HELLO_REQUEST = 0,
|
||||
CLIENT_HELLO = 1,
|
||||
SERVER_HELLO = 2,
|
||||
CERTIFICATE = 11,
|
||||
SERVER_KEY_EXCHANGE = 12,
|
||||
CERTIFICATE_REQUEST = 13,
|
||||
SERVER_HELLO_DONE = 14,
|
||||
CERTIFICATE_VERIFY = 15,
|
||||
CLIENT_KEY_EXCHANGE = 16,
|
||||
FINISHED = 20,
|
||||
CERTIFICATE_URL = 21, # RFC 3546
|
||||
CERTIFICATE_STATUS = 22, # RFC 3546
|
||||
HELLO_REQUEST = 0,
|
||||
CLIENT_HELLO = 1,
|
||||
SERVER_HELLO = 2,
|
||||
SESSION_TICKET = 4, # RFC 5077
|
||||
CERTIFICATE = 11,
|
||||
SERVER_KEY_EXCHANGE = 12,
|
||||
CERTIFICATE_REQUEST = 13,
|
||||
SERVER_HELLO_DONE = 14,
|
||||
CERTIFICATE_VERIFY = 15,
|
||||
CLIENT_KEY_EXCHANGE = 16,
|
||||
FINISHED = 20,
|
||||
CERTIFICATE_URL = 21, # RFC 3546
|
||||
CERTIFICATE_STATUS = 22, # RFC 3546
|
||||
};
|
||||
|
||||
%code{
|
||||
|
@ -286,6 +275,7 @@ enum HandshakeType {
|
|||
case HELLO_REQUEST: return string("HELLO_REQUEST");
|
||||
case CLIENT_HELLO: return string("CLIENT_HELLO");
|
||||
case SERVER_HELLO: return string("SERVER_HELLO");
|
||||
case SESSION_TICKET: return string("SESSION_TICKET");
|
||||
case CERTIFICATE: return string("CERTIFICATE");
|
||||
case SERVER_KEY_EXCHANGE: return string("SERVER_KEY_EXCHANGE");
|
||||
case CERTIFICATE_REQUEST: return string("CERTIFICATE_REQUEST");
|
||||
|
@ -389,7 +379,7 @@ type ClientHello(rec: SSLRecord) = record {
|
|||
# This weirdness is to deal with the possible existence or absence
|
||||
# of the following fields.
|
||||
ext_len: uint16[] &until($element == 0 || $element != 0);
|
||||
extensions : SSLExtension[] &until($input.length() == 0);
|
||||
extensions : SSLExtension(rec)[] &until($input.length() == 0);
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.connection.transition(STATE_INITIAL,
|
||||
|
@ -457,8 +447,7 @@ type V2ServerHello(rec: SSLRecord) = record {
|
|||
cert_data : bytestring &length = cert_len;
|
||||
ciphers : uint24[ciph_len/3];
|
||||
conn_id_data : bytestring &length = conn_id_len;
|
||||
} #&length = 8 + cert_len + ciph_len + conn_id_len,
|
||||
&let {
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
(session_id_hit > 0 ?
|
||||
$context.connection.transition(STATE_CLIENT_HELLO_RCVD,
|
||||
|
@ -608,7 +597,7 @@ type CertificateVerify(rec: SSLRecord) = record {
|
|||
######################################################################
|
||||
|
||||
# The finished messages are always sent after encryption is in effect,
|
||||
# so we will not be able to read those message.
|
||||
# so we will not be able to read those messages.
|
||||
type Finished(rec: SSLRecord) = record {
|
||||
cont : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
|
@ -620,13 +609,17 @@ type Finished(rec: SSLRecord) = record {
|
|||
$context.connection.lost_track();
|
||||
};
|
||||
|
||||
type SessionTicketHandshake(rec: SSLRecord) = record {
|
||||
ticket_lifetime_hint: uint32;
|
||||
data: bytestring &restofdata;
|
||||
};
|
||||
|
||||
######################################################################
|
||||
# V3 Handshake Protocol (7.)
|
||||
######################################################################
|
||||
|
||||
type UnknownHandshake(hs: Handshake, is_orig: bool) = record {
|
||||
cont : bytestring &restofdata &transient;
|
||||
data : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
state_changed : bool = $context.connection.lost_track();
|
||||
};
|
||||
|
@ -636,19 +629,20 @@ type Handshake(rec: SSLRecord) = record {
|
|||
length : uint24;
|
||||
|
||||
body : case msg_type of {
|
||||
HELLO_REQUEST -> hello_request : HelloRequest(rec);
|
||||
CLIENT_HELLO -> client_hello : ClientHello(rec);
|
||||
SERVER_HELLO -> server_hello : ServerHello(rec);
|
||||
CERTIFICATE -> certificate : Certificate(rec);
|
||||
SERVER_KEY_EXCHANGE -> server_key_exchange : ServerKeyExchange(rec);
|
||||
CERTIFICATE_REQUEST -> certificate_request : CertificateRequest(rec);
|
||||
SERVER_HELLO_DONE -> server_hello_done : ServerHelloDone(rec);
|
||||
CERTIFICATE_VERIFY -> certificate_verify : CertificateVerify(rec);
|
||||
CLIENT_KEY_EXCHANGE -> client_key_exchange : ClientKeyExchange(rec);
|
||||
FINISHED -> finished : Finished(rec);
|
||||
CERTIFICATE_URL -> certificate_url : bytestring &restofdata &transient;
|
||||
CERTIFICATE_STATUS -> certificate_status : bytestring &restofdata &transient;
|
||||
default -> unknown_handshake : UnknownHandshake(this, rec.is_orig);
|
||||
HELLO_REQUEST -> hello_request : HelloRequest(rec);
|
||||
CLIENT_HELLO -> client_hello : ClientHello(rec);
|
||||
SERVER_HELLO -> server_hello : ServerHello(rec);
|
||||
SESSION_TICKET -> session_ticket : SessionTicketHandshake(rec);
|
||||
CERTIFICATE -> certificate : Certificate(rec);
|
||||
SERVER_KEY_EXCHANGE -> server_key_exchange : ServerKeyExchange(rec);
|
||||
CERTIFICATE_REQUEST -> certificate_request : CertificateRequest(rec);
|
||||
SERVER_HELLO_DONE -> server_hello_done : ServerHelloDone(rec);
|
||||
CERTIFICATE_VERIFY -> certificate_verify : CertificateVerify(rec);
|
||||
CLIENT_KEY_EXCHANGE -> client_key_exchange : ClientKeyExchange(rec);
|
||||
FINISHED -> finished : Finished(rec);
|
||||
CERTIFICATE_URL -> certificate_url : bytestring &restofdata &transient;
|
||||
CERTIFICATE_STATUS -> certificate_status : bytestring &restofdata &transient;
|
||||
default -> unknown_handshake : UnknownHandshake(this, rec.is_orig);
|
||||
} &length = to_int()(length);
|
||||
};
|
||||
|
||||
|
@ -663,7 +657,7 @@ type UnknownRecord(rec: SSLRecord) = record {
|
|||
state_changed : bool = $context.connection.lost_track();
|
||||
};
|
||||
|
||||
type CiphertextRecord(rec: SSLRecord, is_orig: bool) = record {
|
||||
type CiphertextRecord(rec: SSLRecord) = record {
|
||||
cont : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
|
|
447
src/strings.bif
447
src/strings.bif
|
@ -1,4 +1,5 @@
|
|||
# Definitions of Bro built-in functions related to strings.
|
||||
##! Definitions of built-in functions related to string processing and
|
||||
##! manipulation.
|
||||
|
||||
|
||||
%%{ // C segment
|
||||
|
@ -10,6 +11,14 @@ using namespace std;
|
|||
%%}
|
||||
|
||||
|
||||
## Concates all arguments into a single string. The function takes a variable
|
||||
## number of arguments of type string and stiches them together.
|
||||
##
|
||||
## Returns: The concatenation of all (string) arguments.
|
||||
##
|
||||
## .. bro:see:: cat cat_sep cat_string_array cat_string_array_n
|
||||
## fmt
|
||||
## join_string_vec join_string_array
|
||||
function string_cat%(...%): string
|
||||
%{
|
||||
int n = 0;
|
||||
|
@ -73,18 +82,53 @@ BroString* cat_string_array_n(TableVal* tbl, int start, int end)
|
|||
}
|
||||
%%}
|
||||
|
||||
## Concatenates all elements in an array of strings.
|
||||
##
|
||||
## a: The :bro:type:`string_array` (``table[count] of string``).
|
||||
##
|
||||
## Returns: The concatenation of all elements in *a*.
|
||||
##
|
||||
## .. bro:see:: cat cat_sep string_cat cat_string_array_n
|
||||
## fmt
|
||||
## join_string_vec join_string_array
|
||||
function cat_string_array%(a: string_array%): string
|
||||
%{
|
||||
TableVal* tbl = a->AsTableVal();
|
||||
return new StringVal(cat_string_array_n(tbl, 1, a->AsTable()->Length()));
|
||||
%}
|
||||
|
||||
## Concatenates a specific range of elements in an array of strings.
|
||||
##
|
||||
## a: The :bro:type:`string_array` (``table[count] of string``).
|
||||
##
|
||||
## start: The array index of the first element of the range.
|
||||
##
|
||||
## end: The array index of the last element of the range.
|
||||
##
|
||||
## Returns: The concatenation of the range *[start, end]* in *a*.
|
||||
##
|
||||
## .. bro:see:: cat string_cat cat_string_array
|
||||
## fmt
|
||||
## join_string_vec join_string_array
|
||||
function cat_string_array_n%(a: string_array, start: count, end: count%): string
|
||||
%{
|
||||
TableVal* tbl = a->AsTableVal();
|
||||
return new StringVal(cat_string_array_n(tbl, start, end));
|
||||
%}
|
||||
|
||||
## Joins all values in the given array of strings with a separator placed
|
||||
## between each element.
|
||||
##
|
||||
## sep: The separator to place between each element.
|
||||
##
|
||||
## a: The :bro:type:`string_array` (``table[count] of string``).
|
||||
##
|
||||
## Returns: The concatenation of all elements in *a*, with *sep* placed
|
||||
## between each element.
|
||||
##
|
||||
## .. bro:see:: cat cat_sep string_cat cat_string_array cat_string_array_n
|
||||
## fmt
|
||||
## join_string_vec
|
||||
function join_string_array%(sep: string, a: string_array%): string
|
||||
%{
|
||||
vector<const BroString*> vs;
|
||||
|
@ -108,6 +152,45 @@ function join_string_array%(sep: string, a: string_array%): string
|
|||
return new StringVal(concatenate(vs));
|
||||
%}
|
||||
|
||||
## Joins all values in the given vector of strings with a separator placed
|
||||
## between each element.
|
||||
##
|
||||
## sep: The separator to place between each element.
|
||||
##
|
||||
## a: The :bro:type:`string_vec` (``vector of string``).
|
||||
##
|
||||
## Returns: The concatenation of all elements in *a*, with *sep* placed
|
||||
## between each element.
|
||||
##
|
||||
## .. bro:see:: cat cat_sep string_cat cat_string_array cat_string_array_n
|
||||
## fmt
|
||||
## join_string_array
|
||||
function join_string_vec%(vec: string_vec, sep: string%): string
|
||||
%{
|
||||
ODesc d;
|
||||
VectorVal *v = vec->AsVectorVal();
|
||||
|
||||
for ( unsigned i = 0; i < v->Size(); ++i )
|
||||
{
|
||||
if ( i > 0 )
|
||||
d.Add(sep->CheckString(), 0);
|
||||
|
||||
v->Lookup(i+1)->Describe(&d);
|
||||
}
|
||||
|
||||
BroString* s = new BroString(1, d.TakeBytes(), d.Len());
|
||||
s->SetUseFreeToDelete(true);
|
||||
|
||||
return new StringVal(s);
|
||||
%}
|
||||
|
||||
## Sorts an array of strings.
|
||||
##
|
||||
## a: The :bro:type:`string_array` (``table[count] of string``).
|
||||
##
|
||||
## Returns: A sorted copy of *a*.
|
||||
##
|
||||
## .. bro:see:: sort
|
||||
function sort_string_array%(a: string_array%): string_array
|
||||
%{
|
||||
TableVal* tbl = a->AsTableVal();
|
||||
|
@ -129,31 +212,29 @@ function sort_string_array%(a: string_array%): string_array
|
|||
}
|
||||
// sort(vs.begin(), vs.end(), Bstr_cmp);
|
||||
|
||||
TableVal* b = new TableVal(internal_type("string_array")->AsTableType());
|
||||
TableVal* b = new TableVal(string_array);
|
||||
vs_to_string_array(vs, b, 1, n);
|
||||
return b;
|
||||
%}
|
||||
|
||||
function join_string_vec%(vec: string_vec, sep: string%): string
|
||||
%{
|
||||
ODesc d;
|
||||
VectorVal *v = vec->AsVectorVal();
|
||||
|
||||
for ( unsigned i = 0; i < v->Size(); ++i )
|
||||
{
|
||||
if ( i > 0 )
|
||||
d.Add(sep->CheckString(), 0);
|
||||
|
||||
v->Lookup(i+1)->Describe(&d);
|
||||
}
|
||||
|
||||
BroString* s = new BroString(1, d.TakeBytes(), d.Len());
|
||||
s->SetUseFreeToDelete(true);
|
||||
|
||||
return new StringVal(s);
|
||||
%}
|
||||
|
||||
|
||||
## Returns an edited version of a string that applies a special
|
||||
## "backspace character" (usually ``\x08`` for backspace or ``\x7f`` for DEL).
|
||||
## For ## example, ``edit("hello there", "e")`` returns ``"llo t"``.
|
||||
##
|
||||
## arg_s: The string to edit.
|
||||
##
|
||||
## arg_edit_char: A string of exactly one character that represents the
|
||||
## "backspace character". If it is longer than one character Bro
|
||||
## generates a run-time error and uses the first character in
|
||||
## the string.
|
||||
##
|
||||
## Returns: An edited version of *arg_s* where *arg_edit_char* triggers the
|
||||
## deletetion of the last character.
|
||||
##
|
||||
## .. bro:see:: clean
|
||||
## to_string_literal
|
||||
## escape_string
|
||||
## strip
|
||||
function edit%(arg_s: string, arg_edit_char: string%): string
|
||||
%{
|
||||
if ( arg_edit_char->Len() != 1 )
|
||||
|
@ -184,11 +265,28 @@ function edit%(arg_s: string, arg_edit_char: string%): string
|
|||
return new StringVal(new BroString(1, byte_vec(new_s), ind));
|
||||
%}
|
||||
|
||||
## Returns the number of characters (bytes) in the given string. The
|
||||
## length computation includes any embedded NULs, and also a trailing NUL,
|
||||
## if any (which is why the function isn't called ``strlen``; to remind
|
||||
## the user that Bro strings can include NULs).
|
||||
##
|
||||
## s: The string to compute the length for.
|
||||
##
|
||||
## Returns: The number of characters in *s*.
|
||||
function byte_len%(s: string%): count
|
||||
%{
|
||||
return new Val(s->Len(), TYPE_COUNT);
|
||||
%}
|
||||
|
||||
## Get a substring of from a string, given a starting position length.
|
||||
##
|
||||
## s: The string to obtain a substring from.
|
||||
##
|
||||
## start: The starting position of the substring in *s*
|
||||
##
|
||||
## n: The number of characters to extract, beginning at *start*.
|
||||
##
|
||||
## Returns: A substring of *s* of length *n* from position *start*.
|
||||
function sub_bytes%(s: string, start: count, n: int%): string
|
||||
%{
|
||||
if ( start > 0 )
|
||||
|
@ -216,7 +314,7 @@ static int match_prefix(int s_len, const char* s, int t_len, const char* t)
|
|||
Val* do_split(StringVal* str_val, RE_Matcher* re, TableVal* other_sep,
|
||||
int incl_sep, int max_num_sep)
|
||||
{
|
||||
TableVal* a = new TableVal(internal_type("string_array")->AsTableType());
|
||||
TableVal* a = new TableVal(string_array);
|
||||
ListVal* other_strings = 0;
|
||||
|
||||
if ( other_sep && other_sep->Size() > 0 )
|
||||
|
@ -368,42 +466,94 @@ Val* do_sub(StringVal* str_val, RE_Matcher* re, StringVal* repl, int do_all)
|
|||
}
|
||||
%%}
|
||||
|
||||
# Similar to split in awk.
|
||||
|
||||
## Splits a string into an array of strings according to a pattern.
|
||||
##
|
||||
## str: The string to split.
|
||||
##
|
||||
## re: The pattern describing the element separator in *str*.
|
||||
##
|
||||
## Returns: An array of strings where each element corresponds to a substring
|
||||
## in *str* separated by *re*.
|
||||
##
|
||||
## .. bro:see:: split1 split_all split_n str_split
|
||||
##
|
||||
## .. note:: The returned table starts at index 1. Note that conceptually the
|
||||
## return value is meant to be a vector and this might change in the
|
||||
## future.
|
||||
##
|
||||
function split%(str: string, re: pattern%): string_array
|
||||
%{
|
||||
return do_split(str, re, 0, 0, 0);
|
||||
%}
|
||||
|
||||
# split1(str, pattern, include_separator): table[count] of string
|
||||
#
|
||||
# Same as split, except that str is only split (if possible) at the
|
||||
# earliest position and an array of two strings is returned.
|
||||
# An array of one string is returned when str cannot be splitted.
|
||||
|
||||
## Splits a string *once* into a a two-element array of strings according to a
|
||||
## pattern. This function is the same as :bro:id:`split`, but * is only split
|
||||
## once (if possible) at the earliest position and an array of two strings is
|
||||
## returned.
|
||||
##
|
||||
## str: The string to split.
|
||||
##
|
||||
## re: The pattern describing the separator to split *str* in two pieces.
|
||||
##
|
||||
## Returns: An array of strings with two elements in which the first represents
|
||||
## the substring in *str* up to the first occurence of *re*, and the
|
||||
## second everything after *re*. An array of one string is returned
|
||||
## when *s* cannot be split.
|
||||
##
|
||||
## .. bro:see:: split split_all split_n str_split
|
||||
function split1%(str: string, re: pattern%): string_array
|
||||
%{
|
||||
return do_split(str, re, 0, 0, 1);
|
||||
%}
|
||||
|
||||
# Same as split, except that the array returned by split_all also
|
||||
# includes parts of string that match the pattern in the array.
|
||||
|
||||
# For example, split_all("a-b--cd", /(\-)+/) returns {"a", "-", "b",
|
||||
# "--", "cd"}: odd-indexed elements do not match the pattern
|
||||
# and even-indexed ones do.
|
||||
|
||||
## Splits a string into an array of strings according to a pattern. This
|
||||
## function is the same as :bro:id:`split`, except that the separators are
|
||||
## returned as well. For example, ``split_all("a-b--cd", /(\-)+/)`` returns
|
||||
## ``{"a", "-", "b", "--", "cd"}``: odd-indexed elements do not match the
|
||||
## pattern and even-indexed ones do.
|
||||
##
|
||||
## str: The string to split.
|
||||
##
|
||||
## re: The pattern describing the element separator in *str*.
|
||||
##
|
||||
## Returns: An array of strings where each two successive elements correspond
|
||||
## to a substring in *str* of the part not matching *re* (odd-indexed)
|
||||
## and thei part that matches *re* (even-indexed).
|
||||
##
|
||||
## .. bro:see:: split split1 split_n str_split
|
||||
function split_all%(str: string, re: pattern%): string_array
|
||||
%{
|
||||
return do_split(str, re, 0, 1, 0);
|
||||
%}
|
||||
|
||||
## Splits a string a given number of times into an array of strings according
|
||||
## to a pattern. This function is similar to :bro:id:`split1` and
|
||||
## :bro:id:`split_all`, but with customizable behavior with respect to
|
||||
## including separators in the result and the number of times to split.
|
||||
##
|
||||
## str: The string to split.
|
||||
##
|
||||
## re: The pattern describing the element separator in *str*.
|
||||
##
|
||||
## incl_sep: A flag indicating whether to include the separator matches in the
|
||||
## result (as in :bro:id:`split_all`).
|
||||
##
|
||||
## max_num_sep: The number of times to split *str*.
|
||||
##
|
||||
## Returns: An array of strings where, if *incl_sep* is true, each two
|
||||
## successive elements correspond to a substring in *str* of the part
|
||||
## not matching *re* (odd-indexed) and the part that matches *re*
|
||||
## (even-indexed).
|
||||
##
|
||||
## .. bro:see:: split split1 split_all str_split
|
||||
function split_n%(str: string, re: pattern,
|
||||
incl_sep: bool, max_num_sep: count%): string_array
|
||||
%{
|
||||
return do_split(str, re, 0, incl_sep, max_num_sep);
|
||||
%}
|
||||
|
||||
## Deprecated. Will be removed.
|
||||
# Reason: the parameter ``other`` does nothing.
|
||||
function split_complete%(str: string,
|
||||
re: pattern, other: string_set,
|
||||
incl_sep: bool, max_num_sep: count%): string_array
|
||||
|
@ -411,22 +561,65 @@ function split_complete%(str: string,
|
|||
return do_split(str, re, other->AsTableVal(), incl_sep, max_num_sep);
|
||||
%}
|
||||
|
||||
## Substitutes a given replacement string for the first occurrence of a pattern
|
||||
## in a given string.
|
||||
##
|
||||
## str: The string to perform the substitution in.
|
||||
##
|
||||
## re: The pattern being replaced with *repl*.
|
||||
##
|
||||
## repl: The string that replacs *re*.
|
||||
##
|
||||
## Returns: A copy of *str* with the first occurence of *re* replaced with
|
||||
## *repl*.
|
||||
##
|
||||
## .. bro:see:: gsub subst_string
|
||||
function sub%(str: string, re: pattern, repl: string%): string
|
||||
%{
|
||||
return do_sub(str, re, repl, 0);
|
||||
%}
|
||||
|
||||
## Substitutes a given replacement string for the all occurrences of a pattern
|
||||
## in a given string.
|
||||
##
|
||||
## str: The string to perform the substitution in.
|
||||
##
|
||||
## re: The pattern being replaced with *repl*.
|
||||
##
|
||||
## repl: The string that replacs *re*.
|
||||
##
|
||||
## Returns: A copy of *str* with all occurences of *re* replaced with *repl*.
|
||||
##
|
||||
## .. bro:see:: sub subst_string
|
||||
function gsub%(str: string, re: pattern, repl: string%): string
|
||||
%{
|
||||
return do_sub(str, re, repl, 1);
|
||||
%}
|
||||
|
||||
|
||||
## Lexicographically compares two string.
|
||||
##
|
||||
## s1: The first string.
|
||||
##
|
||||
## s2: The second string.
|
||||
##
|
||||
## Returns: An integer greater than, equal to, or less than 0 according as
|
||||
## *s1* is greater than, equal to, or less than *s2*.
|
||||
function strcmp%(s1: string, s2: string%): int
|
||||
%{
|
||||
return new Val(Bstr_cmp(s1->AsString(), s2->AsString()), TYPE_INT);
|
||||
%}
|
||||
|
||||
# Returns 0 if $little is not found in $big.
|
||||
## Locates the first occurrence of one string in another.
|
||||
##
|
||||
## big: The string to look in.
|
||||
##
|
||||
## little: The (smaller) string to find inside *big*.
|
||||
##
|
||||
## Returns: The location of *little* in *big* or 0 if *little* is not found in
|
||||
## *big*.
|
||||
##
|
||||
## .. bro:see:: find_all find_last
|
||||
function strstr%(big: string, little: string%): count
|
||||
%{
|
||||
return new Val(
|
||||
|
@ -434,8 +627,17 @@ function strstr%(big: string, little: string%): count
|
|||
TYPE_COUNT);
|
||||
%}
|
||||
|
||||
# Substitute each (non-overlapping) appearance of $from in $s to $to,
|
||||
# and return the resulting string.
|
||||
## Substitutes each (non-overlapping) appearance of a string in another.
|
||||
##
|
||||
## s: The string in which to perform the substitution.
|
||||
##
|
||||
## from: The string to look for which is replaced with *to*.
|
||||
##
|
||||
## to: The string that replaces all occurrences of *from* in *s*.
|
||||
##
|
||||
## Returns: A copy of *s* where each occurrence of *from* is replaced with *to*.
|
||||
##
|
||||
## .. bro:see:: sub gsub
|
||||
function subst_string%(s: string, from: string, to: string%): string
|
||||
%{
|
||||
const int little_len = from->Len();
|
||||
|
@ -478,6 +680,15 @@ function subst_string%(s: string, from: string, to: string%): string
|
|||
return new StringVal(concatenate(vs));
|
||||
%}
|
||||
|
||||
## Replaces all uppercase letters in a string with their lowercase counterpart.
|
||||
##
|
||||
## str: The string to convert to lowercase letters.
|
||||
##
|
||||
## Returns: A copy of the given string with the uppercase letters (as indicated
|
||||
## by ``isascii`` and \verb|isupper|``) folded to lowercase
|
||||
## (via ``tolower``).
|
||||
##
|
||||
## .. bro:see:: to_upper is_ascii
|
||||
function to_lower%(str: string%): string
|
||||
%{
|
||||
const u_char* s = str->Bytes();
|
||||
|
@ -498,6 +709,15 @@ function to_lower%(str: string%): string
|
|||
return new StringVal(new BroString(1, lower_s, n));
|
||||
%}
|
||||
|
||||
## Replaces all lowercase letters in a string with their uppercase counterpart.
|
||||
##
|
||||
## str: The string to convert to uppercase letters.
|
||||
##
|
||||
## Returns: A copy of the given string with the lowercase letters (as indicated
|
||||
## by ``isascii`` and \verb|islower|``) folded to uppercase
|
||||
## (via ``toupper``).
|
||||
##
|
||||
## .. bro:see:: to_lower is_ascii
|
||||
function to_upper%(str: string%): string
|
||||
%{
|
||||
const u_char* s = str->Bytes();
|
||||
|
@ -518,18 +738,54 @@ function to_upper%(str: string%): string
|
|||
return new StringVal(new BroString(1, upper_s, n));
|
||||
%}
|
||||
|
||||
## Replaces non-printable characters in a string with escaped sequences. The
|
||||
## mappings are:
|
||||
##
|
||||
## - ``NUL`` to ``\0``
|
||||
## - ``DEL`` to ``^?``
|
||||
## - values <= 26 to ``^[A-Z]``
|
||||
## - values not in *[32, 126]** to ``%XX``
|
||||
##
|
||||
## If the string does not yet have a trailing NUL, one is added.
|
||||
##
|
||||
## str: The string to escape.
|
||||
##
|
||||
## Returns: The escaped string.
|
||||
##
|
||||
## .. bro:see:: to_string_literal escape_string
|
||||
function clean%(str: string%): string
|
||||
%{
|
||||
char* s = str->AsString()->Render();
|
||||
return new StringVal(new BroString(1, byte_vec(s), strlen(s)));
|
||||
%}
|
||||
|
||||
## Replaces non-printable characters in a string with escaped sequences. The
|
||||
## mappings are:
|
||||
##
|
||||
## - ``NUL`` to ``\0``
|
||||
## - ``DEL`` to ``^?``
|
||||
## - values <= 26 to ``^[A-Z]``
|
||||
## - values not in *[32, 126]** to ``%XX``
|
||||
##
|
||||
## str: The string to escape.
|
||||
##
|
||||
## Returns: The escaped string.
|
||||
##
|
||||
## .. bro:see:: clean escape_string
|
||||
function to_string_literal%(str: string%): string
|
||||
%{
|
||||
char* s = str->AsString()->Render(BroString::BRO_STRING_LITERAL);
|
||||
return new StringVal(new BroString(1, byte_vec(s), strlen(s)));
|
||||
%}
|
||||
|
||||
## Determines whether a given string contains only ASCII characters.
|
||||
##
|
||||
## str: The string to examine.
|
||||
##
|
||||
## Returns: False if any byte value of *str* is greater than 127, and true
|
||||
## otherwise.
|
||||
##
|
||||
## .. bro:see:: to_upper to_lower
|
||||
function is_ascii%(str: string%): bool
|
||||
%{
|
||||
int n = str->Len();
|
||||
|
@ -542,7 +798,14 @@ function is_ascii%(str: string%): bool
|
|||
return new Val(1, TYPE_BOOL);
|
||||
%}
|
||||
|
||||
# Make printable version of string.
|
||||
## Creates a printable version of a string. This function is the same as
|
||||
## :bro:id:`clean` except that non-printable characters are removed.
|
||||
##
|
||||
## s: The string to escape.
|
||||
##
|
||||
## Returns: The escaped string.
|
||||
##
|
||||
## .. bro:see:: clean to_string_literal
|
||||
function escape_string%(s: string%): string
|
||||
%{
|
||||
char* escstr = s->AsString()->Render();
|
||||
|
@ -551,7 +814,12 @@ function escape_string%(s: string%): string
|
|||
return val;
|
||||
%}
|
||||
|
||||
# Returns an ASCII hexadecimal representation of a string.
|
||||
## Returns an ASCII hexadecimal representation of a string.
|
||||
##
|
||||
## s: The string to convert to hex.
|
||||
##
|
||||
## Returns: A copy of *s* where each byte is replaced with the corresponding
|
||||
## hex nibble.
|
||||
function string_to_ascii_hex%(s: string%): string
|
||||
%{
|
||||
char* x = new char[s->Len() * 2 + 1];
|
||||
|
@ -563,8 +831,15 @@ function string_to_ascii_hex%(s: string%): string
|
|||
return new StringVal(new BroString(1, (u_char*) x, s->Len() * 2));
|
||||
%}
|
||||
|
||||
function str_smith_waterman%(s1: string, s2: string, params: sw_params%)
|
||||
: sw_substring_vec
|
||||
## Uses the Smith Waterman algorithm to find similar/overlapping substrings.
|
||||
## See `Wikipedia <http://en.wikipedia.org/wiki/Smith%E2%80%93Waterman_algorithm>`_.
|
||||
##
|
||||
## s1: The first string.
|
||||
##
|
||||
## s2: The second string.
|
||||
##
|
||||
## Returns: The result of the Smit Waterman algorithm calculation.
|
||||
function str_smith_waterman%(s1: string, s2: string, params: sw_params%) : sw_substring_vec
|
||||
%{
|
||||
SWParams sw_params(params->AsRecordVal()->Lookup(0)->AsCount(),
|
||||
SWVariant(params->AsRecordVal()->Lookup(1)->AsCount()));
|
||||
|
@ -578,6 +853,16 @@ function str_smith_waterman%(s1: string, s2: string, params: sw_params%)
|
|||
return result;
|
||||
%}
|
||||
|
||||
## Splits a string into substrings with the help of an index vector of cutting
|
||||
## points.
|
||||
##
|
||||
## s: The string to split.
|
||||
##
|
||||
## idx: The index vector (``vector of count``) with the cutting points.
|
||||
##
|
||||
## Returns: A vector of strings.
|
||||
##
|
||||
## .. bro:see:: split split1 split_all split_n
|
||||
function str_split%(s: string, idx: index_vec%): string_vec
|
||||
%{
|
||||
vector<Val*>* idx_v = idx->AsVector();
|
||||
|
@ -606,6 +891,13 @@ function str_split%(s: string, idx: index_vec%): string_vec
|
|||
return result_v;
|
||||
%}
|
||||
|
||||
## Strips whitespace at both ends of a string.
|
||||
##
|
||||
## str: The string to strip the whitespace from.
|
||||
##
|
||||
## Returns: A copy of *str* with leading and trailing whitespace removed.
|
||||
##
|
||||
## .. bro:see:: sub gsub
|
||||
function strip%(str: string%): string
|
||||
%{
|
||||
const u_char* s = str->Bytes();
|
||||
|
@ -629,6 +921,14 @@ function strip%(str: string%): string
|
|||
return new StringVal(new BroString(sp, (e - sp + 1), 1));
|
||||
%}
|
||||
|
||||
## Generates a string of a given size and fills it with repetitions of a source
|
||||
## string.
|
||||
##
|
||||
## len: The length of the output string.
|
||||
##
|
||||
## source: The string to concatenate repeatedly until *len* has been reached.
|
||||
##
|
||||
## Returns: A string of length *len* filled with *source*.
|
||||
function string_fill%(len: int, source: string%): string
|
||||
%{
|
||||
const u_char* src = source->Bytes();
|
||||
|
@ -643,10 +943,15 @@ function string_fill%(len: int, source: string%): string
|
|||
return new StringVal(new BroString(1, byte_vec(dst), len));
|
||||
%}
|
||||
|
||||
# Takes a string and escapes characters that would allow execution of commands
|
||||
# at the shell level. Must be used before including strings in system() or
|
||||
# similar calls.
|
||||
#
|
||||
## Takes a string and escapes characters that would allow execution of
|
||||
## commands at the shell level. Must be used before including strings in
|
||||
## :bro:id:`system` or similar calls.
|
||||
##
|
||||
## source: The string to escape.
|
||||
##
|
||||
## Returns: A shell-escaped version of *source*.
|
||||
##
|
||||
## .. bro:see:: system
|
||||
function str_shell_escape%(source: string%): string
|
||||
%{
|
||||
unsigned j = 0;
|
||||
|
@ -675,11 +980,18 @@ function str_shell_escape%(source: string%): string
|
|||
return new StringVal(new BroString(1, dst, j));
|
||||
%}
|
||||
|
||||
# Returns all occurrences of the given pattern in the given string (an empty
|
||||
# empty set if none).
|
||||
## Finds all occurrences of a pattern in a string.
|
||||
##
|
||||
## str: The string to inspect.
|
||||
##
|
||||
## re: The pattern to look for in *str*.
|
||||
##
|
||||
## Returns: The set of strings in *str* that match *re*, or the empty set.
|
||||
##
|
||||
## .. bro:see: find_last strstr
|
||||
function find_all%(str: string, re: pattern%) : string_set
|
||||
%{
|
||||
TableVal* a = new TableVal(internal_type("string_set")->AsTableType());
|
||||
TableVal* a = new TableVal(string_set);
|
||||
|
||||
const u_char* s = str->Bytes();
|
||||
const u_char* e = s + str->Len();
|
||||
|
@ -697,11 +1009,18 @@ function find_all%(str: string, re: pattern%) : string_set
|
|||
return a;
|
||||
%}
|
||||
|
||||
# Returns the last occurrence of the given pattern in the given string.
|
||||
# If not found, returns an empty string. Note that this function returns
|
||||
# the match that starts at the largest index in the string, which is
|
||||
# not necessarily the longest match. For example, a pattern of /.*/
|
||||
# will return the final character in the string.
|
||||
## Finds the last occurrence of a pattern in a string. This function returns
|
||||
## the match that starts at the largest index in the string, which is not
|
||||
## necessarily the longest match. For example, a pattern of ``/.*/`` will
|
||||
## return the final character in the string.
|
||||
##
|
||||
## str: The string to inspect.
|
||||
##
|
||||
## re: The pattern to look for in *str*.
|
||||
##
|
||||
## Returns: The last string in *str* that matches *re*, or the empty string.
|
||||
##
|
||||
## .. bro:see: find_all strstr
|
||||
function find_last%(str: string, re: pattern%) : string
|
||||
%{
|
||||
const u_char* s = str->Bytes();
|
||||
|
@ -717,10 +1036,16 @@ function find_last%(str: string, re: pattern%) : string
|
|||
return new StringVal("");
|
||||
%}
|
||||
|
||||
# Returns a hex dump for given input data. The hex dump renders
|
||||
# 16 bytes per line, with hex on the left and ASCII (where printable)
|
||||
# on the right. Based on Netdude's hex editor code.
|
||||
#
|
||||
## Returns a hex dump for given input data. The hex dump renders 16 bytes per
|
||||
## line, with hex on the left and ASCII (where printable)
|
||||
## on the right.
|
||||
##
|
||||
## data_str: The string to dump in hex format.
|
||||
##
|
||||
## .. bro:see:: string_to_ascii_hex bytestring_to_hexstr
|
||||
##
|
||||
## .. note:: Based on Netdude's hex editor code.
|
||||
##
|
||||
function hexdump%(data_str: string%) : string
|
||||
%{
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
##! Declaration of various types that the Bro core uses internally.
|
||||
|
||||
enum dce_rpc_ptype %{
|
||||
DCE_RPC_REQUEST,
|
||||
|
@ -134,8 +135,8 @@ enum createmode_t %{
|
|||
EXCLUSIVE = 2,
|
||||
%}
|
||||
|
||||
# Decleare record types that we want to access from the even engine. These are
|
||||
# defined in bro.init.
|
||||
# Declare record types that we want to access from the event engine. These are
|
||||
# defined in init-bare.bro.
|
||||
type info_t: record;
|
||||
type fattr_t: record;
|
||||
type diropargs_t: record;
|
||||
|
|
31
src/util.cc
31
src/util.cc
|
@ -41,6 +41,37 @@
|
|||
#include "Net.h"
|
||||
#include "Reporter.h"
|
||||
|
||||
/**
|
||||
* Takes a string, escapes characters into equivalent hex codes (\x##), and
|
||||
* returns a string containing all escaped values.
|
||||
*
|
||||
* @param str string to escape
|
||||
* @param escape_all If true, all characters are escaped. If false, only
|
||||
* characters are escaped that are either whitespace or not printable in
|
||||
* ASCII.
|
||||
* @return A std::string containing a list of escaped hex values of the form
|
||||
* \x## */
|
||||
std::string get_escaped_string(const std::string& str, bool escape_all)
|
||||
{
|
||||
char tbuf[16];
|
||||
string esc = "";
|
||||
|
||||
for ( size_t i = 0; i < str.length(); ++i )
|
||||
{
|
||||
char c = str[i];
|
||||
|
||||
if ( escape_all || isspace(c) || ! isascii(c) || ! isprint(c) )
|
||||
{
|
||||
snprintf(tbuf, sizeof(tbuf), "\\x%02x", str[i]);
|
||||
esc += tbuf;
|
||||
}
|
||||
else
|
||||
esc += c;
|
||||
}
|
||||
|
||||
return esc;
|
||||
}
|
||||
|
||||
char* copy_string(const char* s)
|
||||
{
|
||||
char* c = new char[strlen(s)+1];
|
||||
|
|
|
@ -89,6 +89,8 @@ void delete_each(T* t)
|
|||
delete *it;
|
||||
}
|
||||
|
||||
std::string get_escaped_string(const std::string& str, bool escape_all);
|
||||
|
||||
extern char* copy_string(const char* s);
|
||||
extern int streq(const char* s1, const char* s2);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue