mirror of
https://github.com/zeek/zeek.git
synced 2025-10-05 08:08:19 +00:00
Merge remote-tracking branch 'origin/master' into topic/robin/pktsrc
Conflicts: configure src/CMakeLists.txt src/Net.cc src/PacketSort.cc src/PacketSort.h src/RemoteSerializer.cc src/Sessions.cc src/Sessions.h
This commit is contained in:
commit
bf6dd2e9ca
794 changed files with 119018 additions and 91558 deletions
|
@ -1,142 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef THREADING_ASCII_FORMATTER_H
|
||||
#define THREADING_ASCII_FORMATTER_H
|
||||
|
||||
#include "../Desc.h"
|
||||
#include "MsgThread.h"
|
||||
|
||||
/**
|
||||
* A thread-safe class for converting values into a readable ASCII
|
||||
* representation, and vice versa. This is a utility class that factors out
|
||||
* common rendering/parsing code needed by a number of input/output threads.
|
||||
*/
|
||||
class AsciiFormatter {
|
||||
public:
|
||||
/**
|
||||
* A struct to pass the necessary configuration values to the
|
||||
* AsciiFormatter module on initialization.
|
||||
*/
|
||||
struct SeparatorInfo
|
||||
{
|
||||
string set_separator; // Separator between set elements.
|
||||
string unset_field; // String marking an unset field.
|
||||
string empty_field; // String marking an empty (but set) field.
|
||||
|
||||
/**
|
||||
* Constructor that leaves separators etc unset to dummy
|
||||
* values. Useful if you use only methods that don't need any
|
||||
* of them, like StringToAddr, etc.
|
||||
*/
|
||||
SeparatorInfo();
|
||||
|
||||
/**
|
||||
* Constructor that defines all the configuration options.
|
||||
* Use if you need either ValToODesc or EntryToVal.
|
||||
*/
|
||||
SeparatorInfo(const string& set_separator, const string& unset_field, const string& empty_field);
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param t The thread that uses this class instance. The class uses
|
||||
* some of the thread's methods, e.g., for error reporting and
|
||||
* internal formatting.
|
||||
*
|
||||
* @param info SeparatorInfo structure defining the necessary
|
||||
* separators.
|
||||
*/
|
||||
AsciiFormatter(threading::MsgThread* t, const SeparatorInfo info);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~AsciiFormatter();
|
||||
|
||||
/**
|
||||
* Convert a threading value into a corresponding ASCII.
|
||||
* representation.
|
||||
*
|
||||
* @param desc The ODesc object to write to.
|
||||
*
|
||||
* @param val the Value to render to the ODesc object.
|
||||
*
|
||||
* @param The name of a field associated with the value. Used only
|
||||
* for error reporting.
|
||||
*
|
||||
* @return Returns true on success, false on error. Errors are also
|
||||
* flagged via the reporter.
|
||||
*/
|
||||
bool Describe(ODesc* desc, threading::Value* val, const string& name) const;
|
||||
|
||||
/**
|
||||
* Convert an IP address into a string.
|
||||
*
|
||||
* @param addr The address.
|
||||
*
|
||||
* @return An ASCII representation of the address.
|
||||
*/
|
||||
string Render(const threading::Value::addr_t& addr) const;
|
||||
|
||||
/**
|
||||
* Convert an subnet value into a string.
|
||||
*
|
||||
* @param addr The address.
|
||||
*
|
||||
* @return An ASCII representation of the subnet.
|
||||
*/
|
||||
string Render(const threading::Value::subnet_t& subnet) const;
|
||||
|
||||
/**
|
||||
* Convert a double into a string. This renders the double with Bro's
|
||||
* standard precision.
|
||||
*
|
||||
* @param d The double.
|
||||
*
|
||||
* @return An ASCII representation of the double.
|
||||
*/
|
||||
string Render(double d) const;
|
||||
|
||||
/**
|
||||
* Convert the ASCII representation of a field into a value.
|
||||
*
|
||||
* @param s The string to parse.
|
||||
*
|
||||
* @param The name of a field associated with the value. Used only
|
||||
* for error reporting.
|
||||
*
|
||||
* @return The new value, or null on error. Errors are also flagged
|
||||
* via the reporter.
|
||||
*/
|
||||
threading::Value* ParseValue(string s, string name, TypeTag type, TypeTag subtype = TYPE_ERROR) const;
|
||||
|
||||
/**
|
||||
* Convert a string into a TransportProto. The string must be one of
|
||||
* \c tcp, \c udp, \c icmp, or \c unknown.
|
||||
*
|
||||
* @param proto The transport protocol
|
||||
*
|
||||
* @return The transport protocol, which will be \c TRANSPORT_UNKNOWN
|
||||
* on error. Errors are also flagged via the reporter.
|
||||
*/
|
||||
TransportProto ParseProto(const string &proto) const;
|
||||
|
||||
/**
|
||||
* Convert a string into a Value::addr_t.
|
||||
*
|
||||
* @param addr String containing an IPv4 or IPv6 address.
|
||||
*
|
||||
* @return The address, which will be all-zero on error. Errors are
|
||||
* also flagged via the reporter.
|
||||
*/
|
||||
threading::Value::addr_t ParseAddr(const string &addr) const;
|
||||
|
||||
private:
|
||||
bool CheckNumberError(const string& s, const char * end) const;
|
||||
|
||||
SeparatorInfo separators;
|
||||
threading::MsgThread* thread;
|
||||
};
|
||||
|
||||
#endif /* THREADING_ASCII_FORMATTER_H */
|
|
@ -24,7 +24,7 @@ BasicThread::BasicThread()
|
|||
pthread = 0;
|
||||
|
||||
buf_len = STD_FMT_BUF_LEN;
|
||||
buf = (char*) malloc(buf_len);
|
||||
buf = (char*) safe_malloc(buf_len);
|
||||
|
||||
strerr_buffer = 0;
|
||||
|
||||
|
|
113
src/threading/Formatter.cc
Normal file
113
src/threading/Formatter.cc
Normal file
|
@ -0,0 +1,113 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <errno.h>
|
||||
|
||||
#include "Formatter.h"
|
||||
#include "bro_inet_ntop.h"
|
||||
|
||||
using namespace threading;
|
||||
using namespace formatter;
|
||||
using threading::Value;
|
||||
using threading::Field;
|
||||
|
||||
Formatter::Formatter(threading::MsgThread* t)
|
||||
{
|
||||
thread = t;
|
||||
}
|
||||
|
||||
Formatter::~Formatter()
|
||||
{
|
||||
}
|
||||
|
||||
string Formatter::Render(const threading::Value::addr_t& addr) const
|
||||
{
|
||||
if ( addr.family == IPv4 )
|
||||
{
|
||||
char s[INET_ADDRSTRLEN];
|
||||
|
||||
if ( ! bro_inet_ntop(AF_INET, &addr.in.in4, s, INET_ADDRSTRLEN) )
|
||||
return "<bad IPv4 address conversion>";
|
||||
else
|
||||
return s;
|
||||
}
|
||||
else
|
||||
{
|
||||
char s[INET6_ADDRSTRLEN];
|
||||
|
||||
if ( ! bro_inet_ntop(AF_INET6, &addr.in.in6, s, INET6_ADDRSTRLEN) )
|
||||
return "<bad IPv6 address conversion>";
|
||||
else
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
TransportProto Formatter::ParseProto(const string &proto) const
|
||||
{
|
||||
if ( proto == "unknown" )
|
||||
return TRANSPORT_UNKNOWN;
|
||||
else if ( proto == "tcp" )
|
||||
return TRANSPORT_TCP;
|
||||
else if ( proto == "udp" )
|
||||
return TRANSPORT_UDP;
|
||||
else if ( proto == "icmp" )
|
||||
return TRANSPORT_ICMP;
|
||||
|
||||
thread->Error(thread->Fmt("Tried to parse invalid/unknown protocol: %s", proto.c_str()));
|
||||
|
||||
return TRANSPORT_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
// More or less verbose copy from IPAddr.cc -- which uses reporter.
|
||||
threading::Value::addr_t Formatter::ParseAddr(const string &s) const
|
||||
{
|
||||
threading::Value::addr_t val;
|
||||
|
||||
if ( s.find(':') == std::string::npos ) // IPv4.
|
||||
{
|
||||
val.family = IPv4;
|
||||
|
||||
if ( inet_aton(s.c_str(), &(val.in.in4)) <= 0 )
|
||||
{
|
||||
thread->Error(thread->Fmt("Bad address: %s", s.c_str()));
|
||||
memset(&val.in.in4.s_addr, 0, sizeof(val.in.in4.s_addr));
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
val.family = IPv6;
|
||||
if ( inet_pton(AF_INET6, s.c_str(), val.in.in6.s6_addr) <=0 )
|
||||
{
|
||||
thread->Error(thread->Fmt("Bad address: %s", s.c_str()));
|
||||
memset(val.in.in6.s6_addr, 0, sizeof(val.in.in6.s6_addr));
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
string Formatter::Render(const threading::Value::subnet_t& subnet) const
|
||||
{
|
||||
char l[16];
|
||||
|
||||
if ( subnet.prefix.family == IPv4 )
|
||||
modp_uitoa10(subnet.length - 96, l);
|
||||
else
|
||||
modp_uitoa10(subnet.length, l);
|
||||
|
||||
string s = Render(subnet.prefix) + "/" + l;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
string Formatter::Render(double d) const
|
||||
{
|
||||
char buf[256];
|
||||
modp_dtoa(d, buf, 6);
|
||||
return buf;
|
||||
}
|
||||
|
153
src/threading/Formatter.h
Normal file
153
src/threading/Formatter.h
Normal file
|
@ -0,0 +1,153 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef THREADING_FORMATTER_H
|
||||
#define THREADING_FORMATTER_H
|
||||
|
||||
#include "../Desc.h"
|
||||
#include "MsgThread.h"
|
||||
|
||||
namespace threading { namespace formatter {
|
||||
|
||||
/**
|
||||
* A thread-safe class for converting values into some textual format. This
|
||||
* is a base class that implements the interface for common
|
||||
* rendering/parsing code needed by a number of input/output threads.
|
||||
*/
|
||||
class Formatter {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param t The thread that uses this class instance. The class uses
|
||||
* some of the thread's methods, e.g., for error reporting and
|
||||
* internal formatting.
|
||||
*
|
||||
*/
|
||||
Formatter(threading::MsgThread* t);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~Formatter();
|
||||
|
||||
/**
|
||||
* Convert a list of threading values into an implementation specific
|
||||
* textual representation.
|
||||
*
|
||||
* @param desc The ODesc object to write to.
|
||||
*
|
||||
* @param num_fields The number of fields in the logging record.
|
||||
*
|
||||
* @param fields Information about the fields for each of the given
|
||||
* log values.
|
||||
*
|
||||
* @param vals The field values.
|
||||
*
|
||||
* @return Returns true on success, false on error. Errors must also
|
||||
* be flagged via the thread.
|
||||
*/
|
||||
virtual bool Describe(ODesc* desc, int num_fields, const threading::Field* const * fields,
|
||||
threading::Value** vals) const = 0;
|
||||
|
||||
/**
|
||||
* Convert a single threading value into an implementation-specific
|
||||
* representation.
|
||||
*
|
||||
* @param desc The ODesc object to write to.
|
||||
*
|
||||
* @param val the Value to render to the ODesc object.
|
||||
*
|
||||
* @param The name of a field associated with the value.
|
||||
*
|
||||
* @return Returns true on success, false on error. Errors are also
|
||||
* flagged via the thread.
|
||||
*/
|
||||
virtual bool Describe(ODesc* desc, threading::Value* val, const string& name = "") const = 0;
|
||||
|
||||
/**
|
||||
* Convert an implementation-specific textual representation of a
|
||||
* field into a value.
|
||||
*
|
||||
* @param s The string to parse.
|
||||
*
|
||||
* @param The name of a field associated with the value. Used only
|
||||
* for error reporting.
|
||||
*
|
||||
* @return The new value, or null on error. Errors must also be
|
||||
* flagged via the thread.
|
||||
*/
|
||||
virtual threading::Value* ParseValue(const string& s, const string& name, TypeTag type, TypeTag subtype = TYPE_ERROR) const = 0;
|
||||
|
||||
/**
|
||||
* Convert an IP address into a string.
|
||||
*
|
||||
* This is a helper function that formatter implementations may use.
|
||||
*
|
||||
* @param addr The address.
|
||||
*
|
||||
* @return An ASCII representation of the address.
|
||||
*/
|
||||
string Render(const threading::Value::addr_t& addr) const;
|
||||
|
||||
/**
|
||||
* Convert an subnet value into a string.
|
||||
*
|
||||
* This is a helper function that formatter implementations may use.
|
||||
*
|
||||
* @param addr The address.
|
||||
*
|
||||
* @return An ASCII representation of the subnet.
|
||||
*/
|
||||
string Render(const threading::Value::subnet_t& subnet) const;
|
||||
|
||||
/**
|
||||
* Convert a double into a string. This renders the double with Bro's
|
||||
* standard precision.
|
||||
*
|
||||
* This is a helper function that formatter implementations may use.
|
||||
*
|
||||
* @param d The double.
|
||||
*
|
||||
* @return An ASCII representation of the double.
|
||||
*/
|
||||
string Render(double d) const;
|
||||
|
||||
/**
|
||||
* Convert a string into a TransportProto. The string must be one of
|
||||
* \c tcp, \c udp, \c icmp, or \c unknown.
|
||||
*
|
||||
* This is a helper function that formatter implementations may use.
|
||||
*
|
||||
* @param proto The transport protocol
|
||||
*
|
||||
* @return The transport protocol, which will be \c TRANSPORT_UNKNOWN
|
||||
* on error. Errors are also flagged via the thread.
|
||||
*/
|
||||
TransportProto ParseProto(const string &proto) const;
|
||||
|
||||
/**
|
||||
* Convert a string into a Value::addr_t.
|
||||
*
|
||||
* This is a helper function that formatter implementations may use.
|
||||
*
|
||||
* @param addr String containing an IPv4 or IPv6 address.
|
||||
*
|
||||
* @return The address, which will be all-zero on error. Errors are
|
||||
* also flagged via the thread.
|
||||
*/
|
||||
threading::Value::addr_t ParseAddr(const string &addr) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Returns the thread associated with the formatter via the
|
||||
* constructor.
|
||||
*/
|
||||
threading::MsgThread* GetThread() const { return thread; }
|
||||
|
||||
private:
|
||||
threading::MsgThread* thread;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* THREADING_FORMATTER_H */
|
|
@ -53,7 +53,6 @@ public:
|
|||
{ network_time = arg_network_time; current_time = arg_current_time; }
|
||||
|
||||
virtual bool Process() {
|
||||
Object()->HeartbeatInChild();
|
||||
return Object()->OnHeartbeat(network_time, current_time);
|
||||
}
|
||||
|
||||
|
@ -254,15 +253,6 @@ void MsgThread::Heartbeat()
|
|||
SendIn(new HeartbeatMessage(this, network_time, current_time()));
|
||||
}
|
||||
|
||||
void MsgThread::HeartbeatInChild()
|
||||
{
|
||||
string n = Fmt("bro: %s (%" PRIu64 "/%" PRIu64 ")", Name(),
|
||||
cnt_sent_in - queue_in.Size(),
|
||||
cnt_sent_out - queue_out.Size());
|
||||
|
||||
SetOSName(n.c_str());
|
||||
}
|
||||
|
||||
void MsgThread::Finished()
|
||||
{
|
||||
child_finished = true;
|
||||
|
|
|
@ -197,10 +197,6 @@ protected:
|
|||
*/
|
||||
virtual void Heartbeat();
|
||||
|
||||
/** Internal heartbeat processing. Called from child.
|
||||
*/
|
||||
void HeartbeatInChild();
|
||||
|
||||
/** Returns true if a child command has reported a failure. In that case, we'll
|
||||
* be in the process of killing this thread and no further activity
|
||||
* should carried out. To be called only from this child thread.
|
||||
|
|
|
@ -66,7 +66,14 @@ bool Field::Write(SerializationFormat* fmt) const
|
|||
|
||||
string Field::TypeName() const
|
||||
{
|
||||
string n = type_name(type);
|
||||
string n;
|
||||
|
||||
// We do not support tables, if the internal Bro type is table it
|
||||
// always is a set.
|
||||
if ( type == TYPE_TABLE )
|
||||
n = "set";
|
||||
else
|
||||
n = type_name(type);
|
||||
|
||||
if ( (type == TYPE_TABLE) || (type == TYPE_VECTOR) )
|
||||
{
|
||||
|
|
|
@ -5,35 +5,54 @@
|
|||
#include <sstream>
|
||||
#include <errno.h>
|
||||
|
||||
#include "AsciiFormatter.h"
|
||||
#include "bro_inet_ntop.h"
|
||||
#include "./Ascii.h"
|
||||
|
||||
AsciiFormatter::SeparatorInfo::SeparatorInfo()
|
||||
using namespace threading::formatter;
|
||||
|
||||
Ascii::SeparatorInfo::SeparatorInfo()
|
||||
{
|
||||
this->set_separator = "SHOULD_NOT_BE_USED";
|
||||
this->unset_field = "SHOULD_NOT_BE_USED";
|
||||
this->empty_field = "SHOULD_NOT_BE_USED";
|
||||
separator = "SHOULD_NOT_BE_USED";
|
||||
set_separator = "SHOULD_NOT_BE_USED";
|
||||
unset_field = "SHOULD_NOT_BE_USED";
|
||||
empty_field = "SHOULD_NOT_BE_USED";
|
||||
}
|
||||
|
||||
AsciiFormatter::SeparatorInfo::SeparatorInfo(const string & set_separator,
|
||||
const string & unset_field, const string & empty_field)
|
||||
Ascii::SeparatorInfo::SeparatorInfo(const string& arg_separator,
|
||||
const string& arg_set_separator,
|
||||
const string& arg_unset_field,
|
||||
const string& arg_empty_field)
|
||||
{
|
||||
this->set_separator = set_separator;
|
||||
this->unset_field = unset_field;
|
||||
this->empty_field = empty_field;
|
||||
separator = arg_separator;
|
||||
set_separator = arg_set_separator;
|
||||
unset_field = arg_unset_field;
|
||||
empty_field = arg_empty_field;
|
||||
}
|
||||
|
||||
AsciiFormatter::AsciiFormatter(threading::MsgThread* t, const SeparatorInfo info)
|
||||
Ascii::Ascii(threading::MsgThread* t, const SeparatorInfo& info) : Formatter(t)
|
||||
{
|
||||
thread = t;
|
||||
this->separators = info;
|
||||
separators = info;
|
||||
}
|
||||
|
||||
AsciiFormatter::~AsciiFormatter()
|
||||
Ascii::~Ascii()
|
||||
{
|
||||
}
|
||||
|
||||
bool AsciiFormatter::Describe(ODesc* desc, threading::Value* val, const string& name) const
|
||||
bool Ascii::Describe(ODesc* desc, int num_fields, const threading::Field* const * fields,
|
||||
threading::Value** vals) const
|
||||
{
|
||||
for ( int i = 0; i < num_fields; i++ )
|
||||
{
|
||||
if ( i > 0 )
|
||||
desc->AddRaw(separators.separator);
|
||||
|
||||
if ( ! Describe(desc, vals[i], fields[i]->name) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Ascii::Describe(ODesc* desc, threading::Value* val, const string& name) const
|
||||
{
|
||||
if ( ! val->present )
|
||||
{
|
||||
|
@ -103,10 +122,8 @@ bool AsciiFormatter::Describe(ODesc* desc, threading::Value* val, const string&
|
|||
// 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];
|
||||
char hex[4] = {'\\', 'x', '0', '0'};
|
||||
bytetohex(*data, hex + 2);
|
||||
desc->AddRaw(hex, 4);
|
||||
|
||||
++data;
|
||||
|
@ -128,17 +145,19 @@ bool AsciiFormatter::Describe(ODesc* desc, threading::Value* val, const string&
|
|||
}
|
||||
|
||||
desc->AddEscapeSequence(separators.set_separator);
|
||||
|
||||
for ( int j = 0; j < val->val.set_val.size; j++ )
|
||||
{
|
||||
if ( j > 0 )
|
||||
desc->AddRaw(separators.set_separator);
|
||||
|
||||
if ( ! Describe(desc, val->val.set_val.vals[j], name) )
|
||||
if ( ! Describe(desc, val->val.set_val.vals[j]) )
|
||||
{
|
||||
desc->RemoveEscapeSequence(separators.set_separator);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
desc->RemoveEscapeSequence(separators.set_separator);
|
||||
|
||||
break;
|
||||
|
@ -153,24 +172,26 @@ bool AsciiFormatter::Describe(ODesc* desc, threading::Value* val, const string&
|
|||
}
|
||||
|
||||
desc->AddEscapeSequence(separators.set_separator);
|
||||
|
||||
for ( int j = 0; j < val->val.vector_val.size; j++ )
|
||||
{
|
||||
if ( j > 0 )
|
||||
desc->AddRaw(separators.set_separator);
|
||||
|
||||
if ( ! Describe(desc, val->val.vector_val.vals[j], name) )
|
||||
if ( ! Describe(desc, val->val.vector_val.vals[j]) )
|
||||
{
|
||||
desc->RemoveEscapeSequence(separators.set_separator);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
desc->RemoveEscapeSequence(separators.set_separator);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
thread->Error(thread->Fmt("unsupported field format %d for %s", val->type, name.c_str()));
|
||||
GetThread()->Error(GetThread()->Fmt("Ascii writer unsupported field format %d", val->type));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -178,22 +199,25 @@ bool AsciiFormatter::Describe(ODesc* desc, threading::Value* val, const string&
|
|||
}
|
||||
|
||||
|
||||
threading::Value* AsciiFormatter::ParseValue(string s, string name, TypeTag type, TypeTag subtype) const
|
||||
threading::Value* Ascii::ParseValue(const string& s, const string& name, TypeTag type, TypeTag subtype) const
|
||||
{
|
||||
if ( s.compare(separators.unset_field) == 0 ) // field is not set...
|
||||
return new threading::Value(type, false);
|
||||
|
||||
threading::Value* val = new threading::Value(type, true);
|
||||
const char* start = s.c_str();
|
||||
char* end = 0;
|
||||
errno = 0;
|
||||
|
||||
switch ( type ) {
|
||||
case TYPE_ENUM:
|
||||
case TYPE_STRING:
|
||||
s = get_unescaped_string(s);
|
||||
val->val.string_val.length = s.size();
|
||||
val->val.string_val.data = copy_string(s.c_str());
|
||||
{
|
||||
string unescaped = get_unescaped_string(s);
|
||||
val->val.string_val.length = unescaped.size();
|
||||
val->val.string_val.data = copy_string(unescaped.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_BOOL:
|
||||
if ( s == "T" )
|
||||
|
@ -202,36 +226,36 @@ threading::Value* AsciiFormatter::ParseValue(string s, string name, TypeTag type
|
|||
val->val.int_val = 0;
|
||||
else
|
||||
{
|
||||
thread->Error(thread->Fmt("Field: %s Invalid value for boolean: %s",
|
||||
name.c_str(), s.c_str()));
|
||||
GetThread()->Error(GetThread()->Fmt("Field: %s Invalid value for boolean: %s",
|
||||
name.c_str(), start));
|
||||
goto parse_error;
|
||||
}
|
||||
break;
|
||||
|
||||
case TYPE_INT:
|
||||
val->val.int_val = strtoll(s.c_str(), &end, 10);
|
||||
if ( CheckNumberError(s, end) )
|
||||
val->val.int_val = strtoll(start, &end, 10);
|
||||
if ( CheckNumberError(start, end) )
|
||||
goto parse_error;
|
||||
break;
|
||||
|
||||
case TYPE_DOUBLE:
|
||||
case TYPE_TIME:
|
||||
case TYPE_INTERVAL:
|
||||
val->val.double_val = strtod(s.c_str(), &end);
|
||||
if ( CheckNumberError(s, end) )
|
||||
val->val.double_val = strtod(start, &end);
|
||||
if ( CheckNumberError(start, end) )
|
||||
goto parse_error;
|
||||
break;
|
||||
|
||||
case TYPE_COUNT:
|
||||
case TYPE_COUNTER:
|
||||
val->val.uint_val = strtoull(s.c_str(), &end, 10);
|
||||
if ( CheckNumberError(s, end) )
|
||||
val->val.uint_val = strtoull(start, &end, 10);
|
||||
if ( CheckNumberError(start, end) )
|
||||
goto parse_error;
|
||||
break;
|
||||
|
||||
case TYPE_PORT:
|
||||
val->val.port_val.port = strtoull(s.c_str(), &end, 10);
|
||||
if ( CheckNumberError(s, end) )
|
||||
val->val.port_val.port = strtoull(start, &end, 10);
|
||||
if ( CheckNumberError(start, end) )
|
||||
goto parse_error;
|
||||
|
||||
val->val.port_val.proto = TRANSPORT_UNKNOWN;
|
||||
|
@ -239,21 +263,21 @@ threading::Value* AsciiFormatter::ParseValue(string s, string name, TypeTag type
|
|||
|
||||
case TYPE_SUBNET:
|
||||
{
|
||||
s = get_unescaped_string(s);
|
||||
size_t pos = s.find("/");
|
||||
if ( pos == s.npos )
|
||||
string unescaped = get_unescaped_string(s);
|
||||
size_t pos = unescaped.find("/");
|
||||
if ( pos == unescaped.npos )
|
||||
{
|
||||
thread->Error(thread->Fmt("Invalid value for subnet: %s", s.c_str()));
|
||||
GetThread()->Error(GetThread()->Fmt("Invalid value for subnet: %s", start));
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
string width_str = s.substr(pos + 1);
|
||||
string width_str = unescaped.substr(pos + 1);
|
||||
uint8_t width = (uint8_t) strtol(width_str.c_str(), &end, 10);
|
||||
|
||||
if ( CheckNumberError(s, end) )
|
||||
if ( CheckNumberError(start, end) )
|
||||
goto parse_error;
|
||||
|
||||
string addr = s.substr(0, pos);
|
||||
string addr = unescaped.substr(0, pos);
|
||||
|
||||
val->val.subnet_val.prefix = ParseAddr(addr);
|
||||
val->val.subnet_val.length = width;
|
||||
|
@ -261,9 +285,11 @@ threading::Value* AsciiFormatter::ParseValue(string s, string name, TypeTag type
|
|||
}
|
||||
|
||||
case TYPE_ADDR:
|
||||
s = get_unescaped_string(s);
|
||||
val->val.addr_val = ParseAddr(s);
|
||||
{
|
||||
string unescaped = get_unescaped_string(s);
|
||||
val->val.addr_val = ParseAddr(unescaped);
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_TABLE:
|
||||
case TYPE_VECTOR:
|
||||
|
@ -316,7 +342,7 @@ threading::Value* AsciiFormatter::ParseValue(string s, string name, TypeTag type
|
|||
|
||||
if ( pos >= length )
|
||||
{
|
||||
thread->Error(thread->Fmt("Internal error while parsing set. pos %d >= length %d."
|
||||
GetThread()->Error(GetThread()->Fmt("Internal error while parsing set. pos %d >= length %d."
|
||||
" Element: %s", pos, length, element.c_str()));
|
||||
error = true;
|
||||
break;
|
||||
|
@ -325,7 +351,7 @@ threading::Value* AsciiFormatter::ParseValue(string s, string name, TypeTag type
|
|||
threading::Value* newval = ParseValue(element, name, subtype);
|
||||
if ( newval == 0 )
|
||||
{
|
||||
thread->Error("Error while reading set or vector");
|
||||
GetThread()->Error("Error while reading set or vector");
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
@ -343,7 +369,7 @@ threading::Value* AsciiFormatter::ParseValue(string s, string name, TypeTag type
|
|||
lvals[pos] = ParseValue("", name, subtype);
|
||||
if ( lvals[pos] == 0 )
|
||||
{
|
||||
thread->Error("Error while trying to add empty set element");
|
||||
GetThread()->Error("Error while trying to add empty set element");
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
|
@ -362,7 +388,7 @@ threading::Value* AsciiFormatter::ParseValue(string s, string name, TypeTag type
|
|||
|
||||
if ( pos != length )
|
||||
{
|
||||
thread->Error(thread->Fmt("Internal error while parsing set: did not find all elements: %s", s.c_str()));
|
||||
GetThread()->Error(GetThread()->Fmt("Internal error while parsing set: did not find all elements: %s", start));
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
|
@ -370,8 +396,8 @@ threading::Value* AsciiFormatter::ParseValue(string s, string name, TypeTag type
|
|||
}
|
||||
|
||||
default:
|
||||
thread->Error(thread->Fmt("unsupported field format %d for %s", type,
|
||||
name.c_str()));
|
||||
GetThread()->Error(GetThread()->Fmt("unsupported field format %d for %s", type,
|
||||
name.c_str()));
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
|
@ -382,128 +408,35 @@ parse_error:
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool AsciiFormatter::CheckNumberError(const string& s, const char* end) const
|
||||
bool Ascii::CheckNumberError(const char* start, const char* end) const
|
||||
{
|
||||
// Do this check first, before executing s.c_str() or similar.
|
||||
// otherwise the value to which *end is pointing at the moment might
|
||||
// be gone ...
|
||||
bool endnotnull = (*end != '\0');
|
||||
threading::MsgThread* thread = GetThread();
|
||||
|
||||
if ( s.length() == 0 )
|
||||
if ( end == start && *end != '\0' ) {
|
||||
thread->Error(thread->Fmt("String '%s' contained no parseable number", start));
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( end - start == 0 && *end == '\0' )
|
||||
{
|
||||
thread->Error("Got empty string for number field");
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( end == s.c_str() ) {
|
||||
thread->Error(thread->Fmt("String '%s' contained no parseable number", s.c_str()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( endnotnull )
|
||||
thread->Warning(thread->Fmt("Number '%s' contained non-numeric trailing characters. Ignored trailing characters '%s'", s.c_str(), end));
|
||||
if ( (*end != '\0') )
|
||||
thread->Warning(thread->Fmt("Number '%s' contained non-numeric trailing characters. Ignored trailing characters '%s'", start, end));
|
||||
|
||||
if ( errno == EINVAL )
|
||||
{
|
||||
thread->Error(thread->Fmt("String '%s' could not be converted to a number", s.c_str()));
|
||||
thread->Error(thread->Fmt("String '%s' could not be converted to a number", start));
|
||||
return true;
|
||||
}
|
||||
|
||||
else if ( errno == ERANGE )
|
||||
{
|
||||
thread->Error(thread->Fmt("Number '%s' out of supported range.", s.c_str()));
|
||||
thread->Error(thread->Fmt("Number '%s' out of supported range.", start));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
string AsciiFormatter::Render(const threading::Value::addr_t& addr) const
|
||||
{
|
||||
if ( addr.family == IPv4 )
|
||||
{
|
||||
char s[INET_ADDRSTRLEN];
|
||||
|
||||
if ( ! bro_inet_ntop(AF_INET, &addr.in.in4, s, INET_ADDRSTRLEN) )
|
||||
return "<bad IPv4 address conversion>";
|
||||
else
|
||||
return s;
|
||||
}
|
||||
else
|
||||
{
|
||||
char s[INET6_ADDRSTRLEN];
|
||||
|
||||
if ( ! bro_inet_ntop(AF_INET6, &addr.in.in6, s, INET6_ADDRSTRLEN) )
|
||||
return "<bad IPv6 address conversion>";
|
||||
else
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
TransportProto AsciiFormatter::ParseProto(const string &proto) const
|
||||
{
|
||||
if ( proto == "unknown" )
|
||||
return TRANSPORT_UNKNOWN;
|
||||
else if ( proto == "tcp" )
|
||||
return TRANSPORT_TCP;
|
||||
else if ( proto == "udp" )
|
||||
return TRANSPORT_UDP;
|
||||
else if ( proto == "icmp" )
|
||||
return TRANSPORT_ICMP;
|
||||
|
||||
thread->Error(thread->Fmt("Tried to parse invalid/unknown protocol: %s", proto.c_str()));
|
||||
|
||||
return TRANSPORT_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
// More or less verbose copy from IPAddr.cc -- which uses reporter.
|
||||
threading::Value::addr_t AsciiFormatter::ParseAddr(const string &s) const
|
||||
{
|
||||
threading::Value::addr_t val;
|
||||
|
||||
if ( s.find(':') == std::string::npos ) // IPv4.
|
||||
{
|
||||
val.family = IPv4;
|
||||
|
||||
if ( inet_aton(s.c_str(), &(val.in.in4)) <= 0 )
|
||||
{
|
||||
thread->Error(thread->Fmt("Bad address: %s", s.c_str()));
|
||||
memset(&val.in.in4.s_addr, 0, sizeof(val.in.in4.s_addr));
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
val.family = IPv6;
|
||||
if ( inet_pton(AF_INET6, s.c_str(), val.in.in6.s6_addr) <=0 )
|
||||
{
|
||||
thread->Error(thread->Fmt("Bad address: %s", s.c_str()));
|
||||
memset(val.in.in6.s6_addr, 0, sizeof(val.in.in6.s6_addr));
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
string AsciiFormatter::Render(const threading::Value::subnet_t& subnet) const
|
||||
{
|
||||
char l[16];
|
||||
|
||||
if ( subnet.prefix.family == IPv4 )
|
||||
modp_uitoa10(subnet.length - 96, l);
|
||||
else
|
||||
modp_uitoa10(subnet.length, l);
|
||||
|
||||
string s = Render(subnet.prefix) + "/" + l;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
string AsciiFormatter::Render(double d) const
|
||||
{
|
||||
char buf[256];
|
||||
modp_dtoa(d, buf, 6);
|
||||
return buf;
|
||||
}
|
||||
|
63
src/threading/formatters/Ascii.h
Normal file
63
src/threading/formatters/Ascii.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef THREADING_FORMATTERS_ASCII_H
|
||||
#define THREADING_FORMATTERS_ASCII_H
|
||||
|
||||
#include "../Formatter.h"
|
||||
|
||||
namespace threading { namespace formatter {
|
||||
|
||||
class Ascii : public Formatter {
|
||||
public:
|
||||
/**
|
||||
* A struct to pass the necessary configuration values to the
|
||||
* Ascii module on initialization.
|
||||
*/
|
||||
struct SeparatorInfo
|
||||
{
|
||||
string separator; // Separator between columns
|
||||
string set_separator; // Separator between set elements.
|
||||
string unset_field; // String marking an unset field.
|
||||
string empty_field; // String marking an empty (but set) field.
|
||||
|
||||
/**
|
||||
* Constructor that defines all the configuration options.
|
||||
* Use if you need either ValToODesc or EntryToVal.
|
||||
*/
|
||||
SeparatorInfo(const string& separator, const string& set_separator, const string& unset_field, const string& empty_field);
|
||||
|
||||
/**
|
||||
* Constructor that leaves separators etc unset to dummy
|
||||
* values. Useful if you use only methods that don't need any
|
||||
* of them, like StringToAddr, etc.
|
||||
*/
|
||||
SeparatorInfo();
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param t The thread that uses this class instance. The class uses
|
||||
* some of the thread's methods, e.g., for error reporting and
|
||||
* internal formatting.
|
||||
*
|
||||
* @param info SeparatorInfo structure defining the necessary
|
||||
* separators.
|
||||
*/
|
||||
Ascii(threading::MsgThread* t, const SeparatorInfo& info);
|
||||
virtual ~Ascii();
|
||||
|
||||
virtual bool Describe(ODesc* desc, threading::Value* val, const string& name = "") const;
|
||||
virtual bool Describe(ODesc* desc, int num_fields, const threading::Field* const * fields,
|
||||
threading::Value** vals) const;
|
||||
virtual threading::Value* ParseValue(const string& s, const string& name, TypeTag type, TypeTag subtype = TYPE_ERROR) const;
|
||||
|
||||
private:
|
||||
bool CheckNumberError(const char* start, const char* end) const;
|
||||
|
||||
SeparatorInfo separators;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* THREADING_FORMATTERS_ASCII_H */
|
219
src/threading/formatters/JSON.cc
Normal file
219
src/threading/formatters/JSON.cc
Normal file
|
@ -0,0 +1,219 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifndef __STDC_LIMIT_MACROS
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "./JSON.h"
|
||||
|
||||
using namespace threading::formatter;
|
||||
|
||||
JSON::JSON(MsgThread* t, TimeFormat tf) : Formatter(t)
|
||||
{
|
||||
timestamps = tf;
|
||||
}
|
||||
|
||||
JSON::~JSON()
|
||||
{
|
||||
}
|
||||
|
||||
bool JSON::Describe(ODesc* desc, int num_fields, const Field* const * fields,
|
||||
Value** vals) const
|
||||
{
|
||||
desc->AddRaw("{");
|
||||
|
||||
for ( int i = 0; i < num_fields; i++ )
|
||||
{
|
||||
const u_char* bytes = desc->Bytes();
|
||||
int len = desc->Len();
|
||||
|
||||
if ( i > 0 && len > 0 && bytes[len-1] != ',' && vals[i]->present )
|
||||
desc->AddRaw(",");
|
||||
|
||||
if ( ! Describe(desc, vals[i], fields[i]->name) )
|
||||
return false;
|
||||
}
|
||||
|
||||
desc->AddRaw("}");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSON::Describe(ODesc* desc, Value* val, const string& name) const
|
||||
{
|
||||
if ( ! val->present )
|
||||
return true;
|
||||
|
||||
if ( name.size() )
|
||||
{
|
||||
desc->AddRaw("\"", 1);
|
||||
desc->Add(name);
|
||||
desc->AddRaw("\":", 2);
|
||||
}
|
||||
|
||||
switch ( val->type )
|
||||
{
|
||||
case TYPE_BOOL:
|
||||
desc->AddRaw(val->val.int_val == 0 ? "false" : "true");
|
||||
break;
|
||||
|
||||
case TYPE_INT:
|
||||
desc->Add(val->val.int_val);
|
||||
break;
|
||||
|
||||
case TYPE_COUNT:
|
||||
case TYPE_COUNTER:
|
||||
{
|
||||
// JSON doesn't support unsigned 64bit ints.
|
||||
if ( val->val.uint_val >= INT64_MAX )
|
||||
{
|
||||
GetThread()->Error(GetThread()->Fmt("count value too large for JSON: %" PRIu64, val->val.uint_val));
|
||||
desc->AddRaw("null", 4);
|
||||
}
|
||||
else
|
||||
desc->Add(val->val.uint_val);
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_PORT:
|
||||
desc->Add(val->val.port_val.port);
|
||||
break;
|
||||
|
||||
case TYPE_SUBNET:
|
||||
desc->AddRaw("\"", 1);
|
||||
desc->Add(Render(val->val.subnet_val));
|
||||
desc->AddRaw("\"", 1);
|
||||
break;
|
||||
|
||||
case TYPE_ADDR:
|
||||
desc->AddRaw("\"", 1);
|
||||
desc->Add(Render(val->val.addr_val));
|
||||
desc->AddRaw("\"", 1);
|
||||
break;
|
||||
|
||||
case TYPE_DOUBLE:
|
||||
case TYPE_INTERVAL:
|
||||
desc->Add(val->val.double_val);
|
||||
break;
|
||||
|
||||
case TYPE_TIME:
|
||||
{
|
||||
if ( timestamps == TS_ISO8601 )
|
||||
{
|
||||
char buffer[40];
|
||||
char buffer2[40];
|
||||
time_t t = time_t(val->val.double_val);
|
||||
|
||||
if ( strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S", gmtime(&t)) > 0 )
|
||||
{
|
||||
double integ;
|
||||
double frac = modf(val->val.double_val, &integ);
|
||||
snprintf(buffer2, sizeof(buffer2), "%s.%06.0fZ", buffer, frac * 1000000);
|
||||
desc->AddRaw("\"", 1);
|
||||
desc->Add(buffer2);
|
||||
desc->AddRaw("\"", 1);
|
||||
}
|
||||
|
||||
else
|
||||
GetThread()->Error(GetThread()->Fmt("strftime error for JSON: %" PRIu64));
|
||||
|
||||
}
|
||||
|
||||
else if ( timestamps == TS_EPOCH )
|
||||
desc->Add(val->val.double_val);
|
||||
|
||||
else if ( timestamps == TS_MILLIS )
|
||||
{
|
||||
// ElasticSearch uses milliseconds for timestamps and json only
|
||||
// supports signed ints (uints can be too large).
|
||||
uint64_t ts = (uint64_t) (val->val.double_val * 1000);
|
||||
if ( ts < INT64_MAX )
|
||||
desc->Add(ts);
|
||||
else
|
||||
{
|
||||
GetThread()->Error(GetThread()->Fmt("time value too large for JSON milliseconds: %" PRIu64, ts));
|
||||
desc->AddRaw("null", 4);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_ENUM:
|
||||
case TYPE_STRING:
|
||||
case TYPE_FILE:
|
||||
case TYPE_FUNC:
|
||||
{
|
||||
desc->AddRaw("\"", 1);
|
||||
|
||||
for ( int i = 0; i < val->val.string_val.length; ++i )
|
||||
{
|
||||
char c = val->val.string_val.data[i];
|
||||
|
||||
// 2byte Unicode escape special characters.
|
||||
if ( c < 32 || c > 126 || c == '\n' || c == '"' || c == '\'' || c == '\\' || c == '&' )
|
||||
{
|
||||
desc->AddRaw("\\u00", 4);
|
||||
char hex[2] = {'0', '0'};
|
||||
bytetohex(c, hex);
|
||||
desc->AddRaw(hex, 1);
|
||||
desc->AddRaw(hex + 1, 1);
|
||||
}
|
||||
else
|
||||
desc->AddRaw(&c, 1);
|
||||
}
|
||||
|
||||
desc->AddRaw("\"", 1);
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_TABLE:
|
||||
{
|
||||
desc->AddRaw("[", 1);
|
||||
|
||||
for ( int j = 0; j < val->val.set_val.size; j++ )
|
||||
{
|
||||
if ( j > 0 )
|
||||
desc->AddRaw(",", 1);
|
||||
|
||||
Describe(desc, val->val.set_val.vals[j]);
|
||||
}
|
||||
|
||||
desc->AddRaw("]", 1);
|
||||
break;
|
||||
}
|
||||
|
||||
case TYPE_VECTOR:
|
||||
{
|
||||
desc->AddRaw("[", 1);
|
||||
|
||||
for ( int j = 0; j < val->val.vector_val.size; j++ )
|
||||
{
|
||||
if ( j > 0 )
|
||||
desc->AddRaw(",", 1);
|
||||
Describe(desc, val->val.vector_val.vals[j]);
|
||||
}
|
||||
|
||||
desc->AddRaw("]", 1);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
threading::Value* JSON::ParseValue(const string& s, const string& name, TypeTag type, TypeTag subtype) const
|
||||
{
|
||||
GetThread()->Error("JSON formatter does not support parsing yet.");
|
||||
return NULL;
|
||||
}
|
36
src/threading/formatters/JSON.h
Normal file
36
src/threading/formatters/JSON.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef THREADING_FORMATTERS_JSON_H
|
||||
#define THREADING_FORMATTERS_JSON_H
|
||||
|
||||
#include "../Formatter.h"
|
||||
|
||||
namespace threading { namespace formatter {
|
||||
|
||||
/**
|
||||
* A thread-safe class for converting values into a JSON representation
|
||||
* and vice versa.
|
||||
*/
|
||||
class JSON : public Formatter {
|
||||
public:
|
||||
enum TimeFormat {
|
||||
TS_EPOCH, // Doubles that represents seconds from the UNIX epoch.
|
||||
TS_ISO8601, // ISO 8601 defined human readable timestamp format.
|
||||
TS_MILLIS // Milliseconds from the UNIX epoch. Some consumers need this (e.g., elasticsearch).
|
||||
};
|
||||
|
||||
JSON(threading::MsgThread* t, TimeFormat tf);
|
||||
virtual ~JSON();
|
||||
|
||||
virtual bool Describe(ODesc* desc, threading::Value* val, const string& name = "") const;
|
||||
virtual bool Describe(ODesc* desc, int num_fields, const threading::Field* const * fields,
|
||||
threading::Value** vals) const;
|
||||
virtual threading::Value* ParseValue(const string& s, const string& name, TypeTag type, TypeTag subtype = TYPE_ERROR) const;
|
||||
|
||||
private:
|
||||
TimeFormat timestamps;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* THREADING_FORMATTERS_JSON_H */
|
Loading…
Add table
Add a link
Reference in a new issue