zeek/src/BroString.cc
Robin Sommer 93894eed9b Overhauling the internal reporting of messages to the user.
The Logger class is now in charge of reporting all errors, warnings,
informational messages, weirds, and syslogs. All other components
route their messages through the global bro_logger singleton.

The Logger class comes with these reporting methods:

    void Message(const char* fmt, ...);
    void Warning(const char* fmt, ...);
    void Error(const char* fmt, ...);
    void FatalError(const char* fmt, ...); // Terminate Bro.
    void Weird(const char* name);
    [ .. some more Weird() variants ... ]
    void Syslog(const char* fmt, ...);
    void InternalWarning(const char* fmt, ...);
    void InternalError(const char* fmt, ...); // Terminates Bro.

See Logger.h for more information on these.

Generally, the reporting now works as follows:

    - All non-fatal message are reported in one of two ways:

        (1) At startup (i.e., before we start processing packets),
            they are logged to stderr.

        (2) During processing, they turn into events:

            event log_message%(msg: string, location: string%);
            event log_warning%(msg: string, location: string%);
            event log_error%(msg: string, location: string%);

            The script level can then handle them as desired.

            If we don't have an event handler, we fall back to
            reporting on stderr.

    - All fatal errors are logged to stderr and Bro terminates
      immediately.

    - Syslog(msg) directly syslogs, but doesn't do anything else.

The three main types of messages can also be generated on the
scripting layer via new Log::* bifs:

    Log::error(msg: string);
    Log::warning(msg: string);
    Log::message(msg: string);

These pass through the bro_logger as well and thus are handled in the
same way. Their output includes location information.

More changes:

    - Removed the alarm statement and the alarm_hook event.

    - Adapted lots of locations to use the bro_logger, including some
      of the messages that were previously either just written to
      stdout, or even funneled through the alarm mechanism.

    - No distinction anymore between Error() and RunTime(). There's
      now only one class of errors; the line was quite blurred already
      anyway.

    - util.h: all the error()/warn()/message()/run_time()/pinpoint()
      functions are gone. Use the bro_logger instead now.

    - Script errors are formatted a bit differently due to the
      changes. What I've seen so far looks ok to me, but let me know
      if there's something odd.

Notes:

    - The default handlers for the new log_* events are just dummy
      implementations for now since we need to integrate all this into
      the new scripts anyway.

    - I'm not too happy with the names of the Logger class and its
      instance bro_logger. We now have a LogMgr as well, which makes
      this all a bit confusing. But I didn't have a good idea for
      better names so I stuck with them for now.

      Perhaps we should merge Logger and LogMgr?
2011-06-25 16:40:54 -07:00

510 lines
9.2 KiB
C++

// $Id: BroString.cc 6219 2008-10-01 05:39:07Z vern $
//
// See the file "COPYING" in the main distribution directory for copyright.
#include "config.h"
#include <algorithm>
#include <ctype.h>
#include <algorithm>
#include "BroString.h"
#include "Var.h"
#include "Logger.h"
#ifdef DEBUG
#define DEBUG_STR(msg) DBG_LOG(DBG_STRING, msg)
#else
#define DEBUG_STR(msg)
#endif
const int BroString::EXPANDED_STRING;
const int BroString::BRO_STRING_LITERAL;
// This constructor forces the user to specify arg_final_NUL. When str
// is a *normal* NUL-terminated string, make arg_n == strlen(str) and
// arg_final_NUL == 1; when str is a sequence of n bytes, make
// arg_final_NUL == 0.
BroString::BroString(int arg_final_NUL, byte_vec str, int arg_n)
{
b = str;
n = arg_n;
final_NUL = arg_final_NUL;
use_free_to_delete = 0;
}
BroString::BroString(const u_char* str, int arg_n, int add_NUL)
{
b = 0;
n = 0;
use_free_to_delete = 0;
Set(str, arg_n, add_NUL);
}
BroString::BroString(const char* str)
{
b = 0;
n = 0;
use_free_to_delete = 0;
Set(str);
}
BroString::BroString(const string &str)
{
b = 0;
n = 0;
use_free_to_delete = 0;
Set(str);
}
BroString::BroString(const BroString& bs)
{
b = 0;
n = 0;
use_free_to_delete = 0;
*this = bs;
}
BroString::BroString()
{
b = 0;
n = 0;
final_NUL = 0;
use_free_to_delete = 0;
}
void BroString::Reset()
{
if ( use_free_to_delete )
free(b);
else
delete [] b;
b = 0;
n = 0;
final_NUL = 0;
use_free_to_delete = 0;
}
const BroString& BroString::operator=(const BroString &bs)
{
Reset();
n = bs.n;
b = new u_char[n+1];
memcpy(b, bs.b, n);
b[n] = '\0';
final_NUL = 1;
use_free_to_delete = 0;
return *this;
}
bool BroString::operator==(const BroString &bs) const
{
return Bstr_eq(this, &bs);
}
bool BroString::operator<(const BroString &bs) const
{
return Bstr_cmp(this, &bs) < 0;
}
void BroString::Adopt(byte_vec bytes, int len)
{
Reset();
b = bytes;
// Check if the string ends with a NUL. If so, mark it as having
// a final NUL and adjust the length accordingly.
final_NUL = (b[len-1] == '\0') ? 1 : 0;
n = len - final_NUL;
}
void BroString::Set(const u_char* str, int len, int add_NUL)
{
Reset();
n = len;
b = new u_char[add_NUL ? n + 1 : n];
memcpy(b, str, n);
final_NUL = add_NUL;
if ( add_NUL )
b[n] = 0;
use_free_to_delete = 0;
}
void BroString::Set(const char* str)
{
Reset();
n = strlen(str);
b = new u_char[n+1];
memcpy(b, str, n+1);
final_NUL = 1;
use_free_to_delete = 0;
}
void BroString::Set(const string& str)
{
Reset();
n = str.size();
b = new u_char[n+1];
memcpy(b, str.c_str(), n+1);
final_NUL = 1;
use_free_to_delete = 0;
}
void BroString::Set(const BroString& str)
{
*this = str;
}
const char* BroString::CheckString() const
{
if ( n == 0 )
return "";
if ( memchr(b, '\0', n + final_NUL) != &b[n] )
{
// Either an embedded NUL, or no final NUL.
char* exp_s = Render();
if ( b[n-1] != '\0' )
bro_logger->Error("string without NUL terminator: \"%s\"", exp_s);
else
bro_logger->Error("string with embedded NUL: \"%s\"", exp_s);
delete [] exp_s;
return "<string-with-NUL>";
}
return (const char*) b;
}
char* BroString::Render(int format, int* len) const
{
// Maxmimum character expansion is as \xHH, so a factor of 4.
char* s = new char[n*4 + 1]; // +1 is for final '\0'
char* sp = s;
int tmp_len;
for ( int i = 0; i < n; ++i )
{
if ( b[i] == '\0' && (format & ESC_NULL) )
{
*sp++ = '\\'; *sp++ = '0';
}
else if ( b[i] == '\x7f' && (format & ESC_DEL) )
{
*sp++ = '^'; *sp++ = '?';
}
else if ( b[i] <= 26 && (format & ESC_LOW) )
{
*sp++ = '^'; *sp++ = b[i] + 'A' - 1;
}
else if ( b[i] == '\\' && (format & ESC_ESC) )
{
*sp++ = '\\'; *sp++ = '\\';
}
else if ( (b[i] == '\'' || b[i] == '"') && (format & ESC_QUOT) )
{
*sp++ = '\\'; *sp++ = b[i];
}
else if ( (b[i] < ' ' || b[i] > 126) && (format & ESC_HEX) )
{
char hex_fmt[16];
*sp++ = '\\'; *sp++ = 'x';
sprintf(hex_fmt, "%02x", b[i]);
*sp++ = hex_fmt[0]; *sp++ = hex_fmt[1];
}
else if ( (b[i] < ' ' || b[i] > 126) && (format & ESC_DOT) )
{
*sp++ = '.';
}
else
{
*sp++ = b[i];
}
}
*sp++ = '\0'; // NUL-terminate.
tmp_len = sp - s;
if ( (format & ESC_SER) )
{
char* result = new char[tmp_len + 16];
snprintf(result, tmp_len + 16, "%u ", tmp_len - 1);
tmp_len += strlen(result);
memcpy(result + strlen(result), s, sp - s);
delete [] s;
s = result;
}
if ( len )
*len = tmp_len;
return s;
}
ostream& BroString::Render(ostream &os, int format) const
{
char* tmp = Render(format);
os << tmp;
delete [] tmp;
return os;
}
istream& BroString::Read(istream &is, int format)
{
if ( (format & BroString::ESC_SER) )
{
int len;
is >> len; // Get the length of the string
char c;
is.read(&c, 1); // Eat single whitespace
char* buf = new char[len+1];
is.read(buf, len);
buf[len] = '\0'; // NUL-terminate just for safety
Adopt((u_char*) buf, len+1);
}
else
{
string str;
is >> str;
Set(str);
}
return is;
}
void BroString::ToUpper()
{
for ( int i = 0; i < n; ++i )
if ( islower(b[i]) )
b[i] = toupper(b[i]);
}
BroString* BroString::GetSubstring(int start, int len) const
{
// This code used to live in bro.bif's sub_bytes() routine.
if ( start < 0 || start > n )
return 0;
if ( len < 0 || len > n - start )
len = n - start;
return new BroString(&b[start], len, 1);
}
int BroString::FindSubstring(const BroString* s) const
{
return strstr_n(n, b, s->Len(), s->Bytes());
}
BroString::Vec* BroString::Split(const BroString::IdxVec& indices) const
{
unsigned int i;
if ( indices.size() == 0 )
return 0;
// Copy input, ensuring space for "0":
IdxVec idx(1 + indices.size());
idx[0] = 0;
idx.insert(idx.end(), indices.begin(), indices.end());
// Sanity checks.
for ( i = 0; i < idx.size(); ++i )
if ( idx[i] >= n || idx[i] < 0 )
idx[i] = 0;
// Sort it:
sort(idx.begin(), idx.end());
// Shuffle vector so duplicate entries are used only once:
IdxVecIt end = unique(idx.begin(), idx.end());
// Each element in idx is now the start index of a new
// substring, and we know that all indices are within [0, n].
//
Vec* result = new Vec();
int last_idx = -1;
int next_idx;
i = 0;
for ( IdxVecIt it = idx.begin(); it != end; ++it, ++i )
{
int len = (it + 1 == end) ? -1 : idx[i + 1] - idx[i];
result->push_back(GetSubstring(idx[i], len));
}
return result;
}
VectorVal* BroString:: VecToPolicy(Vec* vec)
{
VectorVal* result =
new VectorVal(internal_type("string_vec")->AsVectorType());
if ( ! result )
return 0;
for ( unsigned int i = 0; i < vec->size(); ++i )
{
BroString* string = (*vec)[i];
StringVal* val = new StringVal(string->Len(),
(const char*) string->Bytes());
result->Assign(i+1, val, 0);
}
return result;
}
BroString::Vec* BroString::VecFromPolicy(VectorVal* vec)
{
Vec* result = new Vec();
// VectorVals start at index 1!
for ( unsigned int i = 1; i <= vec->Size(); ++i )
{
Val* v = vec->Lookup(i); // get the RecordVal
if ( ! v )
continue;
BroString* string = new BroString(*(v->AsString()));
result->push_back(string);
}
return result;
}
char* BroString::VecToString(const Vec* vec)
{
string result("[");
for ( BroString::VecCIt it = vec->begin(); it != vec->end(); ++it )
{
result += (*it)->CheckString();
result += ",";
}
result += "]";
return strdup(result.c_str());
}
bool BroStringLenCmp::operator()(BroString * const& bst1,
BroString * const& bst2)
{
return _increasing ? (bst1->Len() < bst2->Len()) :
(bst1->Len() > bst2->Len());
}
ostream& operator<<(ostream& os, const BroString& bs)
{
char* tmp = bs.Render(BroString::EXPANDED_STRING);
os << tmp;
delete [] tmp;
return os;
}
int Bstr_eq(const BroString* s1, const BroString* s2)
{
if ( s1->Len() != s2->Len() )
return 0;
return memcmp(s1->Bytes(), s2->Bytes(), s1->Len()) == 0;
}
int Bstr_cmp(const BroString* s1, const BroString* s2)
{
int n = min(s1->Len(), s2->Len());
int cmp = memcmp(s1->Bytes(), s2->Bytes(), n);
if ( cmp || s1->Len() == s2->Len() )
return cmp;
// Compared equal, but one was shorter than the other. Treat
// it as less than the other.
if ( s1->Len() < s2->Len() )
return -1;
else
return 1;
}
BroString* concatenate(std::vector<data_chunk_t>& v)
{
int n = v.size();
int len = 0;
int i;
for ( i = 0; i < n; ++i )
len += v[i].length;
char* data = new char[len+1];
char* b = data;
for ( i = 0; i < n; ++i )
{
memcpy(b, v[i].data, v[i].length);
b += v[i].length;
}
*b = '\0';
return new BroString(1, (byte_vec) data, len);
}
BroString* concatenate(BroString::CVec& v)
{
int n = v.size();
int len = 0;
int i;
for ( i = 0; i < n; ++i )
len += v[i]->Len();
char* data = new char[len+1];
char* b = data;
for ( i = 0; i < n; ++i )
{
memcpy(b, v[i]->Bytes(), v[i]->Len());
b += v[i]->Len();
}
*b = '\0';
return new BroString(1, (byte_vec) data, len);
}
BroString* concatenate(BroString::Vec& v)
{
BroString::CVec cv;
for ( BroString::VecIt it = v.begin(); it != v.end(); ++it )
cv.push_back(*it);
return concatenate(cv);
}
void delete_strings(std::vector<const BroString*>& v)
{
for ( unsigned int i = 0; i < v.size(); ++i )
delete v[i];
v.clear();
}