diff --git a/src/Desc.cc b/src/Desc.cc index baf3ad1160..b73f8ca8cd 100644 --- a/src/Desc.cc +++ b/src/Desc.cc @@ -41,6 +41,8 @@ ODesc::ODesc(desc_type t, BroFile* arg_f) want_quotes = 0; do_flush = 1; include_stats = 0; + escape = 0; + escape_len = 0; } ODesc::~ODesc() @@ -54,6 +56,12 @@ ODesc::~ODesc() free(base); } +void ODesc::SetEscape(const char* arg_escape, int len) + { + escape = arg_escape; + escape_len = len; + } + void ODesc::PushIndent() { ++indent_level; @@ -183,8 +191,44 @@ void ODesc::Indent() Add("\t", 0); } +static const char hex_chars[] = "0123456789ABCDEF"; void ODesc::AddBytes(const void* bytes, unsigned int n) + { + if ( ! escape ) + return AddBytesRaw(bytes, n); + + const char* s = (const char*) bytes; + const char* e = (const char*) bytes + n; + + while ( s < e ) + { + const char* t = (const char*) memchr(s, escape[0], e - s); + + if ( ! t ) + break; + + if ( memcmp(t, escape, escape_len) != 0 ) + break; + + AddBytesRaw(s, t - s); + + for ( int i = 0; i < escape_len; ++i ) + { + char hex[5] = "\\x00"; + hex[2] = hex_chars[(*t) >> 4]; + hex[3] = hex_chars[(*t) & 0x0f]; + AddBytesRaw(hex, sizeof(hex)); + ++t; + } + + s = t; + } + + AddBytesRaw(s, e - s); + } + +void ODesc::AddBytesRaw(const void* bytes, unsigned int n) { if ( n == 0 ) return; diff --git a/src/Desc.h b/src/Desc.h index 49b331d51b..55891223cc 100644 --- a/src/Desc.h +++ b/src/Desc.h @@ -49,6 +49,9 @@ 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 PushIndent(); void PopIndent(); int GetIndentLevel() const { return indent_level; } @@ -93,6 +96,9 @@ public: Add("\n", 0); } + // Bypasses the escaping enabled via SetEscape(). + void AddRaw(const char* s, int len) { AddBytesRaw(s, len); } + // Returns the description as a string. const char* Description() const { return (const char*) base; } @@ -115,6 +121,7 @@ protected: void Indent(); void AddBytes(const void* bytes, unsigned int n); + void AddBytesRaw(const void* bytes, unsigned int n); // Make buffer big enough for n bytes beyond bufp. void Grow(unsigned int n); @@ -128,6 +135,9 @@ 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 + BroFile* f; // or the file we're using. int indent_level; diff --git a/src/LogWriterAscii.cc b/src/LogWriterAscii.cc index d5e550aa91..050616efd4 100644 --- a/src/LogWriterAscii.cc +++ b/src/LogWriterAscii.cc @@ -11,7 +11,10 @@ LogWriterAscii::LogWriterAscii() output_to_stdout = BifConst::LogAscii::output_to_stdout; include_header = BifConst::LogAscii::include_header; - separator = strdup(BifConst::LogAscii::separator->CheckString()); + + separator_len = BifConst::LogAscii::separator->Len(); + separator = new char[separator_len]; + memcpy(separator, BifConst::LogAscii::separator->Bytes(), separator_len); } LogWriterAscii::~LogWriterAscii() @@ -19,7 +22,7 @@ LogWriterAscii::~LogWriterAscii() if ( file ) fclose(file); - free(separator); + delete [] separator; } bool LogWriterAscii::DoInit(string path, int num_fields, const LogField* const * fields) @@ -46,7 +49,7 @@ bool LogWriterAscii::DoInit(string path, int num_fields, const LogField* const * if ( fputs(field->name.c_str(), file) == EOF ) goto write_error; - if ( fputs(separator, file) == EOF ) + if ( fwrite(separator, separator_len, 1, file) != 1 ) goto write_error; } @@ -74,11 +77,12 @@ void LogWriterAscii::DoFinish() bool LogWriterAscii::DoWrite(int num_fields, const LogField* const * fields, LogVal** vals) { ODesc desc(DESC_READABLE); + desc.SetEscape(separator, separator_len); for ( int i = 0; i < num_fields; i++ ) { if ( i > 0 ) - desc.Add(separator); + desc.AddRaw(separator, separator_len); LogVal* val = vals[i]; const LogField* field = fields[i]; diff --git a/src/LogWriterAscii.h b/src/LogWriterAscii.h index 0f42874da6..04968e1a8b 100644 --- a/src/LogWriterAscii.h +++ b/src/LogWriterAscii.h @@ -32,6 +32,7 @@ private: bool output_to_stdout; bool include_header; char* separator; + int separator_len; }; #endif diff --git a/testing/btest/Baseline/logging.ascii-escape/ssh.log b/testing/btest/Baseline/logging.ascii-escape/ssh.log new file mode 100644 index 0000000000..9892e1aeee Binary files /dev/null and b/testing/btest/Baseline/logging.ascii-escape/ssh.log differ diff --git a/testing/btest/logging/ascii-escape.bro b/testing/btest/logging/ascii-escape.bro new file mode 100644 index 0000000000..1789c1bb63 --- /dev/null +++ b/testing/btest/logging/ascii-escape.bro @@ -0,0 +1,33 @@ +# +# @TEST-EXEC: bro %INPUT +# @TEST-EXEC: btest-diff ssh.log + +redef LogAscii::separator = "||"; + +module SSH; + +export { + redef enum Log::ID += { SSH }; + + type Log: record { + t: time; + id: conn_id; # Will be rolled out into individual columns. + status: string &optional; + country: string &default="unknown"; + }; +} + +event bro_init() +{ + Log::create_stream(SSH, [$columns=Log]); + + local cid = [$orig_h=1.2.3.4, $orig_p=1234/tcp, $resp_h=2.3.4.5, $resp_p=80/tcp]; + + Log::write(SSH, [$t=network_time(), $id=cid, $status="success"]); + Log::write(SSH, [$t=network_time(), $id=cid, $status="failure", $country="US"]); + Log::write(SSH, [$t=network_time(), $id=cid, $status="fa||ure", $country="UK"]); + Log::write(SSH, [$t=network_time(), $id=cid, $status="su||ess", $country="BR"]); + Log::write(SSH, [$t=network_time(), $id=cid, $status="failure", $country="MX"]); + +} +