From 0b78f444a111fa8de2b913c81caa962a400f3929 Mon Sep 17 00:00:00 2001 From: Seth Hall Date: Fri, 20 Dec 2013 00:05:08 -0500 Subject: [PATCH 001/299] Initial commit of file reassembly. --- scripts/base/init-bare.bro | 2 +- src/Frag.cc | 2 +- src/Reassem.cc | 2 +- src/Reassem.h | 3 +- src/analyzer/protocol/tcp/TCP_Reassembler.cc | 2 +- src/file_analysis/CMakeLists.txt | 1 + src/file_analysis/File.cc | 85 ++++++++++++++++--- src/file_analysis/File.h | 5 ++ src/file_analysis/FileReassembler.cc | 58 +++++++++++++ src/file_analysis/FileReassembler.h | 45 ++++++++++ .../a.out | 3 + 11 files changed, 189 insertions(+), 19 deletions(-) create mode 100644 src/file_analysis/FileReassembler.cc create mode 100644 src/file_analysis/FileReassembler.h diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 9f8c9f42ac..c58559bd9a 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -356,7 +356,7 @@ type fa_file: record { missing_bytes: count &default=0; ## The number of not all-in-sequence bytes in the file stream that - ## were delivered to file analyzers due to reassembly buffer overflow. + ## were not delivered to file analyzers due to reassembly buffer overflow. overflow_bytes: count &default=0; ## The amount of time between receiving new data for this file that diff --git a/src/Frag.cc b/src/Frag.cc index b1efb41594..4669471227 100644 --- a/src/Frag.cc +++ b/src/Frag.cc @@ -28,7 +28,7 @@ void FragTimer::Dispatch(double t, int /* is_expire */) FragReassembler::FragReassembler(NetSessions* arg_s, const IP_Hdr* ip, const u_char* pkt, HashKey* k, double t) - : Reassembler(0, REASSEM_IP) + : Reassembler(0) { s = arg_s; key = k; diff --git a/src/Reassem.cc b/src/Reassem.cc index 19beaa0a16..e2664b59b9 100644 --- a/src/Reassem.cc +++ b/src/Reassem.cc @@ -40,7 +40,7 @@ DataBlock::DataBlock(const u_char* data, int size, int arg_seq, unsigned int Reassembler::total_size = 0; -Reassembler::Reassembler(int init_seq, ReassemblerType arg_type) +Reassembler::Reassembler(int init_seq) { blocks = last_block = 0; trim_seq = last_reassem_seq = init_seq; diff --git a/src/Reassem.h b/src/Reassem.h index 1f65059e02..d9dd7d72e5 100644 --- a/src/Reassem.h +++ b/src/Reassem.h @@ -22,11 +22,10 @@ public: }; -enum ReassemblerType { REASSEM_IP, REASSEM_TCP }; class Reassembler : public BroObj { public: - Reassembler(int init_seq, ReassemblerType arg_type); + Reassembler(int init_seq); virtual ~Reassembler(); void NewBlock(double t, int seq, int len, const u_char* data); diff --git a/src/analyzer/protocol/tcp/TCP_Reassembler.cc b/src/analyzer/protocol/tcp/TCP_Reassembler.cc index a1e20dc0e6..06c9c06e6c 100644 --- a/src/analyzer/protocol/tcp/TCP_Reassembler.cc +++ b/src/analyzer/protocol/tcp/TCP_Reassembler.cc @@ -33,7 +33,7 @@ TCP_Reassembler::TCP_Reassembler(analyzer::Analyzer* arg_dst_analyzer, TCP_Analyzer* arg_tcp_analyzer, TCP_Reassembler::Type arg_type, bool arg_is_orig, TCP_Endpoint* arg_endp) - : Reassembler(1, REASSEM_TCP) + : Reassembler(1) { dst_analyzer = arg_dst_analyzer; tcp_analyzer = arg_tcp_analyzer; diff --git a/src/file_analysis/CMakeLists.txt b/src/file_analysis/CMakeLists.txt index 846fc4bf15..34dc8d5387 100644 --- a/src/file_analysis/CMakeLists.txt +++ b/src/file_analysis/CMakeLists.txt @@ -11,6 +11,7 @@ set(file_analysis_SRCS Manager.cc File.cc FileTimer.cc + FileReassembler.cc Analyzer.cc AnalyzerSet.cc Component.cc diff --git a/src/file_analysis/File.cc b/src/file_analysis/File.cc index 55b28763c8..d53c45fe06 100644 --- a/src/file_analysis/File.cc +++ b/src/file_analysis/File.cc @@ -3,6 +3,7 @@ #include #include "File.h" +#include "FileReassembler.h" #include "FileTimer.h" #include "Analyzer.h" #include "Manager.h" @@ -87,6 +88,8 @@ File::File(const string& file_id, Connection* conn, analyzer::Tag tag, val = new RecordVal(fa_file_type); val->Assign(id_idx, new StringVal(file_id.c_str())); + forwarded_offset = 0; + file_reassembler = 0; if ( conn ) { // add source, connection, is_orig fields @@ -109,6 +112,9 @@ File::~File() delete_vals(fonc_queue.front().second); fonc_queue.pop(); } + + if ( file_reassembler ) + delete file_reassembler; } void File::UpdateLastActivityTime() @@ -325,32 +331,85 @@ void File::DataIn(const u_char* data, uint64 len, uint64 offset) { analyzers.DrainModifications(); + if ( file_reassembler ) + { + // If there is a file reassembler we must forward any data there. + // But this only happens if the incoming data doesn't happen + // to align with the current forwarded_offset + file_reassembler->NewBlock(network_time, offset, len, data); + + if ( !file_reassembler->HasBlocks() ) + { + delete file_reassembler; + file_reassembler = 0; + } + } + else if ( forwarded_offset == offset ) + { + // This is the normal case where a file is transferred linearly. + // Nothing should be done here. + } + else if ( forwarded_offset > offset && forwarded_offset < offset+len ) + { + // This is a segment that begins before the forwarded_offset + // but proceeds past the forwarded_offset. It needs + // trimmed but the reassembler is not enabled. + uint64 adjustment = forwarded_offset - offset; + data = data + adjustment; + len = len - adjustment; + offset = forwarded_offset; + IncrementByteCount(adjustment, overflow_bytes_idx); + } + else if ( forwarded_offset < offset ) + { + // This is data past a gap and the reassembler needs to be enabled. + file_reassembler = new FileReassembler(this, forwarded_offset); + file_reassembler->NewBlock(network_time, offset, len, data); + return; + } + else + { + // This is data that was already seen so it can be completely ignored. + IncrementByteCount(len, overflow_bytes_idx); + return; + } + if ( first_chunk ) { - // TODO: this should all really be delayed until we attempt reassembly + // TODO: this should all really be delayed until we attempt reassembly. DetectMIME(data, len); FileEvent(file_new); first_chunk = false; } - file_analysis::Analyzer* a = 0; - IterCookie* c = analyzers.InitForIteration(); - - while ( (a = analyzers.NextEntry(c)) ) + if ( IsComplete() ) { - if ( ! a->DeliverChunk(data, len, offset) ) - analyzers.QueueRemove(a->Tag(), a->Args()); + EndOfFile(); } + else + { + file_analysis::Analyzer* a = 0; + IterCookie* c = analyzers.InitForIteration(); - analyzers.DrainModifications(); + while ( (a = analyzers.NextEntry(c)) ) + { + //if ( ! a->DeliverChunk(data, len, offset) ) + // { + // analyzers.QueueRemove(a->Tag(), a->Args()); + // } - // TODO: check reassembly requirement based on buffer size in record - if ( need_reassembly ) - reporter->InternalError("file_analyzer::File TODO: reassembly not yet supported"); + if ( ! a->DeliverStream(data, len) ) + { + analyzers.QueueRemove(a->Tag(), a->Args()); + } - // TODO: reassembly overflow stuff, increment overflow count, eval trigger + } - IncrementByteCount(len, seen_bytes_idx); + analyzers.DrainModifications(); + + forwarded_offset += len; + IncrementByteCount(len, seen_bytes_idx); + } } void File::DataIn(const u_char* data, uint64 len) diff --git a/src/file_analysis/File.h b/src/file_analysis/File.h index 6354c1c7e9..3422982303 100644 --- a/src/file_analysis/File.h +++ b/src/file_analysis/File.h @@ -8,6 +8,7 @@ #include #include +#include "FileReassembler.h" #include "Conn.h" #include "Val.h" #include "Tag.h" @@ -16,6 +17,8 @@ namespace file_analysis { +class FileReassembler; + /** * Wrapper class around \c fa_file record values from script layer. */ @@ -248,6 +251,8 @@ protected: private: string id; /**< A pretty hash that likely identifies file */ RecordVal* val; /**< \c fa_file from script layer. */ + uint64 forwarded_offset; /**< The offset of the file which has been forwarded. */ + FileReassembler *file_reassembler; /**< A reassembler for the file if it's needed. */ bool postpone_timeout; /**< Whether postponing timeout is requested. */ bool first_chunk; /**< Track first non-linear chunk. */ bool missed_bof; /**< Flags that we missed start of file. */ diff --git a/src/file_analysis/FileReassembler.cc b/src/file_analysis/FileReassembler.cc new file mode 100644 index 0000000000..8440fdca83 --- /dev/null +++ b/src/file_analysis/FileReassembler.cc @@ -0,0 +1,58 @@ + +#include "FileReassembler.h" +#include "File.h" + + +namespace file_analysis { + +class File; + +FileReassembler::FileReassembler(File *f, int starting_offset) + : Reassembler(starting_offset), the_file(f) + { + } + +FileReassembler::~FileReassembler() + { + } + +void FileReassembler::BlockInserted(DataBlock* start_block) + { + if ( seq_delta(start_block->seq, last_reassem_seq) > 0 || + seq_delta(start_block->upper, last_reassem_seq) <= 0 ) + return; + + + // We've filled a leading hole. Deliver as much as possible. + // Note that the new block may include both some old stuff + // and some new stuff. AddAndCheck() will have split the + // new stuff off into its own block(s), but in the following + // loop we have to take care not to deliver already-delivered + // data. + for ( DataBlock* b = start_block; + b && seq_delta(b->seq, last_reassem_seq) <= 0; b = b->next ) + { + if ( b->seq == last_reassem_seq ) + { // New stuff. + int len = b->Size(); + int seq = last_reassem_seq; + last_reassem_seq += len; + the_file->DataIn(b->block, len, seq); + } + } + + //CheckEOF(); + } + +void FileReassembler::Undelivered(int up_to_seq) + { + //reporter->Warning("should probably do something here (file reassembler undelivered)\n"); + } + +void FileReassembler::Overlap(const u_char* b1, const u_char* b2, int n) + { + //reporter->Warning("should probably do something here (file reassembler overlap)\n"); + } + + +} // end file_analysis diff --git a/src/file_analysis/FileReassembler.h b/src/file_analysis/FileReassembler.h new file mode 100644 index 0000000000..7f73ec6fa4 --- /dev/null +++ b/src/file_analysis/FileReassembler.h @@ -0,0 +1,45 @@ +#ifndef FILE_ANALYSIS_FILEREASSEMBLER_H +#define FILE_ANALYSIS_FILEREASSEMBLER_H + +#include "Reassem.h" +#include "File.h" + +class BroFile; +class Connection; + +namespace file_analysis { + +class File; + +//const int STOP_ON_GAP = 1; +//const int PUNT_ON_PARTIAL = 1; + +class FileReassembler : public Reassembler { +public: + + FileReassembler(File* f, int starting_offset); + virtual ~FileReassembler(); + + void Done(); + + // Checks if we have delivered all contents that we can possibly + // deliver for this endpoint. Calls TCP_Analyzer::EndpointEOF() + // when so. + //void CheckEOF(); + +private: + //DECLARE_SERIAL(FileReassembler); + + void Undelivered(int up_to_seq); + void BlockInserted(DataBlock* b); + void Overlap(const u_char* b1, const u_char* b2, int n); + + unsigned int had_gap:1; + unsigned int did_EOF:1; + unsigned int skip_deliveries:1; + File* the_file; +}; + +} // namespace analyzer::* + +#endif diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/a.out b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/a.out index 077fb5282c..0eace71c67 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/a.out +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/a.out @@ -10,3 +10,6 @@ file #0, 555523, 0 [orig_h=10.101.84.70, orig_p=10977/tcp, resp_h=129.174.93.161, resp_p=80/tcp] total bytes: 555523 source: HTTP +MD5: 5a484ada9c816c0e8b6d2d3978e3f503 +SHA1: 54e7d39e99eb9d40d6251c0361a1090a0d278571 +SHA256: 61c0718bd534ab55716eba161e91bb49155562ddc7c08f0c20f6359d7b808b66 From 38dbba762275e7ce2281445b56344eb6c82d49e1 Mon Sep 17 00:00:00 2001 From: Seth Hall Date: Sun, 5 Jan 2014 04:58:01 -0500 Subject: [PATCH 002/299] More file reassembly work. - The reassembly behavior can be modified per-file by enabling or disabling the reassembler and/or modifying the size of the reassembly buffer. - Changed the file extraction analyzer to use the stream to avoid issues with the chunk based approach not immediately triggering the file_new event due to mime-type detection delay. Early chunks frequently ended up lost before. - Generally things are working now and I'd consider this in testing. --- scripts/base/frameworks/files/main.bro | 60 +++++- scripts/base/init-bare.bro | 5 +- src/SerialTypes.h | 1 + src/event.bif | 19 ++ src/file_analysis/File.cc | 195 +++++++++--------- src/file_analysis/File.h | 39 +++- src/file_analysis/FileReassembler.cc | 28 +-- src/file_analysis/FileReassembler.h | 12 +- src/file_analysis/Manager.cc | 33 +++ src/file_analysis/Manager.h | 15 ++ src/file_analysis/analyzer/extract/Extract.cc | 27 ++- src/file_analysis/analyzer/extract/Extract.h | 3 +- src/file_analysis/analyzer/extract/events.bif | 4 +- src/file_analysis/file_analysis.bif | 21 ++ .../out | 1 + .../bro..stdout | 12 +- .../out | 8 + .../a.out | 2 + .../b.out | 12 +- .../c.out | 9 +- .../out | 8 + .../out | 16 ++ .../files.log | 4 +- 23 files changed, 375 insertions(+), 159 deletions(-) diff --git a/scripts/base/frameworks/files/main.bro b/scripts/base/frameworks/files/main.bro index cf2c11be45..14e1228fc4 100644 --- a/scripts/base/frameworks/files/main.bro +++ b/scripts/base/frameworks/files/main.bro @@ -99,8 +99,9 @@ export { ## during the process of analysis e.g. due to dropped packets. missing_bytes: count &log &default=0; - ## The number of not all-in-sequence bytes in the file stream that - ## were delivered to file analyzers due to reassembly buffer overflow. + ## The number of bytes in the file stream that were not delivered to + ## stream file analyzers. This could be overlapping bytes or + ## bytes that couldn't be reassembled. overflow_bytes: count &log &default=0; ## Whether the file analysis timed out at least once for the file. @@ -123,6 +124,33 @@ export { ## generate two handles that would hash to the same file id. const salt = "I recommend changing this." &redef; + ## The default setting for if the file reassembler is enabled for + ## each file. + const enable_reassembler = T &redef; + + ## The default allow per-file reassembly buffer size. + const reassembly_buffer_size = 1048576 &redef; + + ## Allows the file reassembler to be used if it's necessary because the + ## file is transferred out of order. + ## + ## f: the file. + global enable_reassembly: function(f: fa_file); + + ## Disables the file reassembler on this file. If the file is not + ## transferred out of order this will have no effect. + ## + ## f: the file. + global disable_reassembly: function(f: fa_file); + + ## Set the maximum size the reassembly buffer is allowed to grow + ## for the given file. + ## + ## f: the file. + ## + ## max: Maximum allowed size of the reassembly buffer. + global set_reassembly_buffer_size: function(f: fa_file, max: count); + ## Sets the *timeout_interval* field of :bro:see:`fa_file`, which is ## used to determine the length of inactivity that is allowed for a file ## before internal state related to it is cleaned up. When used within @@ -273,6 +301,21 @@ function set_timeout_interval(f: fa_file, t: interval): bool return __set_timeout_interval(f$id, t); } +function enable_reassembly(f: fa_file) + { + __enable_reassembly(f$id); + } + +function disable_reassembly(f: fa_file) + { + __disable_reassembly(f$id); + } + +function set_reassembly_buffer_size(f: fa_file, max: count) + { + __set_reassembly_buffer(f$id, max); + } + function add_analyzer(f: fa_file, tag: Files::Tag, args: AnalyzerArgs): bool { add f$info$analyzers[Files::analyzer_name(tag)]; @@ -311,11 +354,24 @@ function analyzer_name(tag: Files::Tag): string event file_new(f: fa_file) &priority=10 { set_info(f); + + if ( enable_reassembler ) + { + Files::enable_reassembly(f); + Files::set_reassembly_buffer_size(f, reassembly_buffer_size); + } } event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priority=10 { set_info(f); + + if ( enable_reassembler ) + { + Files::enable_reassembly(f); + Files::set_reassembly_buffer_size(f, reassembly_buffer_size); + } + add f$info$conn_uids[c$uid]; local cid = c$id; add f$info$tx_hosts[f$is_orig ? cid$orig_h : cid$resp_h]; diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index c58559bd9a..5c60ec6690 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -355,8 +355,9 @@ type fa_file: record { ## during the process of analysis e.g. due to dropped packets. missing_bytes: count &default=0; - ## The number of not all-in-sequence bytes in the file stream that - ## were not delivered to file analyzers due to reassembly buffer overflow. + ## The number of bytes in the file stream that were not delivered to + ## stream file analyzers. This could be overlapping bytes or + ## bytes that couldn't be reassembled. overflow_bytes: count &default=0; ## The amount of time between receiving new data for this file that diff --git a/src/SerialTypes.h b/src/SerialTypes.h index 69927afb74..7f79328083 100644 --- a/src/SerialTypes.h +++ b/src/SerialTypes.h @@ -87,6 +87,7 @@ SERIAL_TCP_CONTENTS(TCP_NVT, 3) #define SERIAL_REASSEMBLER(name, val) SERIAL_CONST(name, val, REASSEMBLER) SERIAL_REASSEMBLER(REASSEMBLER, 1) SERIAL_REASSEMBLER(TCP_REASSEMBLER, 2) +SERIAL_REASSEMBLER(FILE_REASSEMBLER, 3) #define SERIAL_VAL(name, val) SERIAL_CONST(name, val, VAL) SERIAL_VAL(VAL, 1) diff --git a/src/event.bif b/src/event.bif index 4237bebc7b..25df2d823f 100644 --- a/src/event.bif +++ b/src/event.bif @@ -935,8 +935,27 @@ event file_timeout%(f: fa_file%); ## len: The number of missing bytes. ## ## .. bro:see:: file_new file_over_new_connection file_timeout file_state_remove +## file_reassembly_buffer_overflow event file_gap%(f: fa_file, offset: count, len: count%); +## Indicates that the file had an overflow of the reassembly buffer. +## This is a specialization of the :bro:id:`file_gap` event. +## +## f: The file. +## +## offset: The byte offset from the start of the file at which the reassembly +## couldn't continue due to running out of reassembly buffer space. +## +## skipped: The number of bytes of the file skipped over to flush some +## file data and get back under the reassembly buffer size limit. +## This value will also be represented as a gap. +## +## .. bro:see:: file_new file_over_new_connection file_timeout file_state_remove +## file_gap Files::enable_reassembler Files::reassembly_buffer_size +## Files::enable_reassembly Files::disable_reassembly +## Files::set_reassembly_buffer_size +event file_reassembly_buffer_overflow%(f: fa_file, offset: count, skipped: count%); + ## This event is generated each time file analysis is ending for a given file. ## ## f: The file. diff --git a/src/file_analysis/File.cc b/src/file_analysis/File.cc index d53c45fe06..269a78e396 100644 --- a/src/file_analysis/File.cc +++ b/src/file_analysis/File.cc @@ -3,7 +3,6 @@ #include #include "File.h" -#include "FileReassembler.h" #include "FileTimer.h" #include "Analyzer.h" #include "Manager.h" @@ -77,8 +76,8 @@ void File::StaticInit() File::File(const string& file_id, Connection* conn, analyzer::Tag tag, bool is_orig) - : id(file_id), val(0), postpone_timeout(false), first_chunk(true), - missed_bof(false), need_reassembly(false), done(false), + : id(file_id), val(0), stream_offset(0), reassembly_max_buffer(0), + reassembly_enabled(false), postpone_timeout(false), done(false), did_file_new_event(false), analyzers(this) { StaticInit(); @@ -88,7 +87,6 @@ File::File(const string& file_id, Connection* conn, analyzer::Tag tag, val = new RecordVal(fa_file_type); val->Assign(id_idx, new StringVal(file_id.c_str())); - forwarded_offset = 0; file_reassembler = 0; if ( conn ) { @@ -244,7 +242,7 @@ bool File::IsComplete() const if ( ! total ) return false; - if ( LookupFieldDefaultCount(seen_bytes_idx) >= total->AsCount() ) + if ( stream_offset >= total->AsCount() ) return true; return false; @@ -302,6 +300,26 @@ bool File::DetectMIME(const u_char* data, uint64 len) return mime; } +void File::EnableReassembly() + { + reassembly_enabled = true; + } + +void File::DisableReassembly() + { + reassembly_enabled = false; + if ( file_reassembler ) + { + delete file_reassembler; + file_reassembler = NULL; + } + } + +void File::SetReassemblyBuffer(uint64 max) + { + reassembly_max_buffer = max; + } + void File::ReplayBOF() { if ( bof_buffer.replayed ) @@ -311,141 +329,122 @@ void File::ReplayBOF() if ( bof_buffer.chunks.empty() ) { - // Since we missed the beginning, try file type detect on next data in. - missed_bof = true; + // We definitely can't do anything if we don't have any chunks. return; } BroString* bs = concatenate(bof_buffer.chunks); val->Assign(bof_buffer_idx, new StringVal(bs)); - DetectMIME(bs->Bytes(), bs->Len()); - - FileEvent(file_new); - for ( size_t i = 0; i < bof_buffer.chunks.size(); ++i ) DataIn(bof_buffer.chunks[i]->Bytes(), bof_buffer.chunks[i]->Len()); } -void File::DataIn(const u_char* data, uint64 len, uint64 offset) +void File::DeliverStream(const u_char* data, uint64 len) { - analyzers.DrainModifications(); + // Buffer enough data send to libmagic. + if ( BufferBOF(data, len) ) + return; - if ( file_reassembler ) + if ( stream_offset == 0 ) { - // If there is a file reassembler we must forward any data there. - // But this only happens if the incoming data doesn't happen - // to align with the current forwarded_offset - file_reassembler->NewBlock(network_time, offset, len, data); + DetectMIME(data, len); + FileEvent(file_new); + } - if ( !file_reassembler->HasBlocks() ) + file_analysis::Analyzer* a = 0; + IterCookie* c = analyzers.InitForIteration(); + while ( (a = analyzers.NextEntry(c)) ) + { + if ( !a->DeliverStream(data, len) ) { - delete file_reassembler; - file_reassembler = 0; + analyzers.QueueRemove(a->Tag(), a->Args()); } } - else if ( forwarded_offset == offset ) + + stream_offset += len; + IncrementByteCount(len, seen_bytes_idx); + } + +void File::DeliverChunk(const u_char* data, uint64 len, uint64 offset) + { + // Potentially handle reassembly and deliver to the stream analyzers. + if ( file_reassembler ) + { + if ( reassembly_max_buffer > 0 && + reassembly_max_buffer < file_reassembler->TotalSize() ) + { + uint64 first_offset = file_reassembler->GetFirstBlockOffset(); + int gap_bytes = file_reassembler->TrimToSeq(first_offset); + + if ( FileEventAvailable(file_reassembly_buffer_overflow) ) + { + val_list* vl = new val_list(); + vl->append(val->Ref()); + vl->append(new Val(stream_offset, TYPE_COUNT)); + vl->append(new Val(gap_bytes, TYPE_COUNT)); + FileEvent(file_reassembly_buffer_overflow, vl); + } + + Gap(stream_offset, gap_bytes); + } + + // Forward data to the reassembler. + file_reassembler->NewBlock(network_time, offset, len, data); + } + else if ( stream_offset == offset ) { // This is the normal case where a file is transferred linearly. - // Nothing should be done here. + // Nothing special should be done here. + DeliverStream(data, len); } - else if ( forwarded_offset > offset && forwarded_offset < offset+len ) + else if ( reassembly_enabled ) { - // This is a segment that begins before the forwarded_offset - // but proceeds past the forwarded_offset. It needs - // trimmed but the reassembler is not enabled. - uint64 adjustment = forwarded_offset - offset; - data = data + adjustment; - len = len - adjustment; - offset = forwarded_offset; - IncrementByteCount(adjustment, overflow_bytes_idx); - } - else if ( forwarded_offset < offset ) - { - // This is data past a gap and the reassembler needs to be enabled. - file_reassembler = new FileReassembler(this, forwarded_offset); + // This is data that doesn't match the offset and the reassembler + // needs to be enabled. + file_reassembler = new FileReassembler(this, stream_offset); file_reassembler->NewBlock(network_time, offset, len, data); - return; } else { - // This is data that was already seen so it can be completely ignored. + // We can't reassemble so we throw out the data for streaming. IncrementByteCount(len, overflow_bytes_idx); - return; } - if ( first_chunk ) + // Deliver to the chunk analyzers. + file_analysis::Analyzer* a = 0; + IterCookie* c = analyzers.InitForIteration(); + while ( (a = analyzers.NextEntry(c)) ) { - // TODO: this should all really be delayed until we attempt reassembly. - DetectMIME(data, len); - FileEvent(file_new); - first_chunk = false; + if ( !a->DeliverChunk(data, len, offset) ) + { + analyzers.QueueRemove(a->Tag(), a->Args()); + } } if ( IsComplete() ) { + // If the file is complete we can automatically go and close out the file from here. EndOfFile(); } - else - { - file_analysis::Analyzer* a = 0; - IterCookie* c = analyzers.InitForIteration(); + } - while ( (a = analyzers.NextEntry(c)) ) - { - //if ( ! a->DeliverChunk(data, len, offset) ) - // { - // analyzers.QueueRemove(a->Tag(), a->Args()); - // } - if ( ! a->DeliverStream(data, len) ) - { - analyzers.QueueRemove(a->Tag(), a->Args()); - } - - } - - analyzers.DrainModifications(); - - forwarded_offset += len; - IncrementByteCount(len, seen_bytes_idx); - } +void File::DataIn(const u_char* data, uint64 len, uint64 offset) + { + analyzers.DrainModifications(); + DeliverChunk(data, len, offset); + analyzers.DrainModifications(); } void File::DataIn(const u_char* data, uint64 len) { analyzers.DrainModifications(); - - if ( BufferBOF(data, len) ) - return; - - if ( missed_bof ) - { - DetectMIME(data, len); - FileEvent(file_new); - missed_bof = false; - } - - file_analysis::Analyzer* a = 0; - IterCookie* c = analyzers.InitForIteration(); - - while ( (a = analyzers.NextEntry(c)) ) - { - if ( ! a->DeliverStream(data, len) ) - { - analyzers.QueueRemove(a->Tag(), a->Args()); - continue; - } - - uint64 offset = LookupFieldDefaultCount(seen_bytes_idx) + - LookupFieldDefaultCount(missing_bytes_idx); - - if ( ! a->DeliverChunk(data, len, offset) ) - analyzers.QueueRemove(a->Tag(), a->Args()); - } - + + uint64 offset = LookupFieldDefaultCount(seen_bytes_idx) + + LookupFieldDefaultCount(missing_bytes_idx); + DeliverChunk(data, len, offset); analyzers.DrainModifications(); - IncrementByteCount(len, seen_bytes_idx); } void File::EndOfFile() @@ -501,6 +500,8 @@ void File::Gap(uint64 offset, uint64 len) } analyzers.DrainModifications(); + + stream_offset += len; IncrementByteCount(len, missing_bytes_idx); } diff --git a/src/file_analysis/File.h b/src/file_analysis/File.h index 3422982303..14a168d0f9 100644 --- a/src/file_analysis/File.h +++ b/src/file_analysis/File.h @@ -169,6 +169,7 @@ public: protected: friend class Manager; + friend class FileReassembler; /** * Constructor; only file_analysis::Manager should be creating these. @@ -236,6 +237,33 @@ protected: */ bool DetectMIME(const u_char* data, uint64 len); + /** + * Enables reassembly on the file. + */ + void EnableReassembly(); + + /** + * Disables reassembly on the file. If there is an existing reassembler + * for the file, this will cause it to be deleted and won't allow a new + * one to be created until reassembly is reenabled. + */ + void DisableReassembly(); + + /** + * Set a maximum allowed bytes of memory for file reassembly for this file. + */ + void SetReassemblyBuffer(uint64 max); + + /** + * Perform stream-wise delivery for analyzers that need it. + */ + void DeliverStream(const u_char* data, uint64 len); + + /** + * Perform chunk-wise delivery for analyzers that need it. + */ + void DeliverChunk(const u_char* data, uint64 len, uint64 offset); + /** * Lookup a record field index/offset by name. * @param field_name the name of the \c fa_file record field. @@ -248,18 +276,17 @@ protected: */ static void StaticInit(); -private: +protected: string id; /**< A pretty hash that likely identifies file */ RecordVal* val; /**< \c fa_file from script layer. */ - uint64 forwarded_offset; /**< The offset of the file which has been forwarded. */ FileReassembler *file_reassembler; /**< A reassembler for the file if it's needed. */ + uint64 stream_offset; /**< The offset of the file which has been forwarded. */ + uint64 reassembly_max_buffer; /**< Maximum allowed buffer for reassembly. */ + bool reassembly_enabled; /**< Whether file stream reassembly is needed. */ bool postpone_timeout; /**< Whether postponing timeout is requested. */ - bool first_chunk; /**< Track first non-linear chunk. */ - bool missed_bof; /**< Flags that we missed start of file. */ - bool need_reassembly; /**< Whether file stream reassembly is needed. */ bool done; /**< If this object is about to be deleted. */ bool did_file_new_event; /**< Whether the file_new event has been done. */ - AnalyzerSet analyzers; /**< A set of attached file analyzer. */ + AnalyzerSet analyzers; /**< A set of attached file analyzers. */ queue > fonc_queue; struct BOF_Buffer { diff --git a/src/file_analysis/FileReassembler.cc b/src/file_analysis/FileReassembler.cc index 8440fdca83..d05a573682 100644 --- a/src/file_analysis/FileReassembler.cc +++ b/src/file_analysis/FileReassembler.cc @@ -22,13 +22,6 @@ void FileReassembler::BlockInserted(DataBlock* start_block) seq_delta(start_block->upper, last_reassem_seq) <= 0 ) return; - - // We've filled a leading hole. Deliver as much as possible. - // Note that the new block may include both some old stuff - // and some new stuff. AddAndCheck() will have split the - // new stuff off into its own block(s), but in the following - // loop we have to take care not to deliver already-delivered - // data. for ( DataBlock* b = start_block; b && seq_delta(b->seq, last_reassem_seq) <= 0; b = b->next ) { @@ -36,23 +29,34 @@ void FileReassembler::BlockInserted(DataBlock* start_block) { // New stuff. int len = b->Size(); int seq = last_reassem_seq; + the_file->DeliverStream(b->block, len); last_reassem_seq += len; - the_file->DataIn(b->block, len, seq); } } - - //CheckEOF(); } void FileReassembler::Undelivered(int up_to_seq) { - //reporter->Warning("should probably do something here (file reassembler undelivered)\n"); + // Not doing anything here yet. } void FileReassembler::Overlap(const u_char* b1, const u_char* b2, int n) { - //reporter->Warning("should probably do something here (file reassembler overlap)\n"); + // Not doing anything here yet. } +IMPLEMENT_SERIAL(FileReassembler, SER_FILE_REASSEMBLER); + +bool FileReassembler::DoSerialize(SerialInfo* info) const + { + reporter->InternalError("FileReassembler::DoSerialize not implemented"); + return false; // Cannot be reached. + } + +bool FileReassembler::DoUnserialize(UnserialInfo* info) + { + reporter->InternalError("FileReassembler::DoUnserialize not implemented"); + return false; // Cannot be reached. + } } // end file_analysis diff --git a/src/file_analysis/FileReassembler.h b/src/file_analysis/FileReassembler.h index 7f73ec6fa4..146171b6ed 100644 --- a/src/file_analysis/FileReassembler.h +++ b/src/file_analysis/FileReassembler.h @@ -21,14 +21,16 @@ public: virtual ~FileReassembler(); void Done(); + uint64 GetFirstBlockOffset() { return blocks->seq; } // Checks if we have delivered all contents that we can possibly - // deliver for this endpoint. Calls TCP_Analyzer::EndpointEOF() - // when so. - //void CheckEOF(); + // deliver for this endpoint. + void CheckEOF(); -private: - //DECLARE_SERIAL(FileReassembler); +protected: + FileReassembler() { } + + DECLARE_SERIAL(FileReassembler); void Undelivered(int up_to_seq); void BlockInserted(DataBlock* b); diff --git a/src/file_analysis/Manager.cc b/src/file_analysis/Manager.cc index 0337dbb098..5585c6c33c 100644 --- a/src/file_analysis/Manager.cc +++ b/src/file_analysis/Manager.cc @@ -183,6 +183,39 @@ bool Manager::SetTimeoutInterval(const string& file_id, double interval) const return true; } +bool Manager::EnableReassembly(const string& file_id) + { + File* file = LookupFile(file_id); + + if ( ! file ) + return false; + + file->EnableReassembly(); + return true; + } + +bool Manager::DisableReassembly(const string& file_id) + { + File* file = LookupFile(file_id); + + if ( ! file ) + return false; + + file->DisableReassembly(); + return true; + } + +bool Manager::SetReassemblyBuffer(const string& file_id, uint64 max) + { + File* file = LookupFile(file_id); + + if ( ! file ) + return false; + + file->SetReassemblyBuffer(max); + return true; + } + bool Manager::SetExtractionLimit(const string& file_id, RecordVal* args, uint64 n) const { diff --git a/src/file_analysis/Manager.h b/src/file_analysis/Manager.h index cf73c6b52d..1d30e73e8a 100644 --- a/src/file_analysis/Manager.h +++ b/src/file_analysis/Manager.h @@ -173,6 +173,21 @@ public: */ bool SetTimeoutInterval(const string& file_id, double interval) const; + /** + * Enable the reassembler for a file. + */ + bool EnableReassembly(const string& file_id); + + /** + * Disable the reassembler for a file. + */ + bool DisableReassembly(const string& file_id); + + /** + * Set the reassembly for a file in bytes. + */ + bool SetReassemblyBuffer(const string& file_id, uint64 max); + /** * Sets a limit on the maximum size allowed for extracting the file * to local disk; diff --git a/src/file_analysis/analyzer/extract/Extract.cc b/src/file_analysis/analyzer/extract/Extract.cc index 1a3917cd0e..032a176564 100644 --- a/src/file_analysis/analyzer/extract/Extract.cc +++ b/src/file_analysis/analyzer/extract/Extract.cc @@ -14,7 +14,7 @@ Extract::Extract(RecordVal* args, File* file, const string& arg_filename, : file_analysis::Analyzer(file_mgr->GetComponentTag("EXTRACT"), args, file), filename(arg_filename), limit(arg_limit) { - fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); + fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, 0666); if ( fd < 0 ) { @@ -53,7 +53,7 @@ file_analysis::Analyzer* Extract::Instantiate(RecordVal* args, File* file) limit->AsCount()); } -static bool check_limit_exceeded(uint64 lim, uint64 off, uint64 len, uint64* n) +static bool check_limit_exceeded(uint64 lim, uint64 len, uint64* n) { if ( lim == 0 ) { @@ -61,13 +61,13 @@ static bool check_limit_exceeded(uint64 lim, uint64 off, uint64 len, uint64* n) return false; } - if ( off >= lim ) - { - *n = 0; - return true; - } - - *n = lim - off; + //if ( off >= lim ) + // { + // *n = 0; + // return true; + // } + // + //*n = lim - off; if ( len > *n ) return true; @@ -77,13 +77,13 @@ static bool check_limit_exceeded(uint64 lim, uint64 off, uint64 len, uint64* n) return false; } -bool Extract::DeliverChunk(const u_char* data, uint64 len, uint64 offset) +bool Extract::DeliverStream(const u_char* data, uint64 len) { if ( ! fd ) return false; uint64 towrite = 0; - bool limit_exceeded = check_limit_exceeded(limit, offset, len, &towrite); + bool limit_exceeded = check_limit_exceeded(limit, len, &towrite); if ( limit_exceeded && file_extraction_limit ) { @@ -92,16 +92,15 @@ bool Extract::DeliverChunk(const u_char* data, uint64 len, uint64 offset) vl->append(f->GetVal()->Ref()); vl->append(Args()->Ref()); vl->append(new Val(limit, TYPE_COUNT)); - vl->append(new Val(offset, TYPE_COUNT)); vl->append(new Val(len, TYPE_COUNT)); f->FileEvent(file_extraction_limit, vl); // Limit may have been modified by BIF, re-check it. - limit_exceeded = check_limit_exceeded(limit, offset, len, &towrite); + limit_exceeded = check_limit_exceeded(limit, len, &towrite); } if ( towrite > 0 ) - safe_pwrite(fd, data, towrite, offset); + safe_write(fd, (const char *) data, towrite); return ( ! limit_exceeded ); } diff --git a/src/file_analysis/analyzer/extract/Extract.h b/src/file_analysis/analyzer/extract/Extract.h index 00c4dbe2b7..59130fa230 100644 --- a/src/file_analysis/analyzer/extract/Extract.h +++ b/src/file_analysis/analyzer/extract/Extract.h @@ -28,11 +28,10 @@ public: * Write a chunk of file data to the local extraction file. * @param data pointer to a chunk of file data. * @param len number of bytes in the data chunk. - * @param offset number of bytes from start of file at which chunk starts. * @return false if there was no extraction file open and the data couldn't * be written, else true. */ - virtual bool DeliverChunk(const u_char* data, uint64 len, uint64 offset); + virtual bool DeliverStream(const u_char* data, uint64 len); /** * Create a new instance of an Extract analyzer. diff --git a/src/file_analysis/analyzer/extract/events.bif b/src/file_analysis/analyzer/extract/events.bif index 1c08736416..f5ebb6816b 100644 --- a/src/file_analysis/analyzer/extract/events.bif +++ b/src/file_analysis/analyzer/extract/events.bif @@ -11,9 +11,7 @@ ## ## limit: The limit, in bytes, the extracted file is about to breach. ## -## offset: The offset at which a file chunk is about to be written. -## ## len: The length of the file chunk about to be written. ## ## .. bro:see:: Files::add_analyzer Files::ANALYZER_EXTRACT -event file_extraction_limit%(f: fa_file, args: any, limit: count, offset: count, len: count%); +event file_extraction_limit%(f: fa_file, args: any, limit: count, len: count%); diff --git a/src/file_analysis/file_analysis.bif b/src/file_analysis/file_analysis.bif index 0e904f298f..4e4b4c6cdb 100644 --- a/src/file_analysis/file_analysis.bif +++ b/src/file_analysis/file_analysis.bif @@ -15,6 +15,27 @@ function Files::__set_timeout_interval%(file_id: string, t: interval%): bool return new Val(result, TYPE_BOOL); %} +## :bro:see:`Files::enable_reassembly`. +function Files::__enable_reassembly%(file_id: string%): bool + %{ + bool result = file_mgr->EnableReassembly(file_id->CheckString()); + return new Val(result, TYPE_BOOL); + %} + +## :bro:see:`Files::disable_reassembly`. +function Files::__disable_reassembly%(file_id: string%): bool + %{ + bool result = file_mgr->DisableReassembly(file_id->CheckString()); + return new Val(result, TYPE_BOOL); + %} + +## :bro:see:`Files::set_reassembly_buffer`. +function Files::__set_reassembly_buffer%(file_id: string, max: count%): bool + %{ + bool result = file_mgr->SetReassemblyBuffer(file_id->CheckString(), max); + return new Val(result, TYPE_BOOL); + %} + ## :bro:see:`Files::add_analyzer`. function Files::__add_analyzer%(file_id: string, tag: Files::Tag, args: any%): bool %{ diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.actions.data_event/out b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.actions.data_event/out index cbd60840bf..c9b5e20466 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.actions.data_event/out +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.actions.data_event/out @@ -7,6 +7,7 @@ text/plain FILE_OVER_NEW_CONNECTION file_stream, file #0, 1500, ^J0.26 | 2012-08-24 15:10:04 -0700^J^J * Fixing update-changes, which could pick the wrong control file. (Robin Sommer)^J^J * Fixing GPG signing script. (Robin Sommer)^J^J0.25 | 2012-08-01 13:55:46 -0500^J^J * Fix configure script to exit with non-zero status on error (Jon Siwek)^J^J0.24 | 2012-07-05 12:50:43 -0700^J^J * Raise minimum required CMake version to 2.6.3 (Jon Siwek)^J^J * Adding script to delete old fully-merged branches. (Robin Sommer)^J^J0.23-2 | 2012-01-25 13:24:01 -0800^J^J * Fix a bro-cut error message. (Daniel Thayer)^J^J0.23 | 2012-01-11 12:16:11 -0800^J^J * Tweaks to release scripts, plus a new one for signing files.^J (Robin Sommer)^J^J0.22 | 2012-01-10 16:45:19 -0800^J^J * Tweaks for OpenBSD support. (Jon Siwek)^J^J * bro-cut extensions and fixes. (Robin Sommer)^J ^J - If no field names are given on the command line, we now pass through^J all fields. Adresses #657.^J^J - Removing some GNUism from awk script. Addresses #653.^J^J - Added option for time output in UTC. Addresses #668.^J^J - Added output field separator option -F. Addresses #649.^J^J - Fixing option -c: only some header lines were passed through^J rather than all. (Robin Sommer)^J^J * Fix parallel make portability. (Jon Siwek)^J^J0.21-9 | 2011-11-07 05:44:14 -0800^J^J * Fixing compiler warnings. Addresses #388. (Jon Siwek)^J^J0.21-2 | 2011-11-02 18:12:13 -0700^J^J * Fix for misnaming temp file in update-changes script. (Robin Sommer)^J^J0.21-1 | 2011-11-02 18:10:39 -0700^J^J * Little fix for make-relea file_chunk, file #0, 1500, 0, ^J0.26 | 2012-08-24 15:10:04 -0700^J^J * Fixing update-changes, which could pick the wrong control file. (Robin Sommer)^J^J * Fixing GPG signing script. (Robin Sommer)^J^J0.25 | 2012-08-01 13:55:46 -0500^J^J * Fix configure script to exit with non-zero status on error (Jon Siwek)^J^J0.24 | 2012-07-05 12:50:43 -0700^J^J * Raise minimum required CMake version to 2.6.3 (Jon Siwek)^J^J * Adding script to delete old fully-merged branches. (Robin Sommer)^J^J0.23-2 | 2012-01-25 13:24:01 -0800^J^J * Fix a bro-cut error message. (Daniel Thayer)^J^J0.23 | 2012-01-11 12:16:11 -0800^J^J * Tweaks to release scripts, plus a new one for signing files.^J (Robin Sommer)^J^J0.22 | 2012-01-10 16:45:19 -0800^J^J * Tweaks for OpenBSD support. (Jon Siwek)^J^J * bro-cut extensions and fixes. (Robin Sommer)^J ^J - If no field names are given on the command line, we now pass through^J all fields. Adresses #657.^J^J - Removing some GNUism from awk script. Addresses #653.^J^J - Added option for time output in UTC. Addresses #668.^J^J - Added output field separator option -F. Addresses #649.^J^J - Fixing option -c: only some header lines were passed through^J rather than all. (Robin Sommer)^J^J * Fix parallel make portability. (Jon Siwek)^J^J0.21-9 | 2011-11-07 05:44:14 -0800^J^J * Fixing compiler warnings. Addresses #388. (Jon Siwek)^J^J0.21-2 | 2011-11-02 18:12:13 -0700^J^J * Fix for misnaming temp file in update-changes script. (Robin Sommer)^J^J0.21-1 | 2011-11-02 18:10:39 -0700^J^J * Little fix for make-relea +file_chunk, file #0, 1500, 0, ^J0.26 | 2012-08-24 15:10:04 -0700^J^J * Fixing update-changes, which could pick the wrong control file. (Robin Sommer)^J^J * Fixing GPG signing script. (Robin Sommer)^J^J0.25 | 2012-08-01 13:55:46 -0500^J^J * Fix configure script to exit with non-zero status on error (Jon Siwek)^J^J0.24 | 2012-07-05 12:50:43 -0700^J^J * Raise minimum required CMake version to 2.6.3 (Jon Siwek)^J^J * Adding script to delete old fully-merged branches. (Robin Sommer)^J^J0.23-2 | 2012-01-25 13:24:01 -0800^J^J * Fix a bro-cut error message. (Daniel Thayer)^J^J0.23 | 2012-01-11 12:16:11 -0800^J^J * Tweaks to release scripts, plus a new one for signing files.^J (Robin Sommer)^J^J0.22 | 2012-01-10 16:45:19 -0800^J^J * Tweaks for OpenBSD support. (Jon Siwek)^J^J * bro-cut extensions and fixes. (Robin Sommer)^J ^J - If no field names are given on the command line, we now pass through^J all fields. Adresses #657.^J^J - Removing some GNUism from awk script. Addresses #653.^J^J - Added option for time output in UTC. Addresses #668.^J^J - Added output field separator option -F. Addresses #649.^J^J - Fixing option -c: only some header lines were passed through^J rather than all. (Robin Sommer)^J^J * Fix parallel make portability. (Jon Siwek)^J^J0.21-9 | 2011-11-07 05:44:14 -0800^J^J * Fixing compiler warnings. Addresses #388. (Jon Siwek)^J^J0.21-2 | 2011-11-02 18:12:13 -0700^J^J * Fix for misnaming temp file in update-changes script. (Robin Sommer)^J^J0.21-1 | 2011-11-02 18:10:39 -0700^J^J * Little fix for make-relea file_stream, file #0, 1024, se script, which could pick out the wrong^J tag. (Robin Sommer)^J^J0.21 | 2011-10-27 17:40:45 -0700^J^J * Fixing bro-cut's usage message and argument error handling. (Robin Sommer)^J^J * Bugfix in update-changes script. (Robin Sommer)^J^J * update-changes now ignores commits it did itself. (Robin Sommer)^J^J * Fix a bug in the update-changes script. (Robin Sommer)^J^J * bro-cut now always installs to $prefix/bin by `make install`. (Jon Siwek)^J^J * Options to adjust time format for bro-cut. (Robin Sommer)^J^J The default with -d is now ISO format. The new option "-D "^J specifies a custom strftime()-style format string. Alternatively,^J the environment variable BRO_CUT_TIMEFMT can set the format as^J well.^J^J * bro-cut now understands the field separator header. (Robin Sommer)^J^J * Renaming options -h/-H -> -c/-C, and doing some general cleanup.^J^J0.2 | 2011-10-25 19:53:57 -0700^J^J * Adding support for replacing version string in a setup.py. (Robin^J Sommer)^J^J * Change generated root cert DN indices f file_chunk, file #0, 1024, 1500, se script, which could pick out the wrong^J tag. (Robin Sommer)^J^J0.21 | 2011-10-27 17:40:45 -0700^J^J * Fixing bro-cut's usage message and argument error handling. (Robin Sommer)^J^J * Bugfix in update-changes script. (Robin Sommer)^J^J * update-changes now ignores commits it did itself. (Robin Sommer)^J^J * Fix a bug in the update-changes script. (Robin Sommer)^J^J * bro-cut now always installs to $prefix/bin by `make install`. (Jon Siwek)^J^J * Options to adjust time format for bro-cut. (Robin Sommer)^J^J The default with -d is now ISO format. The new option "-D "^J specifies a custom strftime()-style format string. Alternatively,^J the environment variable BRO_CUT_TIMEFMT can set the format as^J well.^J^J * bro-cut now understands the field separator header. (Robin Sommer)^J^J * Renaming options -h/-H -> -c/-C, and doing some general cleanup.^J^J0.2 | 2011-10-25 19:53:57 -0700^J^J * Adding support for replacing version string in a setup.py. (Robin^J Sommer)^J^J * Change generated root cert DN indices f file_stream, file #0, 476, ormat for RFC2253^J compliance. (Jon Siwek)^J^J * New tool devel-tools/check-release to run before making releases.^J (Robin Sommer)^J^J * devel-tools/update-changes gets a new option -a to amend to^J previous commit if possible. Default is now not to (used to be the^J opposite). (Robin Sommer)^J^J * Change Mozilla trust root generation to index certs by subject DN. (Jon Siwek)^J^J * Change distclean to only remove build dir. (Jon Siwek)^J^J * Make dist now cleans the diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.bifs.set_timeout_interval/bro..stdout b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.bifs.set_timeout_interval/bro..stdout index e78f5c8c17..8b2826a925 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.bifs.set_timeout_interval/bro..stdout +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.bifs.set_timeout_interval/bro..stdout @@ -1,5 +1,7 @@ FILE_NEW file #0, 0, 0 +FILE_BOF_BUFFER +MZ\x90\0^C\0\0\0^D\0\0 MIME_TYPE application/x-dosexec FILE_OVER_NEW_CONNECTION @@ -8,15 +10,13 @@ file #0, 1022920, 0 [orig_h=192.168.72.14, orig_p=3254/tcp, resp_h=65.54.95.206, resp_p=80/tcp] total bytes: 1022920 source: HTTP -FILE_NEW -file #1, 0, 0 -MIME_TYPE -application/octet-stream -FILE_OVER_NEW_CONNECTION +MD5: fc13fee1d44ef737a3133f1298b21d28 +SHA1: 7d99803eaf3b6e8dfa3581348bc694089579d25a +SHA256: dcb87a62a2b5d449abc138776000fd1b14edc690e9da6ea325b8f352ab033202 FILE_TIMEOUT FILE_TIMEOUT FILE_STATE_REMOVE -file #1, 206024, 0 +file #0, 0, 0 [orig_h=192.168.72.14, orig_p=3257/tcp, resp_h=65.54.95.14, resp_p=80/tcp] total bytes: 1022920 source: HTTP diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.multipart/out b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.multipart/out index da42f4fd68..9870cd8888 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.multipart/out +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.multipart/out @@ -55,3 +55,11 @@ source: HTTP MD5: 226244811006caf4ac904344841168dd SHA1: 7222902b8b8e68e25c0422e7f8bdf344efeda54d SHA256: dd485ecf240e12807516b0a27718fc3ab9a17c1158a452967343c98cefba07a0 +FILE_STATE_REMOVE +file #3, 465, 0 +[orig_h=141.142.228.5, orig_p=57262/tcp, resp_h=54.243.88.146, resp_p=80/tcp] +total bytes: 465 +source: HTTP +MD5: 226244811006caf4ac904344841168dd +SHA1: 7222902b8b8e68e25c0422e7f8bdf344efeda54d +SHA256: dd485ecf240e12807516b0a27718fc3ab9a17c1158a452967343c98cefba07a0 diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/a.out b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/a.out index 0eace71c67..47c42efd13 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/a.out +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/a.out @@ -1,5 +1,7 @@ FILE_NEW file #0, 0, 0 +FILE_BOF_BUFFER +%PDF-1.4^J%\xd0 MIME_TYPE application/pdf FILE_OVER_NEW_CONNECTION diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/b.out b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/b.out index 9c05f311f3..9c123887e7 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/b.out +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/b.out @@ -1,5 +1,7 @@ FILE_NEW file #0, 0, 0 +FILE_BOF_BUFFER +MZ\x90\0^C\0\0\0^D\0\0 MIME_TYPE application/x-dosexec FILE_OVER_NEW_CONNECTION @@ -8,14 +10,12 @@ file #0, 1022920, 0 [orig_h=192.168.72.14, orig_p=3254/tcp, resp_h=65.54.95.206, resp_p=80/tcp] total bytes: 1022920 source: HTTP -FILE_NEW -file #1, 0, 0 -MIME_TYPE -application/octet-stream -FILE_OVER_NEW_CONNECTION +MD5: fc13fee1d44ef737a3133f1298b21d28 +SHA1: 7d99803eaf3b6e8dfa3581348bc694089579d25a +SHA256: dcb87a62a2b5d449abc138776000fd1b14edc690e9da6ea325b8f352ab033202 FILE_TIMEOUT FILE_STATE_REMOVE -file #1, 206024, 0 +file #0, 0, 0 [orig_h=192.168.72.14, orig_p=3257/tcp, resp_h=65.54.95.14, resp_p=80/tcp] total bytes: 1022920 source: HTTP diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/c.out b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/c.out index d85a9de314..b344249aa1 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/c.out +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/c.out @@ -1,12 +1,17 @@ FILE_NEW file #0, 0, 0 +FILE_BOF_BUFFER +%PDF-1.4^M%\xe2 MIME_TYPE -application/octet-stream +application/pdf FILE_OVER_NEW_CONNECTION FILE_OVER_NEW_CONNECTION FILE_STATE_REMOVE -file #0, 498702, 0 +file #0, 498668, 0 [orig_h=10.45.179.94, orig_p=19950/tcp, resp_h=129.174.93.170, resp_p=80/tcp] [orig_h=10.45.179.94, orig_p=19953/tcp, resp_h=129.174.93.170, resp_p=80/tcp] total bytes: 498668 source: HTTP +MD5: 94046a5fb1c5802d0f1e6d704cf3e10e +SHA1: 250aa71dd1594363bc7083d25cfd0240e441b119 +SHA256: 5c3bc213c9eff85f98feceac8810b955f8415564e50e3889b447e847c50c5ba7 diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.pipeline/out b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.pipeline/out index b85485cd1a..b3c4bd3e31 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.pipeline/out +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.pipeline/out @@ -41,6 +41,14 @@ source: HTTP MD5: d903de7e30db1691d3130ba5eae6b9a7 SHA1: 81f5f056ce5e97d940854bb0c48017b45dd9f15e SHA256: 6fb22aa9d780ea63bd7a2e12b92b16fcbf1c4874f1d3e11309a5ba984433c315 +FILE_STATE_REMOVE +file #2, 94, 0 +[orig_h=192.168.1.104, orig_p=1673/tcp, resp_h=63.245.209.11, resp_p=80/tcp] +total bytes: 94 +source: HTTP +MD5: d903de7e30db1691d3130ba5eae6b9a7 +SHA1: 81f5f056ce5e97d940854bb0c48017b45dd9f15e +SHA256: 6fb22aa9d780ea63bd7a2e12b92b16fcbf1c4874f1d3e11309a5ba984433c315 FILE_NEW file #3, 0, 0 FILE_BOF_BUFFER diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.post/out b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.post/out index cedc396254..f130f2d270 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.post/out +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.post/out @@ -13,6 +13,14 @@ source: HTTP MD5: 5eb63bbbe01eeed093cb22bb8f5acdc3 SHA1: 2aae6c35c94fcfb415dbe95f408b9ce91ee846ed SHA256: b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 +FILE_STATE_REMOVE +file #0, 11, 0 +[orig_h=141.142.228.5, orig_p=53595/tcp, resp_h=54.243.55.129, resp_p=80/tcp] +total bytes: 11 +source: HTTP +MD5: 5eb63bbbe01eeed093cb22bb8f5acdc3 +SHA1: 2aae6c35c94fcfb415dbe95f408b9ce91ee846ed +SHA256: b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 FILE_NEW file #1, 0, 0 FILE_BOF_BUFFER @@ -28,3 +36,11 @@ source: HTTP MD5: c9337794df612aeaa901dcf9fa446bca SHA1: 6a1582672c203210c6d18d700322060b676365e7 SHA256: 8eb24c16df7cb45cb6a1790b0d26ad2571f754228d0ac111b3ac59adbfecbeb8 +FILE_STATE_REMOVE +file #1, 366, 0 +[orig_h=141.142.228.5, orig_p=53595/tcp, resp_h=54.243.55.129, resp_p=80/tcp] +total bytes: 366 +source: HTTP +MD5: c9337794df612aeaa901dcf9fa446bca +SHA1: 6a1582672c203210c6d18d700322060b676365e7 +SHA256: 8eb24c16df7cb45cb6a1790b0d26ad2571f754228d0ac111b3ac59adbfecbeb8 diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.logging/files.log b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.logging/files.log index 447d991f3e..643176a6b3 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.logging/files.log +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.logging/files.log @@ -3,8 +3,8 @@ #empty_field (empty) #unset_field - #path files -#open 2013-08-26-18-39-03 +#open 2014-01-05-09-08-10 #fields ts fuid tx_hosts rx_hosts conn_uids source depth analyzers mime_type filename duration local_orig is_orig seen_bytes total_bytes missing_bytes overflow_bytes timedout parent_fuid md5 sha1 sha256 extracted #types time string table[addr] table[addr] table[string] string count table[string] string string interval bool bool count count count count bool string string string string string 1362692527.009721 FakNcS1Jfe01uljb3 192.150.187.43 141.142.228.5 CXWv6p3arKYeMETxOg HTTP 0 SHA256,DATA_EVENT,MD5,EXTRACT,SHA1 text/plain - 0.000054 - F 4705 4705 0 0 F - 397168fd09991a0e712254df7bc639ac 1dd7ac0398df6cbc0696445a91ec681facf4dc47 4e7c7ef0984119447e743e3ec77e1de52713e345cde03fe7df753a35849bed18 FakNcS1Jfe01uljb3-file -#close 2013-08-26-18-39-03 +#close 2014-01-05-09-08-10 From 78b5f6b94b2d78d642165101183cbd7d20a49f75 Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Wed, 2 Apr 2014 23:03:24 -0400 Subject: [PATCH 003/299] BinPAC SSH analyzer basic functionality. --- scripts/base/protocols/ssh/README | 1 - scripts/base/protocols/ssh/__load__.bro | 2 +- scripts/base/protocols/ssh/main.bro | 212 +++++---------------- src/analyzer/protocol/CMakeLists.txt | 4 +- src/analyzer/protocol/ssh/CMakeLists.txt | 8 +- src/analyzer/protocol/ssh/Plugin.cc | 7 +- src/analyzer/protocol/ssh/SSH.cc | 198 +++++++++++-------- src/analyzer/protocol/ssh/SSH.h | 51 ++++- src/analyzer/protocol/ssh/events.bif | 51 ++--- src/analyzer/protocol/ssh/ssh-analyzer.pac | 25 +++ src/analyzer/protocol/ssh/ssh-protocol.pac | 175 +++++++++++++++++ src/analyzer/protocol/ssh/ssh.pac | 32 ++++ 12 files changed, 465 insertions(+), 301 deletions(-) delete mode 100644 scripts/base/protocols/ssh/README create mode 100644 src/analyzer/protocol/ssh/ssh-analyzer.pac create mode 100644 src/analyzer/protocol/ssh/ssh-protocol.pac create mode 100644 src/analyzer/protocol/ssh/ssh.pac diff --git a/scripts/base/protocols/ssh/README b/scripts/base/protocols/ssh/README deleted file mode 100644 index c3f68d543f..0000000000 --- a/scripts/base/protocols/ssh/README +++ /dev/null @@ -1 +0,0 @@ -Support for Secure Shell (SSH) protocol analysis. diff --git a/scripts/base/protocols/ssh/__load__.bro b/scripts/base/protocols/ssh/__load__.bro index 0f3cb011f8..9e43682d13 100644 --- a/scripts/base/protocols/ssh/__load__.bro +++ b/scripts/base/protocols/ssh/__load__.bro @@ -1,3 +1,3 @@ +# Generated by binpac_quickstart @load ./main - @load-sigs ./dpd.sig \ No newline at end of file diff --git a/scripts/base/protocols/ssh/main.bro b/scripts/base/protocols/ssh/main.bro index 33b0c84147..c3f90ec332 100644 --- a/scripts/base/protocols/ssh/main.bro +++ b/scripts/base/protocols/ssh/main.bro @@ -1,66 +1,31 @@ -##! Base SSH analysis script. The heuristic to blindly determine success or -##! failure for SSH connections is implemented here. At this time, it only -##! uses the size of the data being returned from the server to make the -##! heuristic determination about success of the connection. -##! Requires that :bro:id:`use_conn_size_analyzer` is set to T! The heuristic -##! is not attempted if the connection size analyzer isn't enabled. +##! Implements base functionality for SSH analysis. Generates the ssh.log file. -@load base/protocols/conn -@load base/frameworks/notice -@load base/utils/site -@load base/utils/thresholds -@load base/utils/conn-ids -@load base/utils/directions-and-hosts +# Generated by binpac_quickstart module SSH; export { - ## The SSH protocol logging stream identifier. redef enum Log::ID += { LOG }; type Info: record { - ## Time when the SSH connection began. - ts: time &log; + ## Timestamp for when the event happened. + ts: time &log; ## Unique ID for the connection. - uid: string &log; + uid: string &log; ## The connection's 4-tuple of endpoint addresses/ports. - id: conn_id &log; - ## Indicates if the login was heuristically guessed to be - ## "success", "failure", or "undetermined". - status: string &log &default="undetermined"; - ## Direction of the connection. If the client was a local host - ## logging into an external host, this would be OUTBOUND. INBOUND - ## would be set for the opposite situation. - # TODO: handle local-local and remote-remote better. - direction: Direction &log &optional; - ## Software string from the client. - client: string &log &optional; - ## Software string from the server. - server: string &log &optional; - ## Indicate if the SSH session is done being watched. - done: bool &default=F; + id: conn_id &log; + ## The client's version string + client: string &log &optional; + ## The server's version string + server: string &log &optional; + ## Auth result + result: string &log &optional; + ## Auth method + method: string &log &optional; }; - ## The size in bytes of data sent by the server at which the SSH - ## connection is presumed to be successful. - const authentication_data_size = 4000 &redef; - - ## If true, we tell the event engine to not look at further data - ## packets after the initial SSH handshake. Helps with performance - ## (especially with large file transfers) but precludes some - ## kinds of analyses. - const skip_processing_after_detection = F &redef; - - ## Event that is generated when the heuristic thinks that a login - ## was successful. - global heuristic_successful_login: event(c: connection); - - ## Event that is generated when the heuristic thinks that a login - ## failed. - global heuristic_failed_login: event(c: connection); - - ## Event that can be handled to access the :bro:type:`SSH::Info` - ## record as it is sent on to the logging framework. + ## Event that can be handled to access the SSH record as it is sent on + ## to the loggin framework. global log_ssh: event(rec: Info); } @@ -69,136 +34,55 @@ redef record connection += { }; const ports = { 22/tcp }; -redef likely_server_ports += { ports }; event bro_init() &priority=5 -{ + { Log::create_stream(SSH::LOG, [$columns=Info, $ev=log_ssh]); Analyzer::register_for_ports(Analyzer::ANALYZER_SSH, ports); -} - -function set_session(c: connection) - { - if ( ! c?$ssh ) - { - local info: Info; - info$ts=network_time(); - info$uid=c$uid; - info$id=c$id; - c$ssh = info; - } } -function check_ssh_connection(c: connection, done: bool) + +event ssh_version(c: connection, is_orig: bool, version: string) { - # If already done watching this connection, just return. - if ( c$ssh$done ) - return; - - if ( done ) + if ( !c?$ssh ) { - # If this connection is done, then we can look to see if - # this matches the conditions for a failed login. Failed - # logins are only detected at connection state removal. - - if ( # Require originators and responders to have sent at least 50 bytes. - c$orig$size > 50 && c$resp$size > 50 && - # Responders must be below 4000 bytes. - c$resp$size < authentication_data_size && - # Responder must have sent fewer than 40 packets. - c$resp$num_pkts < 40 && - # If there was a content gap we can't reliably do this heuristic. - c?$conn && c$conn$missed_bytes == 0 )# && - # Only "normal" connections can count. - #c$conn?$conn_state && c$conn$conn_state in valid_states ) - { - c$ssh$status = "failure"; - event SSH::heuristic_failed_login(c); - } - - if ( c$resp$size >= authentication_data_size ) - { - c$ssh$status = "success"; - event SSH::heuristic_successful_login(c); - } + local s: SSH::Info; + s$ts = network_time(); + s$uid = c$uid; + s$id = c$id; + c$ssh = s; } + if ( is_orig ) + c$ssh$client = version; else - { - # If this connection is still being tracked, then it's possible - # to watch for it to be a successful connection. - if ( c$resp$size >= authentication_data_size ) - { - c$ssh$status = "success"; - event SSH::heuristic_successful_login(c); - } - else - # This connection must be tracked longer. Let the scheduled - # check happen again. - return; - } - - # Set the direction for the log. - c$ssh$direction = Site::is_local_addr(c$id$orig_h) ? OUTBOUND : INBOUND; - - # Set the "done" flag to prevent the watching event from rescheduling - # after detection is done. - c$ssh$done=T; - - if ( skip_processing_after_detection ) - { - # Stop watching this connection, we don't care about it anymore. - skip_further_processing(c$id); - set_record_packets(c$id, F); - } + c$ssh$server = version; +# print c$ssh; } - -event heuristic_successful_login(c: connection) &priority=-5 +event ssh_auth_successful(c: connection, method: string) { - Log::write(SSH::LOG, c$ssh); - } - -event heuristic_failed_login(c: connection) &priority=-5 - { - Log::write(SSH::LOG, c$ssh); - } - -event connection_state_remove(c: connection) &priority=-5 - { - if ( c?$ssh ) - { - check_ssh_connection(c, T); - if ( c$ssh$status == "undetermined" ) - Log::write(SSH::LOG, c$ssh); - } - } - -event ssh_watcher(c: connection) - { - local id = c$id; - # don't go any further if this connection is gone already! - if ( ! connection_exists(id) ) + if ( !c?$ssh ) return; - - lookup_connection(c$id); - check_ssh_connection(c, F); - if ( ! c$ssh$done ) - schedule +15secs { ssh_watcher(c) }; + c$ssh$result = "success"; + c$ssh$method = method; + Log::write(SSH::LOG, c$ssh); } -event ssh_server_version(c: connection, version: string) &priority=5 +event ssh_auth_failed(c: connection, method: string) { - set_session(c); - c$ssh$server = version; + if ( !c?$ssh ) + return; + c$ssh$result = "failure"; + c$ssh$method = method; + Log::write(SSH::LOG, c$ssh); } -event ssh_client_version(c: connection, version: string) &priority=5 +event connection_closed(c: connection) { - set_session(c); - c$ssh$client = version; - - # The heuristic detection for SSH relies on the ConnSize analyzer. - # Don't do the heuristics if it's disabled. - if ( use_conn_size_analyzer ) - schedule +15secs { ssh_watcher(c) }; - } + if ( c?$ssh && !c$ssh?$result ) + { + c$ssh$result = "unknown"; + c$ssh$method = "unknown"; + Log::write(SSH::LOG, c$ssh); + } + } \ No newline at end of file diff --git a/src/analyzer/protocol/CMakeLists.txt b/src/analyzer/protocol/CMakeLists.txt index fc63aa4b66..59e33843ac 100644 --- a/src/analyzer/protocol/CMakeLists.txt +++ b/src/analyzer/protocol/CMakeLists.txt @@ -19,11 +19,11 @@ add_subdirectory(ident) add_subdirectory(interconn) add_subdirectory(irc) add_subdirectory(login) -add_subdirectory(modbus) add_subdirectory(mime) +add_subdirectory(modbus) add_subdirectory(ncp) -add_subdirectory(netflow) add_subdirectory(netbios) +add_subdirectory(netflow) add_subdirectory(ntp) add_subdirectory(pia) add_subdirectory(pop3) diff --git a/src/analyzer/protocol/ssh/CMakeLists.txt b/src/analyzer/protocol/ssh/CMakeLists.txt index 505c89332e..1266e4f496 100644 --- a/src/analyzer/protocol/ssh/CMakeLists.txt +++ b/src/analyzer/protocol/ssh/CMakeLists.txt @@ -1,9 +1,11 @@ +# Generated by binpac_quickstart include(BroPlugin) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) bro_plugin_begin(Bro SSH) -bro_plugin_cc(SSH.cc Plugin.cc) -bro_plugin_bif(events.bif) -bro_plugin_end() + bro_plugin_cc(SSH.cc Plugin.cc) + bro_plugin_bif(events.bif) + bro_plugin_pac(ssh.pac ssh-analyzer.pac ssh-protocol.pac) +bro_plugin_end() \ No newline at end of file diff --git a/src/analyzer/protocol/ssh/Plugin.cc b/src/analyzer/protocol/ssh/Plugin.cc index 53a0294a88..ddb01964f5 100644 --- a/src/analyzer/protocol/ssh/Plugin.cc +++ b/src/analyzer/protocol/ssh/Plugin.cc @@ -1,10 +1,11 @@ +// Generated by binpac_quickstart #include "plugin/Plugin.h" #include "SSH.h" BRO_PLUGIN_BEGIN(Bro, SSH) - BRO_PLUGIN_DESCRIPTION("SSH analyzer"); - BRO_PLUGIN_ANALYZER("SSH", ssh::SSH_Analyzer); + BRO_PLUGIN_DESCRIPTION("Secure Shell analyzer"); + BRO_PLUGIN_ANALYZER("SSH", SSH::SSH_Analyzer); BRO_PLUGIN_BIF_FILE(events); -BRO_PLUGIN_END +BRO_PLUGIN_END \ No newline at end of file diff --git a/src/analyzer/protocol/ssh/SSH.cc b/src/analyzer/protocol/ssh/SSH.cc index ab3f6a5e5b..caeb5c4ca7 100644 --- a/src/analyzer/protocol/ssh/SSH.cc +++ b/src/analyzer/protocol/ssh/SSH.cc @@ -1,105 +1,135 @@ -// See the file "COPYING" in the main distribution directory for copyright. +// Generated by binpac_quickstart -#include "config.h" - -#include - -#include "NetVar.h" #include "SSH.h" -#include "Event.h" -#include "analyzer/protocol/tcp/ContentLine.h" + +#include "analyzer/protocol/tcp/TCP_Reassembler.h" + +#include "Reporter.h" #include "events.bif.h" -using namespace analyzer::ssh; +using namespace analyzer::SSH; SSH_Analyzer::SSH_Analyzer(Connection* c) + : tcp::TCP_ApplicationAnalyzer("SSH", c) - { - orig = new tcp::ContentLine_Analyzer(c, true); - orig->SetSkipPartial(true); - orig->SetCRLFAsEOL(LF_as_EOL); - AddSupportAnalyzer(orig); - resp = new tcp::ContentLine_Analyzer(c, false); - resp->SetSkipPartial(true); - resp->SetCRLFAsEOL(LF_as_EOL); - AddSupportAnalyzer(resp); + { + interp = new binpac::SSH::SSH_Conn(this); + had_gap = false; + num_encrypted_packets_seen = 0; + } + +SSH_Analyzer::~SSH_Analyzer() + { + delete interp; } -void SSH_Analyzer::DeliverStream(int length, const u_char* data, bool is_orig) +void SSH_Analyzer::Done() { - tcp::TCP_ApplicationAnalyzer::DeliverStream(length, data, is_orig); + + tcp::TCP_ApplicationAnalyzer::Done(); - // We're all done processing this endpoint - flag it as such, - // before we even determine whether we have any event generation - // work to do, to make sure we don't do any further work on it. - if ( is_orig ) - orig->SetSkipDeliveries(true); - else - resp->SetSkipDeliveries(true); + interp->FlowEOF(true); + interp->FlowEOF(false); + + } - if ( TCP() ) +void SSH_Analyzer::EndpointEOF(bool is_orig) + { + tcp::TCP_ApplicationAnalyzer::EndpointEOF(is_orig); + interp->FlowEOF(is_orig); + } + +void SSH_Analyzer::DeliverStream(int len, const u_char* data, bool orig) + { + tcp::TCP_ApplicationAnalyzer::DeliverStream(len, data, orig); + + assert(TCP()); + if ( TCP()->IsPartial() ) + return; + + if ( had_gap ) + // If only one side had a content gap, we could still try to + // deliver data to the other side if the script layer can handle this. + return; + + if ( num_encrypted_packets_seen || interp->get_state(orig) == binpac::SSH::ENCRYPTED ) { - // Don't try to parse version if there has already been a gap. - tcp::TCP_Endpoint* endp = is_orig ? TCP()->Orig() : TCP()->Resp(); - if ( endp->HadGap() ) - return; - } - - const char* line = (const char*) data; - - // The SSH identification looks like this: - // - // SSH-.-\n - // - // We're interested in the "version" part here. - - if ( length < 4 || memcmp(line, "SSH-", 4) != 0 ) - { - Weird("malformed_ssh_identification"); - ProtocolViolation("malformed ssh identification", line, length); + ProcessEncrypted(len, orig); return; } - int i; - for ( i = 4; i < length && line[i] != '-'; ++i ) - ; - - if ( TCP() ) + try { - if ( length >= i ) - { - IPAddr dst; - - if ( is_orig ) - dst = TCP()->Orig()->dst_addr; - else - dst = TCP()->Resp()->dst_addr; - - if ( Conn()->VersionFoundEvent(dst, line + i, - length - i) ) - ProtocolConfirmation(); - else - ProtocolViolation("malformed ssh version", - line, length); - } - else - { - Weird("malformed_ssh_version"); - ProtocolViolation("malformed ssh version", line, length); - } + interp->NewData(orig, data, data + len); + } + catch ( const binpac::Exception& e ) + { + printf(" **** %s\n", e.c_msg()); + ProtocolViolation(fmt("Binpac exception: %s", e.c_msg())); } - - // Generate SSH events. - EventHandlerPtr event = is_orig ? - ssh_client_version : ssh_server_version; - if ( ! event ) - return; - - val_list* vl = new val_list; - vl->append(BuildConnVal()); - vl->append(new StringVal(length, line)); - - ConnectionEvent(event, vl); + } + +void SSH_Analyzer::Undelivered(int seq, int len, bool orig) + { + tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig); + had_gap = true; + interp->NewGap(orig, len); + } + +void SSH_Analyzer::ProcessEncrypted(int len, bool orig) + { + if (!num_encrypted_packets_seen) + { + initial_encrypted_packet_size = len; + } + // printf("Encrypted packet of size %d from %s.\n", len, orig?"client":"server"); + int relative_len = len - initial_encrypted_packet_size; + if ( num_encrypted_packets_seen >= 2 ) + { + int auth_result = AuthResult(relative_len, orig); + if ( auth_result > 0 ) + { + StringVal* method = new StringVal(AuthMethod(relative_len, orig)); + if ( auth_result == 1 ) + BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), method); + if ( auth_result == 2 ) + BifEvent::generate_ssh_auth_failed(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), method); + } + packet_n_2_is_orig = packet_n_1_is_orig; + packet_n_2_size = packet_n_1_size; + } + packet_n_1_is_orig = orig; + packet_n_1_size = relative_len; + num_encrypted_packets_seen++; + } + + +int SSH_Analyzer::AuthResult(int len, bool orig) + { + if ( orig && !packet_n_1_is_orig && packet_n_2_is_orig ) + { + if ( len == -16 ) + return 1; + else if ( len >= 16 && + len <= 32 ) + return 2; + return 0; + } + return -1; + } + +const char* SSH_Analyzer::AuthMethod(int len, bool orig) + { + if ( packet_n_1_size == 96 ) // Password auth + return "keyboard-interactive"; + if ( packet_n_1_size == 32 ) // Challenge-response auth + return "challenge-response"; + if ( packet_n_2_size >= 112 && + packet_n_2_size <= 432 ) // Public key auth + return "pubkey"; + if ( packet_n_2_size == 16 ) // Host-based auth + return "host-based"; + return fmt("unknown auth method: n-1=%d n-2=%d", packet_n_1_size, packet_n_2_size); } diff --git a/src/analyzer/protocol/ssh/SSH.h b/src/analyzer/protocol/ssh/SSH.h index 3878881693..7d391e8d66 100644 --- a/src/analyzer/protocol/ssh/SSH.h +++ b/src/analyzer/protocol/ssh/SSH.h @@ -1,25 +1,62 @@ -// See the file "COPYING" in the main distribution directory for copyright. +// Generated by binpac_quickstart #ifndef ANALYZER_PROTOCOL_SSH_SSH_H #define ANALYZER_PROTOCOL_SSH_SSH_H +#include "events.bif.h" + + #include "analyzer/protocol/tcp/TCP.h" -#include "analyzer/protocol/tcp/ContentLine.h" -namespace analyzer { namespace ssh { +#include "ssh_pac.h" + +namespace analyzer { namespace SSH { + +class SSH_Analyzer + +: public tcp::TCP_ApplicationAnalyzer { -class SSH_Analyzer : public tcp::TCP_ApplicationAnalyzer { public: SSH_Analyzer(Connection* conn); + virtual ~SSH_Analyzer(); + // Overriden from Analyzer. + virtual void Done(); + virtual void DeliverStream(int len, const u_char* data, bool orig); + virtual void Undelivered(int seq, int len, bool orig); + // Overriden from tcp::TCP_ApplicationAnalyzer. + virtual void EndpointEOF(bool is_orig); + static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn) { return new SSH_Analyzer(conn); } -private: - tcp::ContentLine_Analyzer* orig; - tcp::ContentLine_Analyzer* resp; + static bool Available() + { + // TODO: After you define your events, || them together here. + // See events.bif for more information + return ( ssh_event ); + } + +protected: + binpac::SSH::SSH_Conn* interp; + + void ProcessEncrypted(int len, bool orig); + int AuthResult(int len, bool orig); + const char* AuthMethod(int len, bool orig); + + bool had_gap; + + // Packet analysis stuff + int initial_encrypted_packet_size; + int num_encrypted_packets_seen; + + bool packet_n_1_is_orig; + int packet_n_1_size; + bool packet_n_2_is_orig; + int packet_n_2_size; + }; } } // namespace analyzer::* diff --git a/src/analyzer/protocol/ssh/events.bif b/src/analyzer/protocol/ssh/events.bif index 9d73f5e483..f1fa16919d 100644 --- a/src/analyzer/protocol/ssh/events.bif +++ b/src/analyzer/protocol/ssh/events.bif @@ -1,38 +1,17 @@ -## Generated when seeing an SSH client's version identification. The SSH -## protocol starts with a clear-text handshake message that reports client and -## server protocol/software versions. This event provides access to what the -## client sent. -## -## -## See `Wikipedia `__ for more -## information about the SSH protocol. -## -## c: The connection. -## -## version: The version string the client sent (e.g., `SSH-2.0-libssh-0.11`). -## -## .. bro:see:: ssh_server_version -## -## .. note:: As everything after the initial version handshake proceeds -## encrypted, Bro cannot further analyze SSH sessions. -event ssh_client_version%(c: connection, version: string%); +# Generated by binpac_quickstart -## Generated when seeing an SSH server's version identification. The SSH -## protocol starts with a clear-text handshake message that reports client and -## server protocol/software versions. This event provides access to what the -## server sent. -## -## See `Wikipedia `__ for more -## information about the SSH protocol. -## -## c: The connection. -## -## version: The version string the server sent (e.g., -## ``SSH-1.99-OpenSSH_3.9p1``). -## -## .. bro:see:: ssh_client_version -## -## .. note:: As everything coming after the initial version handshake proceeds -## encrypted, Bro cannot further analyze SSH sessions. -event ssh_server_version%(c: connection, version: string%); +# In this file, you'll define the events that your analyzer will generate. A sample event is included. +## Generated for SSH connections +## +## See `Google `__ for more information about SSH +## +## c: The connection +##3 +event ssh_event%(c: connection%); + +event ssh_version%(c: connection, is_orig: bool, version: string%); + +event ssh_auth_successful%(c: connection, method: string%); + +event ssh_auth_failed%(c: connection, method: string%); \ No newline at end of file diff --git a/src/analyzer/protocol/ssh/ssh-analyzer.pac b/src/analyzer/protocol/ssh/ssh-analyzer.pac new file mode 100644 index 0000000000..5cde754521 --- /dev/null +++ b/src/analyzer/protocol/ssh/ssh-analyzer.pac @@ -0,0 +1,25 @@ +# Generated by binpac_quickstart + +refine flow SSH_Flow += { + function proc_ssh_version(msg: SSH_Version): bool + %{ + BifEvent::generate_ssh_version(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), ${msg.is_orig}, + bytestring_to_val(${msg.version})); + return true; + %} + + function proc_newkeys(): bool + %{ + connection()->bro_analyzer()->ProtocolConfirmation(); + return true; + %} + +}; + +refine typeattr SSH_Version += &let { + proc: bool = $context.flow.proc_ssh_version(this); +}; + +refine typeattr SSH_Message += &let { + proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == SSH_MSG_NEWKEYS); +}; diff --git a/src/analyzer/protocol/ssh/ssh-protocol.pac b/src/analyzer/protocol/ssh/ssh-protocol.pac new file mode 100644 index 0000000000..84b1bc1f6a --- /dev/null +++ b/src/analyzer/protocol/ssh/ssh-protocol.pac @@ -0,0 +1,175 @@ +enum state { + VERSION_EXCHANGE = 0, + KEY_EXCHANGE_CLEARTEXT = 1, + ENCRYPTED = 2, +}; + +enum message_id { + SSH_MSG_DISCONNECT = 1, + SSH_MSG_IGNORE = 2, + SSH_MSG_UNIMPLEMENTED = 3, + SSH_MSG_DEBUG = 4, + SSH_MSG_SERVICE_REQUEST = 5, + SSH_MSG_SERVICE_ACCEPT = 6, + SSH_MSG_KEXINIT = 20, + SSH_MSG_NEWKEYS = 21, + SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30, + SSH_MSG_KEX_DH_GEX_GROUP = 31, + SSH_MSG_KEX_DH_GEX_INIT = 32, + SSH_MSG_KEX_DH_GEX_REPLY = 33, + SSH_MSG_KEX_DH_GEX_REQUEST = 34, + SSH_MSG_USERAUTH_REQUEST = 50, + SSH_MSG_USERAUTH_FAILURE = 51, + SSH_MSG_USERAUTH_SUCCESS = 52, + SSH_MSG_USERAUTH_BANNER = 53, + SSH_MSG_GLOBAL_REQUEST = 80, + SSH_MSG_REQUEST_SUCCESS = 81, + SSH_MSG_REQUEST_FAILURE = 82, + SSH_MSG_CHANNEL_OPEN = 90, + SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91, + SSH_MSG_CHANNEL_OPEN_FAILURE = 92, + SSH_MSG_CHANNEL_WINDOW_ADJUST = 93, + SSH_MSG_CHANNEL_DATA = 94, + SSH_MSG_CHANNEL_EXTENDED_DATA = 95, + SSH_MSG_CHANNEL_EOF = 96, + SSH_MSG_CHANNEL_CLOSE = 97, + SSH_MSG_CHANNEL_REQUEST = 98, + SSH_MSG_CHANNEL_SUCCESS = 99, + SSH_MSG_CHANNEL_FAILURE = 100, +}; + +type SSH_PDU(is_orig: bool) = case $context.connection.get_state(is_orig) of { + VERSION_EXCHANGE -> version: SSH_Version(is_orig); + KEY_EXCHANGE_CLEARTEXT -> kex: SSH_Key_Exchange(is_orig); + ENCRYPTED -> unk: bytestring &length=100; +} &byteorder=bigendian; + +type SSH_Version(is_orig: bool) = record { + version: bytestring &oneline; +} &let { + update_state: bool = $context.connection.update_state(KEY_EXCHANGE_CLEARTEXT, is_orig); +}; + +type SSH_Key_Exchange_Header(is_orig: bool) = record { + packet_length: uint32; + padding_length: uint8; +} &length=5; + +type SSH_Key_Exchange(is_orig: bool) = record { + header : SSH_Key_Exchange_Header(is_orig); + payload: SSH_Payload(is_orig, header.packet_length - header.padding_length - 2); + pad : bytestring &length=header.padding_length; +}; + +type SSH_Payload_Header(length: uint32) = record { + message_type: uint8; +} &length=1; + +type SSH_Payload(is_orig: bool, packet_length: uint32) = record { + header: SSH_Payload_Header(packet_length); + message: SSH_Message(is_orig, header.message_type, packet_length); +}; + +type SSH_Message(is_orig: bool, msg_type: uint8, packet_length: uint32) = case msg_type of { + SSH_MSG_KEXINIT -> kexinit: SSH_KEXINIT(is_orig, packet_length); + SSH_MSG_KEX_DH_GEX_REQUEST -> dh_gex_request: SSH_DH_GEX_REQUEST(is_orig, packet_length); + SSH_MSG_KEX_DH_GEX_GROUP -> dh_gex_group: SSH_DH_GEX_GROUP(is_orig, packet_length); + SSH_MSG_KEX_DH_GEX_INIT -> dh_gex_init: SSH_DH_GEX_INIT(is_orig, packet_length); + SSH_MSG_KEX_DH_GEX_REPLY -> dh_gex_reply: SSH_DH_GEX_REPLY(is_orig, packet_length); + default -> unknown: bytestring &length=packet_length; +} &let { + detach: bool = $context.connection.update_state(ENCRYPTED, is_orig) &if(msg_type == SSH_MSG_NEWKEYS); +}; + +type SSH_KEXINIT(is_orig: bool, length: uint32) = record { + cookie : bytestring &length=16; + kex_algorithms_len : uint32; + kex_algorithms : bytestring &length=kex_algorithms_len; + server_host_key_algorithms_len : uint32; + server_host_key_algorithms : bytestring &length=server_host_key_algorithms_len; + encryption_algorithms_client_to_server_len : uint32; + encryption_algorithms_client_to_server : bytestring &length=encryption_algorithms_client_to_server_len; + encryption_algorithms_server_to_client_len : uint32; + encryption_algorithms_server_to_client : bytestring &length=encryption_algorithms_server_to_client_len; + mac_algorithms_client_to_server_len : uint32; + mac_algorithms_client_to_server : bytestring &length=mac_algorithms_client_to_server_len; + mac_algorithms_server_to_client_len : uint32; + mac_algorithms_server_to_client : bytestring &length=mac_algorithms_server_to_client_len; + compression_algorithms_client_to_server_len : uint32; + compression_algorithms_client_to_server : bytestring &length=compression_algorithms_client_to_server_len; + compression_algorithms_server_to_client_len : uint32; + compression_algorithms_server_to_client : bytestring &length=compression_algorithms_server_to_client_len; + languages_client_to_server_len : uint32; + languages_client_to_server : bytestring &length=languages_client_to_server_len; + languages_server_to_client_len : uint32; + languages_server_to_client : bytestring &length=languages_server_to_client_len; + first_kex_packet_follows : uint8; + reserved : uint32; +} &length=length; + +type SSH_DH_GEX_REQUEST(is_orig: bool, length: uint32) = record { + min: uint32; + n : uint32; + max: uint32; +} &length=12; + +type SSH_DH_GEX_GROUP(is_orig: bool, length: uint32) = record { + p: mpint; + g: mpint; +} &length=length; + +type SSH_DH_GEX_INIT(is_orig: bool, length: uint32) = record { + e: mpint; +} &length=length; + +type SSH_DH_GEX_REPLY(is_orig: bool, length: uint32) = record { + k_s : ssh_string; + f : mpint; + signature: ssh_string; +} &length=length; + +#type SSH_NEWKEYS(is_orig: bool, length: uint32) = record { +# blah: ; +#} &let { +# detach: bool = $context.connection.detach(); +#} &length=0; + +type mpint = record { + len: uint32; + val: bytestring &length=len; +}; + +type ssh_string = record { + len: uint32; + val: bytestring &length=len; +}; + +refine connection SSH_Conn += { + %member{ + int state_up_; + int state_down_; + %} + + %init{ + state_up_ = VERSION_EXCHANGE; + state_down_ = VERSION_EXCHANGE; + %} + + function get_state(is_orig: bool): int + %{ + if ( is_orig ) + return state_up_; + else + return state_down_; + %} + + function update_state(s: state, is_orig: bool): bool + %{ + if ( is_orig ) + state_up_ = s; + else + state_down_ = s; + return true; + %} + +}; \ No newline at end of file diff --git a/src/analyzer/protocol/ssh/ssh.pac b/src/analyzer/protocol/ssh/ssh.pac new file mode 100644 index 0000000000..b3181c4fa1 --- /dev/null +++ b/src/analyzer/protocol/ssh/ssh.pac @@ -0,0 +1,32 @@ +# Generated by binpac_quickstart + +# Analyzer for Secure Shell +# - ssh-protocol.pac: describes the SSH protocol messages +# - ssh-analyzer.pac: describes the SSH analyzer code + +%include binpac.pac +%include bro.pac + +%extern{ + #include "events.bif.h" +%} + +analyzer SSH withcontext { + connection: SSH_Conn; + flow: SSH_Flow; +}; + +# Our connection consists of two flows, one in each direction. +connection SSH_Conn(bro_analyzer: BroAnalyzer) { + upflow = SSH_Flow(true); + downflow = SSH_Flow(false); +}; + +%include ssh-protocol.pac + +# Now we define the flow: +flow SSH_Flow(is_orig: bool) { + flowunit = SSH_PDU(is_orig) withcontext(connection, this); +}; + +%include ssh-analyzer.pac \ No newline at end of file From 2698fcea8eb5f7e83ddaa6478a20f835bf3b4761 Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Tue, 22 Apr 2014 18:26:39 -0400 Subject: [PATCH 004/299] SSH: Various updates. --- scripts/base/protocols/ssh/main.bro | 36 ++++++++++++---- src/analyzer/protocol/ssh/SSH.cc | 42 +++++++++++------- src/analyzer/protocol/ssh/SSH.h | 9 ++-- src/analyzer/protocol/ssh/events.bif | 20 +++------ src/analyzer/protocol/ssh/ssh-analyzer.pac | 50 ++++++++++++++++++++-- src/analyzer/protocol/ssh/ssh-protocol.pac | 19 +++++--- 6 files changed, 126 insertions(+), 50 deletions(-) diff --git a/scripts/base/protocols/ssh/main.bro b/scripts/base/protocols/ssh/main.bro index c3f90ec332..b9768b6dbb 100644 --- a/scripts/base/protocols/ssh/main.bro +++ b/scripts/base/protocols/ssh/main.bro @@ -18,6 +18,8 @@ export { client: string &log &optional; ## The server's version string server: string &log &optional; + ## The server's key fingerprint + host_key: string &log &optional; ## Auth result result: string &log &optional; ## Auth method @@ -42,7 +44,7 @@ event bro_init() &priority=5 } -event ssh_version(c: connection, is_orig: bool, version: string) +event ssh_server_version(c: connection, version: string) { if ( !c?$ssh ) { @@ -52,16 +54,25 @@ event ssh_version(c: connection, is_orig: bool, version: string) s$id = c$id; c$ssh = s; } - if ( is_orig ) - c$ssh$client = version; - else - c$ssh$server = version; -# print c$ssh; + c$ssh$server = version; + } + +event ssh_client_version(c: connection, version: string) + { + if ( !c?$ssh ) + { + local s: SSH::Info; + s$ts = network_time(); + s$uid = c$uid; + s$id = c$id; + c$ssh = s; + } + c$ssh$client = version; } event ssh_auth_successful(c: connection, method: string) { - if ( !c?$ssh ) + if ( !c?$ssh || ( c$ssh?$result && c$ssh$result == "success" ) ) return; c$ssh$result = "success"; c$ssh$method = method; @@ -70,7 +81,7 @@ event ssh_auth_successful(c: connection, method: string) event ssh_auth_failed(c: connection, method: string) { - if ( !c?$ssh ) + if ( !c?$ssh || ( c$ssh?$result && c$ssh$result == "success" ) ) return; c$ssh$result = "failure"; c$ssh$method = method; @@ -85,4 +96,13 @@ event connection_closed(c: connection) c$ssh$method = "unknown"; Log::write(SSH::LOG, c$ssh); } + } + +event ssh_server_host_key(c: connection, key: string) + { + if ( !c?$ssh ) + return; + local lx = str_split(md5_hash(key), vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30)); + lx[0] = ""; + c$ssh$host_key = sub(join_string_vec(lx, ":"), /:/, ""); } \ No newline at end of file diff --git a/src/analyzer/protocol/ssh/SSH.cc b/src/analyzer/protocol/ssh/SSH.cc index caeb5c4ca7..c89a77b9e7 100644 --- a/src/analyzer/protocol/ssh/SSH.cc +++ b/src/analyzer/protocol/ssh/SSH.cc @@ -18,6 +18,8 @@ SSH_Analyzer::SSH_Analyzer(Connection* c) interp = new binpac::SSH::SSH_Conn(this); had_gap = false; num_encrypted_packets_seen = 0; + initial_client_packet_size = 0; + initial_server_packet_size = 0; } SSH_Analyzer::~SSH_Analyzer() @@ -54,7 +56,7 @@ void SSH_Analyzer::DeliverStream(int len, const u_char* data, bool orig) // deliver data to the other side if the script layer can handle this. return; - if ( num_encrypted_packets_seen || interp->get_state(orig) == binpac::SSH::ENCRYPTED ) + if ( interp->get_state(orig) == binpac::SSH::ENCRYPTED ) { ProcessEncrypted(len, orig); return; @@ -80,23 +82,33 @@ void SSH_Analyzer::Undelivered(int seq, int len, bool orig) void SSH_Analyzer::ProcessEncrypted(int len, bool orig) { - if (!num_encrypted_packets_seen) - { - initial_encrypted_packet_size = len; - } - // printf("Encrypted packet of size %d from %s.\n", len, orig?"client":"server"); - int relative_len = len - initial_encrypted_packet_size; - if ( num_encrypted_packets_seen >= 2 ) + if (orig && !initial_client_packet_size) + initial_client_packet_size = len; + if (!orig && !initial_server_packet_size) + initial_server_packet_size = len; + + int relative_len; + if (orig) + relative_len = len - initial_client_packet_size; + else + relative_len = len - initial_server_packet_size; + // printf("Encrypted packet of length %d from %s.\n", len, orig?"client":"server"); + if ( num_encrypted_packets_seen >= 4 ) { int auth_result = AuthResult(relative_len, orig); if ( auth_result > 0 ) { + num_encrypted_packets_seen = 1; + //printf("Have auth\n"); StringVal* method = new StringVal(AuthMethod(relative_len, orig)); if ( auth_result == 1 ) BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), method); if ( auth_result == 2 ) BifEvent::generate_ssh_auth_failed(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), method); } + } + if ( num_encrypted_packets_seen >= 2 ) + { packet_n_2_is_orig = packet_n_1_is_orig; packet_n_2_size = packet_n_1_size; } @@ -108,7 +120,7 @@ void SSH_Analyzer::ProcessEncrypted(int len, bool orig) int SSH_Analyzer::AuthResult(int len, bool orig) { - if ( orig && !packet_n_1_is_orig && packet_n_2_is_orig ) + if ( !orig && packet_n_1_is_orig && !packet_n_2_is_orig ) { if ( len == -16 ) return 1; @@ -123,13 +135,13 @@ int SSH_Analyzer::AuthResult(int len, bool orig) const char* SSH_Analyzer::AuthMethod(int len, bool orig) { if ( packet_n_1_size == 96 ) // Password auth - return "keyboard-interactive"; - if ( packet_n_1_size == 32 ) // Challenge-response auth - return "challenge-response"; + return fmt("password (L=%d, L-1=%d, L-2=%d)", len, packet_n_1_size, packet_n_2_size); + if ( packet_n_1_size == 32 && ( packet_n_2_size == 0 || packet_n_2_size == 48 ) ) // Challenge-response auth + return fmt("challenge-response (L=%d, L-1=%d, L-2=%d)", len, packet_n_1_size, packet_n_2_size); if ( packet_n_2_size >= 112 && packet_n_2_size <= 432 ) // Public key auth - return "pubkey"; + return fmt("pubkey (L=%d, L-1=%d, L-2=%d)", len, packet_n_1_size, packet_n_2_size); if ( packet_n_2_size == 16 ) // Host-based auth - return "host-based"; - return fmt("unknown auth method: n-1=%d n-2=%d", packet_n_1_size, packet_n_2_size); + return fmt("host-based (L=%d, L-1=%d, L-2=%d)", len, packet_n_1_size, packet_n_2_size); + return fmt("unknown (L=%d, L-1=%d, L-2=%d)", len, packet_n_1_size, packet_n_2_size); } diff --git a/src/analyzer/protocol/ssh/SSH.h b/src/analyzer/protocol/ssh/SSH.h index 7d391e8d66..b0d8aade57 100644 --- a/src/analyzer/protocol/ssh/SSH.h +++ b/src/analyzer/protocol/ssh/SSH.h @@ -34,9 +34,9 @@ public: static bool Available() { - // TODO: After you define your events, || them together here. - // See events.bif for more information - return ( ssh_event ); + return ( ssh_server_version || ssh_client_version || + ssh_auth_successful || ssh_auth_failed || + ssh_server_capabilities || ssh_server_host_key ); } protected: @@ -49,7 +49,8 @@ protected: bool had_gap; // Packet analysis stuff - int initial_encrypted_packet_size; + int initial_client_packet_size; + int initial_server_packet_size; int num_encrypted_packets_seen; bool packet_n_1_is_orig; diff --git a/src/analyzer/protocol/ssh/events.bif b/src/analyzer/protocol/ssh/events.bif index f1fa16919d..cefb591a6e 100644 --- a/src/analyzer/protocol/ssh/events.bif +++ b/src/analyzer/protocol/ssh/events.bif @@ -1,17 +1,11 @@ -# Generated by binpac_quickstart +event ssh_server_version%(c: connection, version: string%); -# In this file, you'll define the events that your analyzer will generate. A sample event is included. - -## Generated for SSH connections -## -## See `Google `__ for more information about SSH -## -## c: The connection -##3 -event ssh_event%(c: connection%); - -event ssh_version%(c: connection, is_orig: bool, version: string%); +event ssh_client_version%(c: connection, version: string%); event ssh_auth_successful%(c: connection, method: string%); -event ssh_auth_failed%(c: connection, method: string%); \ No newline at end of file +event ssh_auth_failed%(c: connection, method: string%); + +event ssh_server_capabilities%(c: connection, kex_algorithms: string, server_host_key_algorithms: string, encryption_algorithms_client_to_server: string, encryption_algorithms_server_to_client: string, mac_algorithms_client_to_server: string, mac_algorithms_server_to_client: string, compression_algorithms_client_to_server: string, compression_algorithms_server_to_client: string, languages_client_to_server: string, languages_server_to_client: string%); + +event ssh_server_host_key%(c: connection, key: string%); \ No newline at end of file diff --git a/src/analyzer/protocol/ssh/ssh-analyzer.pac b/src/analyzer/protocol/ssh/ssh-analyzer.pac index 5cde754521..05cf20d4b4 100644 --- a/src/analyzer/protocol/ssh/ssh-analyzer.pac +++ b/src/analyzer/protocol/ssh/ssh-analyzer.pac @@ -3,8 +3,34 @@ refine flow SSH_Flow += { function proc_ssh_version(msg: SSH_Version): bool %{ - BifEvent::generate_ssh_version(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), ${msg.is_orig}, - bytestring_to_val(${msg.version})); + if ( ssh_client_version && ${msg.is_orig } ) + BifEvent::generate_ssh_client_version(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), bytestring_to_val(${msg.version})); + else if ( ssh_server_version ) + BifEvent::generate_ssh_server_version(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), bytestring_to_val(${msg.version})); + return true; + %} + + function proc_ssh_kexinit(msg: SSH_KEXINIT): bool + %{ + if ( ssh_server_capabilities ) + BifEvent::generate_ssh_server_capabilities(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), + bytestring_to_val(${msg.kex_algorithms}), bytestring_to_val(${msg.server_host_key_algorithms}), + bytestring_to_val(${msg.encryption_algorithms_client_to_server}), + bytestring_to_val(${msg.encryption_algorithms_server_to_client}), + bytestring_to_val(${msg.mac_algorithms_client_to_server}), + bytestring_to_val(${msg.mac_algorithms_server_to_client}), + bytestring_to_val(${msg.compression_algorithms_client_to_server}), + bytestring_to_val(${msg.compression_algorithms_server_to_client}), + bytestring_to_val(${msg.languages_client_to_server}), + bytestring_to_val(${msg.languages_server_to_client})); + return true; + %} + + function proc_ssh_server_host_key(key: bytestring): bool + %{ + if ( ssh_server_host_key ) + BifEvent::generate_ssh_server_host_key(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), + bytestring_to_val(${key})); return true; %} @@ -14,12 +40,30 @@ refine flow SSH_Flow += { return true; %} + function debug(loc: uint8): bool + %{ + printf("DEBUG: %d", loc); + return true; + %} + }; refine typeattr SSH_Version += &let { proc: bool = $context.flow.proc_ssh_version(this); }; +refine typeattr SSH_KEXINIT += &let { + proc: bool = $context.flow.proc_ssh_kexinit(this); +}; + +refine typeattr SSH_DH_GEX_REPLY += &let { + proc: bool = $context.flow.proc_ssh_server_host_key(k_s.val); +}; + +refine typeattr SSH_DH_GEX_GROUP += &let { + proc: bool = $context.flow.proc_ssh_server_host_key(p.val); +}; + refine typeattr SSH_Message += &let { proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == SSH_MSG_NEWKEYS); -}; +}; \ No newline at end of file diff --git a/src/analyzer/protocol/ssh/ssh-protocol.pac b/src/analyzer/protocol/ssh/ssh-protocol.pac index 84b1bc1f6a..aea112ff10 100644 --- a/src/analyzer/protocol/ssh/ssh-protocol.pac +++ b/src/analyzer/protocol/ssh/ssh-protocol.pac @@ -41,7 +41,7 @@ enum message_id { type SSH_PDU(is_orig: bool) = case $context.connection.get_state(is_orig) of { VERSION_EXCHANGE -> version: SSH_Version(is_orig); KEY_EXCHANGE_CLEARTEXT -> kex: SSH_Key_Exchange(is_orig); - ENCRYPTED -> unk: bytestring &length=100; + ENCRYPTED -> ciphertext: bytestring &length=1 &transient; } &byteorder=bigendian; type SSH_Version(is_orig: bool) = record { @@ -71,12 +71,13 @@ type SSH_Payload(is_orig: bool, packet_length: uint32) = record { }; type SSH_Message(is_orig: bool, msg_type: uint8, packet_length: uint32) = case msg_type of { - SSH_MSG_KEXINIT -> kexinit: SSH_KEXINIT(is_orig, packet_length); - SSH_MSG_KEX_DH_GEX_REQUEST -> dh_gex_request: SSH_DH_GEX_REQUEST(is_orig, packet_length); - SSH_MSG_KEX_DH_GEX_GROUP -> dh_gex_group: SSH_DH_GEX_GROUP(is_orig, packet_length); - SSH_MSG_KEX_DH_GEX_INIT -> dh_gex_init: SSH_DH_GEX_INIT(is_orig, packet_length); - SSH_MSG_KEX_DH_GEX_REPLY -> dh_gex_reply: SSH_DH_GEX_REPLY(is_orig, packet_length); - default -> unknown: bytestring &length=packet_length; + SSH_MSG_KEXINIT -> kexinit: SSH_KEXINIT(is_orig, packet_length); + SSH_MSG_KEX_DH_GEX_REQUEST -> dh_gex_request: SSH_DH_GEX_REQUEST(is_orig, packet_length); + SSH_MSG_KEX_DH_GEX_REQUEST_OLD -> dh_gex_request_old: SSH_DH_GEX_REQUEST_OLD(is_orig, packet_length); + SSH_MSG_KEX_DH_GEX_GROUP -> dh_gex_group: SSH_DH_GEX_GROUP(is_orig, packet_length); + SSH_MSG_KEX_DH_GEX_INIT -> dh_gex_init: SSH_DH_GEX_INIT(is_orig, packet_length); + SSH_MSG_KEX_DH_GEX_REPLY -> dh_gex_reply: SSH_DH_GEX_REPLY(is_orig, packet_length); + SSH_MSG_NEWKEYS -> new_keys: bytestring &length=packet_length; } &let { detach: bool = $context.connection.update_state(ENCRYPTED, is_orig) &if(msg_type == SSH_MSG_NEWKEYS); }; @@ -113,6 +114,10 @@ type SSH_DH_GEX_REQUEST(is_orig: bool, length: uint32) = record { max: uint32; } &length=12; +type SSH_DH_GEX_REQUEST_OLD(is_orig: bool, length: uint32) = record { + payload: bytestring &length=length; +} &length=length; + type SSH_DH_GEX_GROUP(is_orig: bool, length: uint32) = record { p: mpint; g: mpint; From cd81eaedca4fca6cff14a9189af4e00e97c470da Mon Sep 17 00:00:00 2001 From: Hui Lin Date: Tue, 5 Aug 2014 15:43:33 -0500 Subject: [PATCH 005/299] modify DNP3.cc and DNP3.h to add DNP3_UDP_Analyzer; binpac unchanged --- src/analyzer/protocol/dnp3/DNP3.cc | 270 +++++++++++++++++++++++++++++ src/analyzer/protocol/dnp3/DNP3.h | 47 +++++ 2 files changed, 317 insertions(+) diff --git a/src/analyzer/protocol/dnp3/DNP3.cc b/src/analyzer/protocol/dnp3/DNP3.cc index 9d9ddf0c35..7931b31462 100644 --- a/src/analyzer/protocol/dnp3/DNP3.cc +++ b/src/analyzer/protocol/dnp3/DNP3.cc @@ -112,6 +112,10 @@ const unsigned int PSEUDO_LINK_LAYER_LEN = 8; // length of DNP3 Pseudo Link Lay bool DNP3_Analyzer::crc_table_initialized = false; unsigned int DNP3_Analyzer::crc_table[256]; +bool DNP3_UDP_Analyzer::crc_table_initialized = false; +unsigned int DNP3_UDP_Analyzer::crc_table[256]; + + DNP3_Analyzer::DNP3_Analyzer(Connection* c) : TCP_ApplicationAnalyzer("DNP3", c) { interp = new binpac::DNP3::DNP3_Conn(this); @@ -374,3 +378,269 @@ unsigned int DNP3_Analyzer::CalcCRC(int len, const u_char* data) return ~crc & 0xFFFF; } + +// ?? For DNP3 over UDP analyzer. most of the codes are copied and pasted. Better way to reuse the code? + +DNP3_UDP_Analyzer::DNP3_UDP_Analyzer(Connection* c) : Analyzer("DHCP", c) + { + interp = new binpac::DNP3::DNP3_Conn(this); + + ClearEndpointState(true); + ClearEndpointState(false); + + if ( ! crc_table_initialized ) + PrecomputeCRCTable(); + } + +DNP3_UDP_Analyzer::~DNP3_UDP_Analyzer() + { + delete interp; + } + +void DNP3_UDP_Analyzer::Done() + { + Analyzer::Done(); + + interp->FlowEOF(true); + interp->FlowEOF(false); + } + +void DNP3_UDP_Analyzer::DeliverStream(int len, const u_char* data, bool orig) + { + Analyzer::DeliverStream(len, data, orig); + + try + { + if ( ! ProcessData(len, data, orig) ) + SetSkip(1); + } + + catch ( const binpac::Exception& e ) + { + SetSkip(1); + throw; + } + } +/* +void DNP3_UDP_Analyzer::Undelivered(uint64 seq, int len, bool orig) + { + Analyzer::Undelivered(seq, len, orig); + interp->NewGap(orig, len); + } + +void DNP3_UDP_Analyzer::EndpointEOF(bool is_orig) + { + Analyzer::EndpointEOF(is_orig); + interp->FlowEOF(is_orig); + } +*/ + +bool DNP3_UDP_Analyzer::ProcessData(int len, const u_char* data, bool orig) + { + Endpoint* endp = orig ? &orig_state : &resp_state; + + while ( len ) + { + if ( endp->in_hdr ) + { + // We're parsing the DNP3 header and link layer, get that in full. + if ( ! AddToBuffer(endp, PSEUDO_APP_LAYER_INDEX, &data, &len) ) + return true; + + // The first two bytes must always be 0x0564. + if( endp->buffer[0] != 0x05 || endp->buffer[1] != 0x64 ) + { + Weird("dnp3_header_lacks_magic"); + return false; + } + + // Make sure header checksum is correct. + if ( ! CheckCRC(PSEUDO_LINK_LAYER_LEN, endp->buffer, endp->buffer + PSEUDO_LINK_LAYER_LEN, "header") ) + { + ProtocolViolation("broken_checksum"); + return false; + } + + // If the checksum works out, we're pretty certainly DNP3. + ProtocolConfirmation(); + + // DNP3 packets without transport and application + // layers can happen, we ignore them. + if ( (endp->buffer[PSEUDO_LENGTH_INDEX] + 3) == (char)PSEUDO_LINK_LAYER_LEN ) + { + ClearEndpointState(orig); + return true; + } + + // Double check the direction in case the first + // received packet is a response. + u_char ctrl = endp->buffer[PSEUDO_CONTROL_FIELD_INDEX]; + + if ( orig != (bool)(ctrl & 0x80) ) + Weird("dnp3_unexpected_flow_direction"); + + // Update state. + endp->pkt_length = endp->buffer[PSEUDO_LENGTH_INDEX]; + endp->tpflags = endp->buffer[PSEUDO_TRANSPORT_INDEX]; + endp->in_hdr = false; // Now parsing application layer. + + // For the first packet, we submit the header to + // BinPAC. + if ( ++endp->pkt_cnt == 1 ) + interp->NewData(orig, endp->buffer, endp->buffer + PSEUDO_LINK_LAYER_LEN); + } + + if ( ! endp->in_hdr ) + { + assert(endp->pkt_length); + + // We're parsing the DNP3 application layer, get that + // in full now as well. We calculate the number of + // raw bytes the application layer consists of from + // the packet length by determining how much 16-byte + // chunks fit in there, and then add 2 bytes CRC for + // each. + int n = PSEUDO_APP_LAYER_INDEX + (endp->pkt_length - 5) + ((endp->pkt_length - 5) / 16) * 2 + 2 - 1; + + if ( ! AddToBuffer(endp, n, &data, &len) ) + return true; + + // Parse the the application layer data. + if ( ! ParseAppLayer(endp) ) + return false; + + // Done with this packet, prepare for next. + endp->buffer_len = 0; + endp->in_hdr = true; + } + } + + return true; + } + +bool DNP3_UDP_Analyzer::AddToBuffer(Endpoint* endp, int target_len, const u_char** data, int* len) + { + if ( ! target_len ) + return true; + + int to_copy = min(*len, target_len - endp->buffer_len); + + memcpy(endp->buffer + endp->buffer_len, *data, to_copy); + *data += to_copy; + *len -= to_copy; + endp->buffer_len += to_copy; + + return endp->buffer_len == target_len; + } + +bool DNP3_UDP_Analyzer::ParseAppLayer(Endpoint* endp) + { + bool orig = (endp == &orig_state); + binpac::DNP3::DNP3_Flow* flow = orig ? interp->upflow() : interp->downflow(); + + u_char* data = endp->buffer + PSEUDO_TRANSPORT_INDEX; // The transport layer byte counts as app-layer it seems. + int len = endp->pkt_length - 5; + + // DNP3 Packet : DNP3 Pseudo Link Layer | DNP3 Pseudo Transport Layer | DNP3 Pseudo Application Layer + // DNP3 Serial Transport Layer data is always 1 byte. + // Get FIN FIR seq field in transport header. + // FIR indicate whether the following DNP3 Serial Application Layer is first chunk of bytes or not. + // FIN indicate whether the following DNP3 Serial Application Layer is last chunk of bytes or not. + + int is_first = (endp->tpflags & 0x40) >> 6; // Initial chunk of data in this packet. + int is_last = (endp->tpflags & 0x80) >> 7; // Last chunk of data in this packet. + + int transport = PSEUDO_TRANSPORT_LEN; + + int i = 0; + while ( len > 0 ) + { + int n = min(len, 16); + + // Make sure chunk has a correct checksum. + if ( ! CheckCRC(n, data, data + n, "app_chunk") ) + return false; + + // Pass on to BinPAC. + assert(data + n < endp->buffer + endp->buffer_len); + flow->flow_buffer()->BufferData(data + transport, data + n); + transport = 0; + + data += n + 2; + len -= n; + } + + if ( is_first ) + endp->encountered_first_chunk = true; + + if ( ! is_first && ! endp->encountered_first_chunk ) + { + // We lost the first chunk. + Weird("dnp3_first_application_layer_chunk_missing"); + return false; + } + + if ( is_last ) + { + flow->flow_buffer()->FinishBuffer(); + flow->FlowEOF(); + ClearEndpointState(orig); + } + + return true; + } + +void DNP3_UDP_Analyzer::ClearEndpointState(bool orig) + { + Endpoint* endp = orig ? &orig_state : &resp_state; + binpac::DNP3::DNP3_Flow* flow = orig ? interp->upflow() : interp->downflow(); + + endp->in_hdr = true; + endp->encountered_first_chunk = false; + endp->buffer_len = 0; + endp->pkt_length = 0; + endp->tpflags = 0; + endp->pkt_cnt = 0; + } + +bool DNP3_UDP_Analyzer::CheckCRC(int len, const u_char* data, const u_char* crc16, const char* where) + { + unsigned int crc = CalcCRC(len, data); + + if ( crc16[0] == (crc & 0xff) && crc16[1] == (crc & 0xff00) >> 8 ) + return true; + + Weird(fmt("dnp3_corrupt_%s_checksum", where)); + return false; + } + +void DNP3_UDP_Analyzer::PrecomputeCRCTable() + { + for( unsigned int i = 0; i < 256; i++) + { + unsigned int crc = i; + + for ( unsigned int j = 0; j < 8; ++j ) + { + if ( crc & 0x0001 ) + crc = (crc >> 1) ^ 0xA6BC; // Generating polynomial. + else + crc >>= 1; + } + + crc_table[i] = crc; + } + } + +unsigned int DNP3_UDP_Analyzer::CalcCRC(int len, const u_char* data) + { + unsigned int crc = 0x0000; + + for ( int i = 0; i < len; i++ ) + { + unsigned int index = (crc ^ data[i]) & 0xFF; + crc = crc_table[index] ^ (crc >> 8); + } + + return ~crc & 0xFFFF; + } diff --git a/src/analyzer/protocol/dnp3/DNP3.h b/src/analyzer/protocol/dnp3/DNP3.h index 9cccf04d4d..ff3aff3594 100644 --- a/src/analyzer/protocol/dnp3/DNP3.h +++ b/src/analyzer/protocol/dnp3/DNP3.h @@ -3,6 +3,8 @@ #define ANALYZER_PROTOCOL_DNP3_DNP3_H #include "analyzer/protocol/tcp/TCP.h" +#include "analyzer/protocol/udp/UDP.h" + #include "dnp3_pac.h" namespace analyzer { namespace dnp3 { @@ -51,6 +53,51 @@ private: static unsigned int crc_table[256]; }; +class DNP3_UDP_Analyzer : public analyzer::Analyzer { +public: + DNP3_UDP_Analyzer(Connection* conn); + virtual ~DNP3_UDP_Analyzer(); + + virtual void Done(); + virtual void DeliverStream(int len, const u_char* data, bool orig); + //virtual void Undelivered(uint64 seq, int len, bool orig); + //virtual void EndpointEOF(bool is_orig); + + static Analyzer* Instantiate(Connection* conn) + { return new DNP3_UDP_Analyzer(conn); } + +private: + static const int MAX_BUFFER_SIZE = 300; + + struct Endpoint { + u_char buffer[MAX_BUFFER_SIZE]; + int buffer_len; + bool in_hdr; + int tpflags; + int pkt_length; + int pkt_cnt; + bool encountered_first_chunk; + }; + + bool ProcessData(int len, const u_char* data, bool orig); + void ClearEndpointState(bool orig); + bool AddToBuffer(Endpoint* endp, int target_len, const u_char** data, int* len); + bool ParseAppLayer(Endpoint* endp); + bool CheckCRC(int len, const u_char* data, const u_char* crc16, const char* where); + unsigned int CalcCRC(int len, const u_char* data); + + binpac::DNP3::DNP3_Conn* interp; + + Endpoint orig_state; + Endpoint resp_state; + + static void PrecomputeCRCTable(); + + static bool crc_table_initialized; + static unsigned int crc_table[256]; +}; + + } } // namespace analyzer::* #endif From 51e936ec59d37b3d906f876730a950420a346abb Mon Sep 17 00:00:00 2001 From: Hui Lin Date: Wed, 6 Aug 2014 15:07:11 -0500 Subject: [PATCH 006/299] changed a bug, but still not working --- src/analyzer/protocol/dnp3/DNP3.cc | 25 +++++++++++++++---------- src/analyzer/protocol/dnp3/DNP3.h | 10 ++++++---- src/analyzer/protocol/dnp3/Plugin.cc | 6 ++++-- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/analyzer/protocol/dnp3/DNP3.cc b/src/analyzer/protocol/dnp3/DNP3.cc index 7931b31462..f07e999ad0 100644 --- a/src/analyzer/protocol/dnp3/DNP3.cc +++ b/src/analyzer/protocol/dnp3/DNP3.cc @@ -97,7 +97,7 @@ // Binpac DNP3 Analyzer #include "DNP3.h" -#include "analyzer/protocol/tcp/TCP_Reassembler.h" +//#include "analyzer/protocol/tcp/TCP_Reassembler.h" #include "events.bif.h" using namespace analyzer::dnp3; @@ -109,13 +109,13 @@ const unsigned int PSEUDO_APP_LAYER_INDEX = 11; // index of first DNP3 app-laye const unsigned int PSEUDO_TRANSPORT_LEN = 1; // length of DNP3 Transport Layer const unsigned int PSEUDO_LINK_LAYER_LEN = 8; // length of DNP3 Pseudo Link Layer -bool DNP3_Analyzer::crc_table_initialized = false; -unsigned int DNP3_Analyzer::crc_table[256]; +//bool DNP3_Analyzer::crc_table_initialized = false; +//unsigned int DNP3_Analyzer::crc_table[256]; bool DNP3_UDP_Analyzer::crc_table_initialized = false; unsigned int DNP3_UDP_Analyzer::crc_table[256]; - +/* DNP3_Analyzer::DNP3_Analyzer(Connection* c) : TCP_ApplicationAnalyzer("DNP3", c) { interp = new binpac::DNP3::DNP3_Conn(this); @@ -378,11 +378,14 @@ unsigned int DNP3_Analyzer::CalcCRC(int len, const u_char* data) return ~crc & 0xFFFF; } +*/ // ?? For DNP3 over UDP analyzer. most of the codes are copied and pasted. Better way to reuse the code? -DNP3_UDP_Analyzer::DNP3_UDP_Analyzer(Connection* c) : Analyzer("DHCP", c) +DNP3_UDP_Analyzer::DNP3_UDP_Analyzer(Connection* c) : Analyzer("DNP3", c) { + + printf("enter DNP3_UDP_Analyzer\n"); interp = new binpac::DNP3::DNP3_Conn(this); ClearEndpointState(true); @@ -400,14 +403,13 @@ DNP3_UDP_Analyzer::~DNP3_UDP_Analyzer() void DNP3_UDP_Analyzer::Done() { Analyzer::Done(); - - interp->FlowEOF(true); - interp->FlowEOF(false); } -void DNP3_UDP_Analyzer::DeliverStream(int len, const u_char* data, bool orig) +void DNP3_UDP_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, int seq, const IP_Hdr* ip, int caplen) { - Analyzer::DeliverStream(len, data, orig); + printf("enter DNP3_UDP_Analyzer DeliverPacket\n"); + Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen); + try { @@ -437,8 +439,11 @@ void DNP3_UDP_Analyzer::EndpointEOF(bool is_orig) bool DNP3_UDP_Analyzer::ProcessData(int len, const u_char* data, bool orig) { + printf("enter DNP3_UDP_Analyzer process Data\n"); + Endpoint* endp = orig ? &orig_state : &resp_state; + while ( len ) { if ( endp->in_hdr ) diff --git a/src/analyzer/protocol/dnp3/DNP3.h b/src/analyzer/protocol/dnp3/DNP3.h index ff3aff3594..fcb1758ddb 100644 --- a/src/analyzer/protocol/dnp3/DNP3.h +++ b/src/analyzer/protocol/dnp3/DNP3.h @@ -2,13 +2,13 @@ #ifndef ANALYZER_PROTOCOL_DNP3_DNP3_H #define ANALYZER_PROTOCOL_DNP3_DNP3_H -#include "analyzer/protocol/tcp/TCP.h" +//#include "analyzer/protocol/tcp/TCP.h" #include "analyzer/protocol/udp/UDP.h" #include "dnp3_pac.h" namespace analyzer { namespace dnp3 { - +/* class DNP3_Analyzer : public tcp::TCP_ApplicationAnalyzer { public: DNP3_Analyzer(Connection* conn); @@ -52,6 +52,7 @@ private: static bool crc_table_initialized; static unsigned int crc_table[256]; }; +*/ class DNP3_UDP_Analyzer : public analyzer::Analyzer { public: @@ -59,11 +60,12 @@ public: virtual ~DNP3_UDP_Analyzer(); virtual void Done(); - virtual void DeliverStream(int len, const u_char* data, bool orig); + virtual void DeliverPacket(int len, const u_char* data, bool orig, + int seq, const IP_Hdr* ip, int caplen); //virtual void Undelivered(uint64 seq, int len, bool orig); //virtual void EndpointEOF(bool is_orig); - static Analyzer* Instantiate(Connection* conn) + static analyzer::Analyzer* Instantiate(Connection* conn) { return new DNP3_UDP_Analyzer(conn); } private: diff --git a/src/analyzer/protocol/dnp3/Plugin.cc b/src/analyzer/protocol/dnp3/Plugin.cc index 614ff38773..0ebc49355a 100644 --- a/src/analyzer/protocol/dnp3/Plugin.cc +++ b/src/analyzer/protocol/dnp3/Plugin.cc @@ -12,11 +12,13 @@ class Plugin : public plugin::Plugin { public: plugin::Configuration Configure() { - AddComponent(new ::analyzer::Component("DNP3", ::analyzer::dnp3::DNP3_Analyzer::Instantiate)); + //AddComponent(new ::analyzer::Component("DNP3", ::analyzer::dnp3::DNP3_Analyzer::Instantiate)); + AddComponent(new ::analyzer::Component("DNP3", ::analyzer::dnp3::DNP3_UDP_Analyzer::Instantiate)); plugin::Configuration config; config.name = "Bro::DNP3"; - config.description = "DNP3 analyzer"; + //config.description = "DNP3 analyzer"; + config.description = "DNP3 UDP analyzer"; return config; } } plugin; From b83d4a9c849378c30a16f8b775d961616ad8dff2 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 6 Aug 2014 15:41:53 -0500 Subject: [PATCH 007/299] Fix some things in DNP3 UDP analyzer. - DeliverPacket override had a wrong parameter. - Change the DNP3 plugin to provide both UDP and TCP analyzer versions. - Add a DPD signature. --- scripts/base/protocols/dnp3/dpd.sig | 6 ++++++ src/analyzer/protocol/dnp3/DNP3.cc | 10 ++++------ src/analyzer/protocol/dnp3/DNP3.h | 8 ++++---- src/analyzer/protocol/dnp3/Plugin.cc | 7 +++---- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/scripts/base/protocols/dnp3/dpd.sig b/scripts/base/protocols/dnp3/dpd.sig index c482661a43..24fa740626 100644 --- a/scripts/base/protocols/dnp3/dpd.sig +++ b/scripts/base/protocols/dnp3/dpd.sig @@ -7,3 +7,9 @@ signature dpd_dnp3_server { tcp-state responder enable "dnp3" } + +signature dpd_dnp3_server_udp { + ip-proto == udp + payload /\x05\x64/ + enable "dnp3_udp" +} diff --git a/src/analyzer/protocol/dnp3/DNP3.cc b/src/analyzer/protocol/dnp3/DNP3.cc index f07e999ad0..b6b4a5ea3d 100644 --- a/src/analyzer/protocol/dnp3/DNP3.cc +++ b/src/analyzer/protocol/dnp3/DNP3.cc @@ -109,13 +109,12 @@ const unsigned int PSEUDO_APP_LAYER_INDEX = 11; // index of first DNP3 app-laye const unsigned int PSEUDO_TRANSPORT_LEN = 1; // length of DNP3 Transport Layer const unsigned int PSEUDO_LINK_LAYER_LEN = 8; // length of DNP3 Pseudo Link Layer -//bool DNP3_Analyzer::crc_table_initialized = false; -//unsigned int DNP3_Analyzer::crc_table[256]; +bool DNP3_Analyzer::crc_table_initialized = false; +unsigned int DNP3_Analyzer::crc_table[256]; bool DNP3_UDP_Analyzer::crc_table_initialized = false; unsigned int DNP3_UDP_Analyzer::crc_table[256]; -/* DNP3_Analyzer::DNP3_Analyzer(Connection* c) : TCP_ApplicationAnalyzer("DNP3", c) { interp = new binpac::DNP3::DNP3_Conn(this); @@ -378,11 +377,10 @@ unsigned int DNP3_Analyzer::CalcCRC(int len, const u_char* data) return ~crc & 0xFFFF; } -*/ // ?? For DNP3 over UDP analyzer. most of the codes are copied and pasted. Better way to reuse the code? -DNP3_UDP_Analyzer::DNP3_UDP_Analyzer(Connection* c) : Analyzer("DNP3", c) +DNP3_UDP_Analyzer::DNP3_UDP_Analyzer(Connection* c) : Analyzer("DNP3_UDP", c) { printf("enter DNP3_UDP_Analyzer\n"); @@ -405,7 +403,7 @@ void DNP3_UDP_Analyzer::Done() Analyzer::Done(); } -void DNP3_UDP_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, int seq, const IP_Hdr* ip, int caplen) +void DNP3_UDP_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, uint64 seq, const IP_Hdr* ip, int caplen) { printf("enter DNP3_UDP_Analyzer DeliverPacket\n"); Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen); diff --git a/src/analyzer/protocol/dnp3/DNP3.h b/src/analyzer/protocol/dnp3/DNP3.h index fcb1758ddb..0cc7fc9c85 100644 --- a/src/analyzer/protocol/dnp3/DNP3.h +++ b/src/analyzer/protocol/dnp3/DNP3.h @@ -2,13 +2,13 @@ #ifndef ANALYZER_PROTOCOL_DNP3_DNP3_H #define ANALYZER_PROTOCOL_DNP3_DNP3_H -//#include "analyzer/protocol/tcp/TCP.h" +#include "analyzer/protocol/tcp/TCP.h" #include "analyzer/protocol/udp/UDP.h" #include "dnp3_pac.h" namespace analyzer { namespace dnp3 { -/* + class DNP3_Analyzer : public tcp::TCP_ApplicationAnalyzer { public: DNP3_Analyzer(Connection* conn); @@ -52,7 +52,7 @@ private: static bool crc_table_initialized; static unsigned int crc_table[256]; }; -*/ + class DNP3_UDP_Analyzer : public analyzer::Analyzer { public: @@ -61,7 +61,7 @@ public: virtual void Done(); virtual void DeliverPacket(int len, const u_char* data, bool orig, - int seq, const IP_Hdr* ip, int caplen); + uint64 seq, const IP_Hdr* ip, int caplen); //virtual void Undelivered(uint64 seq, int len, bool orig); //virtual void EndpointEOF(bool is_orig); diff --git a/src/analyzer/protocol/dnp3/Plugin.cc b/src/analyzer/protocol/dnp3/Plugin.cc index 0ebc49355a..c2462b7af7 100644 --- a/src/analyzer/protocol/dnp3/Plugin.cc +++ b/src/analyzer/protocol/dnp3/Plugin.cc @@ -12,13 +12,12 @@ class Plugin : public plugin::Plugin { public: plugin::Configuration Configure() { - //AddComponent(new ::analyzer::Component("DNP3", ::analyzer::dnp3::DNP3_Analyzer::Instantiate)); - AddComponent(new ::analyzer::Component("DNP3", ::analyzer::dnp3::DNP3_UDP_Analyzer::Instantiate)); + AddComponent(new ::analyzer::Component("DNP3", ::analyzer::dnp3::DNP3_Analyzer::Instantiate)); + AddComponent(new ::analyzer::Component("DNP3_UDP", ::analyzer::dnp3::DNP3_UDP_Analyzer::Instantiate)); plugin::Configuration config; config.name = "Bro::DNP3"; - //config.description = "DNP3 analyzer"; - config.description = "DNP3 UDP analyzer"; + config.description = "DNP3 UDP/TCP analyzers"; return config; } } plugin; From 11f7e2d74bcbde3ced7bcbce90b3aef7b1dab5e6 Mon Sep 17 00:00:00 2001 From: Hui Lin Date: Mon, 11 Aug 2014 15:41:25 -0500 Subject: [PATCH 008/299] fixed the bug of deciding the size of object 1 varition 1 in DNP3 --- src/analyzer/protocol/dnp3/dnp3-protocol.pac | 10 +- .../output | 2342 +++++++++++++---- 2 files changed, 1795 insertions(+), 557 deletions(-) diff --git a/src/analyzer/protocol/dnp3/dnp3-protocol.pac b/src/analyzer/protocol/dnp3/dnp3-protocol.pac index 9407b000eb..7fa14320dc 100644 --- a/src/analyzer/protocol/dnp3/dnp3-protocol.pac +++ b/src/analyzer/protocol/dnp3/dnp3-protocol.pac @@ -90,7 +90,7 @@ type DNP3_Application_Response_Header = record { type Request_Objects(function_code: uint8) = record { object_header: Object_Header(function_code); data: case (object_header.object_type_field) of { - 0x0c03 -> bocmd_PM: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ ( object_header.number_of_item / 8 ) + 1 ]; + 0x0c03 -> bocmd_PM: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ ( object_header.number_of_item / 8 ) + 1*( object_header.number_of_item > ( (object_header.number_of_item / 8)*8 ) ) ]; 0x3202 -> time_interval_ojbects: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item] &check( object_header.qualifer_field == 0x0f && object_header.number_of_item == 0x01); default -> ojbects: Request_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; @@ -112,10 +112,10 @@ type Request_Objects(function_code: uint8) = record { type Response_Objects(function_code: uint8) = record { object_header: Object_Header(function_code); data: case (object_header.object_type_field) of { - 0x0101 -> biwoflag: Response_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ ( object_header.number_of_item / 8 ) + 1 ]; - 0x0301 -> diwoflag: Response_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ ( object_header.number_of_item / 8 ) + 1 ]; - 0x0a01 -> bowoflag: Response_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ ( object_header.number_of_item / 8 ) + 1 ]; - 0x0c03 -> bocmd_PM: Response_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ ( object_header.number_of_item / 8 ) + 1 ]; + 0x0101 -> biwoflag: Response_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ ( object_header.number_of_item / 8 ) + 1*( object_header.number_of_item > ( (object_header.number_of_item / 8)*8 ) ) ]; + 0x0301 -> diwoflag: Response_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ ( object_header.number_of_item / 8 ) + 1*( object_header.number_of_item > ( (object_header.number_of_item / 8)*8 ) ) ]; + 0x0a01 -> bowoflag: Response_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ ( object_header.number_of_item / 8 ) + 1*( object_header.number_of_item > ( (object_header.number_of_item / 8)*8 ) )]; + 0x0c03 -> bocmd_PM: Response_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ ( object_header.number_of_item / 8 ) + 1*( object_header.number_of_item > ( (object_header.number_of_item / 8)*8 ) )]; default -> ojbects: Response_Data_Object(function_code, object_header.qualifier_field, object_header.object_type_field )[ object_header.number_of_item]; }; }; diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_link_only/output b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_link_only/output index 0ddd6632ba..01c66d72f4 100644 --- a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_link_only/output +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_link_only/output @@ -260,622 +260,1860 @@ dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 0 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 0 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 10 -dnp3_object_header, F, 513, 0, 256, 0, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_prefix, F, 0 -dnp3_response_data_object, F, 255 -dnp3_object_header, F, 257, 1, 1, 257, 257 +dnp3_object_header, F, 2562, 1, 512, 0, 511 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 257, 1, 1, 257, 257 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 1 -dnp3_object_header, F, 286, 5, 0, 16777235, 16861313 -dnp3_object_header, F, 26940, 1, 4294964795, 49930, 47428 -dnp3_object_header, F, 457, 102, 0, 65535, 65535 -dnp3_object_header, F, 55993, 1, 4294962261, 19986, 14950 -dnp3_object_header, F, 274, 174, 0, 0, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_header, F, 7685, 1, 276, 0, 275 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 1013547336 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 3108291338 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 3118098121 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 979783186 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 1013100050 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 976559429 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 1069427906 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 1114636174 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 982332387 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 987182644 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 3121874082 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 dnp3_header_block, F, 25605, 255, 68, 100, 1 dnp3_application_response_header, F, 129, 5120 dnp3_object_header, F, 7685, 1, 224, 276, 499 From ff60706742926f6b98b21141062f8924a9720409 Mon Sep 17 00:00:00 2001 From: Hui Lin Date: Fri, 15 Aug 2014 14:26:47 -0500 Subject: [PATCH 009/299] Removing the debug printf in DNP3.cc --- src/analyzer/protocol/dnp3/DNP3.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/analyzer/protocol/dnp3/DNP3.cc b/src/analyzer/protocol/dnp3/DNP3.cc index b6b4a5ea3d..bd5d7ebc94 100644 --- a/src/analyzer/protocol/dnp3/DNP3.cc +++ b/src/analyzer/protocol/dnp3/DNP3.cc @@ -383,7 +383,6 @@ unsigned int DNP3_Analyzer::CalcCRC(int len, const u_char* data) DNP3_UDP_Analyzer::DNP3_UDP_Analyzer(Connection* c) : Analyzer("DNP3_UDP", c) { - printf("enter DNP3_UDP_Analyzer\n"); interp = new binpac::DNP3::DNP3_Conn(this); ClearEndpointState(true); @@ -405,7 +404,7 @@ void DNP3_UDP_Analyzer::Done() void DNP3_UDP_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, uint64 seq, const IP_Hdr* ip, int caplen) { - printf("enter DNP3_UDP_Analyzer DeliverPacket\n"); + Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen); @@ -437,8 +436,7 @@ void DNP3_UDP_Analyzer::EndpointEOF(bool is_orig) bool DNP3_UDP_Analyzer::ProcessData(int len, const u_char* data, bool orig) { - printf("enter DNP3_UDP_Analyzer process Data\n"); - + Endpoint* endp = orig ? &orig_state : &resp_state; From fb21236661414d205b5a909b93510765f5e39d03 Mon Sep 17 00:00:00 2001 From: Hui Lin Date: Sat, 16 Aug 2014 11:01:30 -0500 Subject: [PATCH 010/299] quickly fix another bug; adding missing field of the declaration of dnp3_request_application_header and dnp3_response_application_header --- scripts/base/protocols/dnp3/main.bro | 4 +- src/analyzer/protocol/dnp3/dnp3-analyzer.pac | 10 +- src/analyzer/protocol/dnp3/events.bif | 4 +- .../dnp3.log | 4 +- .../output | 4 +- .../dnp3.log | 4 +- .../output | 4 +- .../dnp3.log | 4 +- .../output | 4 +- .../dnp3.log | 4 +- .../output | 22 +- .../dnp3.log | 4 +- .../output | 12 +- .../output | 6 +- .../dnp3.log | 4 +- .../output | 4 +- .../dnp3.log | 4 +- .../output | 4 +- .../dnp3.log | 4 +- .../output | 8 +- .../dnp3.log | 4 +- .../output | 4 +- .../dnp3.log | 4 +- .../scripts.base.protocols.dnp3.events/output | 230 +++++++++--------- .../scripts/base/protocols/dnp3/events.bro | 8 +- 25 files changed, 185 insertions(+), 183 deletions(-) diff --git a/scripts/base/protocols/dnp3/main.bro b/scripts/base/protocols/dnp3/main.bro index 3e5eede462..38b767c87d 100644 --- a/scripts/base/protocols/dnp3/main.bro +++ b/scripts/base/protocols/dnp3/main.bro @@ -40,7 +40,7 @@ event bro_init() &priority=5 Analyzer::register_for_ports(Analyzer::ANALYZER_DNP3, ports); } -event dnp3_application_request_header(c: connection, is_orig: bool, fc: count) +event dnp3_application_request_header(c: connection, is_orig: bool, application_control: count, fc: count) { if ( ! c?$dnp3 ) c$dnp3 = [$ts=network_time(), $uid=c$uid, $id=c$id]; @@ -49,7 +49,7 @@ event dnp3_application_request_header(c: connection, is_orig: bool, fc: count) c$dnp3$fc_request = function_codes[fc]; } -event dnp3_application_response_header(c: connection, is_orig: bool, fc: count, iin: count) +event dnp3_application_response_header(c: connection, is_orig: bool, application_control: count, fc: count, iin: count) { if ( ! c?$dnp3 ) c$dnp3 = [$ts=network_time(), $uid=c$uid, $id=c$id]; diff --git a/src/analyzer/protocol/dnp3/dnp3-analyzer.pac b/src/analyzer/protocol/dnp3/dnp3-analyzer.pac index 2ae783c82e..96b7c09bce 100644 --- a/src/analyzer/protocol/dnp3/dnp3-analyzer.pac +++ b/src/analyzer/protocol/dnp3/dnp3-analyzer.pac @@ -20,7 +20,7 @@ flow DNP3_Flow(is_orig: bool) { return true; %} - function get_dnp3_application_request_header(fc: uint8): bool + function get_dnp3_application_request_header(application_control: uint8, fc: uint8): bool %{ if ( ::dnp3_application_request_header ) { @@ -28,13 +28,14 @@ flow DNP3_Flow(is_orig: bool) { connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), is_orig(), + application_control, fc ); } return true; %} - function get_dnp3_application_response_header(fc: uint8, iin: uint16): bool + function get_dnp3_application_response_header(application_control: uint8, fc: uint8, iin: uint16): bool %{ if ( ::dnp3_application_response_header ) { @@ -42,6 +43,7 @@ flow DNP3_Flow(is_orig: bool) { connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), is_orig(), + application_control, fc, iin ); @@ -725,11 +727,11 @@ refine typeattr Header_Block += &let { }; refine typeattr DNP3_Application_Request_Header += &let { - process_request: bool = $context.flow.get_dnp3_application_request_header(function_code); + process_request: bool = $context.flow.get_dnp3_application_request_header(application_control, function_code); }; refine typeattr DNP3_Application_Response_Header += &let { - process_request: bool = $context.flow.get_dnp3_application_response_header(function_code, internal_indications); + process_request: bool = $context.flow.get_dnp3_application_response_header(application_control, function_code, internal_indications); }; refine typeattr Object_Header += &let { diff --git a/src/analyzer/protocol/dnp3/events.bif b/src/analyzer/protocol/dnp3/events.bif index 80f9504a9e..bd3aa5c647 100644 --- a/src/analyzer/protocol/dnp3/events.bif +++ b/src/analyzer/protocol/dnp3/events.bif @@ -7,7 +7,7 @@ ## ## fc: function code. ## -event dnp3_application_request_header%(c: connection, is_orig: bool, fc: count%); +event dnp3_application_request_header%(c: connection, is_orig: bool, application: count, fc: count%); ## Generated for a DNP3 response header. ## @@ -19,7 +19,7 @@ event dnp3_application_request_header%(c: connection, is_orig: bool, fc: count%) ## ## iin: internal indication number. ## -event dnp3_application_response_header%(c: connection, is_orig: bool, fc: count, iin: count%); +event dnp3_application_response_header%(c: connection, is_orig: bool, application: count, fc: count, iin: count%); ## Generated for the object header found in both DNP3 requests and responses. ## diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_del_measure/dnp3.log b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_del_measure/dnp3.log index 68931eb81e..c18fa59ef0 100644 --- a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_del_measure/dnp3.log +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_del_measure/dnp3.log @@ -3,8 +3,8 @@ #empty_field (empty) #unset_field - #path dnp3 -#open 2013-08-26-19-04-04 +#open 2014-08-16-15-58-44 #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p fc_request fc_reply iin #types time string addr port addr port string string count 1324503054.884183 CXWv6p3arKYeMETxOg 130.126.142.250 49413 130.126.140.229 20000 DELAY_MEASURE RESPONSE 0 -#close 2013-08-26-19-04-04 +#close 2014-08-16-15-58-44 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_del_measure/output b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_del_measure/output index 5bd7d932bc..85c7c845f0 100644 --- a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_del_measure/output +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_del_measure/output @@ -1,7 +1,7 @@ dnp3_header_block, T, 25605, 8, 196, 2, 3 -dnp3_application_request_header, T, 23 +dnp3_application_request_header, T, 196, 23 dnp3_header_block, F, 25605, 16, 68, 3, 2 -dnp3_application_response_header, F, 129, 0 +dnp3_application_response_header, F, 196, 129, 0 dnp3_object_header, F, 13314, 7, 1, 1, 0 dnp3_object_prefix, F, 0 dnp3_response_data_object, F, 255 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_en_spon/dnp3.log b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_en_spon/dnp3.log index 90c7e9dfd3..ffca7690c4 100644 --- a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_en_spon/dnp3.log +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_en_spon/dnp3.log @@ -3,8 +3,8 @@ #empty_field (empty) #unset_field - #path dnp3 -#open 2013-08-26-19-04-04 +#open 2014-08-16-15-58-46 #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p fc_request fc_reply iin #types time string addr port addr port string string count 1324916729.150101 CXWv6p3arKYeMETxOg 130.126.142.250 50059 130.126.140.229 20000 ENABLE_UNSOLICITED RESPONSE 0 -#close 2013-08-26-19-04-04 +#close 2014-08-16-15-58-46 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_en_spon/output b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_en_spon/output index 16491bb3a5..53c6dc8700 100644 --- a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_en_spon/output +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_en_spon/output @@ -1,7 +1,7 @@ dnp3_header_block, T, 25605, 17, 196, 2, 3 -dnp3_application_request_header, T, 20 +dnp3_application_request_header, T, 203, 20 dnp3_object_header, T, 15362, 6, 0, 65535, 65535 dnp3_object_header, T, 15363, 6, 0, 65535, 65535 dnp3_object_header, T, 15364, 6, 0, 65535, 65535 dnp3_header_block, F, 25605, 10, 68, 3, 2 -dnp3_application_response_header, F, 129, 0 +dnp3_application_response_header, F, 203, 129, 0 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_del/dnp3.log b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_del/dnp3.log index 4a1fb6329a..3d0033bd1a 100644 --- a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_del/dnp3.log +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_del/dnp3.log @@ -3,8 +3,8 @@ #empty_field (empty) #unset_field - #path dnp3 -#open 2013-08-26-19-04-05 +#open 2014-08-16-15-58-47 #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p fc_request fc_reply iin #types time string addr port addr port string string count 1325044377.992570 CXWv6p3arKYeMETxOg 130.126.142.250 50301 130.126.140.229 20000 DELETE_FILE RESPONSE 0 -#close 2013-08-26-19-04-05 +#close 2014-08-16-15-58-47 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_del/output b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_del/output index 37ccbc5bc9..9c63a41ae4 100644 --- a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_del/output +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_del/output @@ -1,9 +1,9 @@ dnp3_header_block, T, 25605, 99, 196, 4, 3 -dnp3_application_request_header, T, 27 +dnp3_application_request_header, T, 201, 27 dnp3_object_header, T, 17923, 91, 1, 1, 0 dnp3_object_prefix, T, 85 dnp3_header_block, F, 25605, 29, 68, 3, 4 -dnp3_application_response_header, F, 129, 0 +dnp3_application_response_header, F, 201, 129, 0 dnp3_object_header, F, 17924, 91, 1, 1, 0 dnp3_object_prefix, F, 13 dnp3_response_data_object, F, 255 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_read/dnp3.log b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_read/dnp3.log index 9db6d6468d..7acf3a1608 100644 --- a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_read/dnp3.log +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_read/dnp3.log @@ -3,7 +3,7 @@ #empty_field (empty) #unset_field - #path dnp3 -#open 2013-08-26-19-04-05 +#open 2014-08-16-15-58-48 #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p fc_request fc_reply iin #types time string addr port addr port string string count 1325036012.621691 CXWv6p3arKYeMETxOg 130.126.142.250 50276 130.126.140.229 20000 OPEN_FILE RESPONSE 4096 @@ -11,4 +11,4 @@ 1325036019.765502 CXWv6p3arKYeMETxOg 130.126.142.250 50276 130.126.140.229 20000 WRITE RESPONSE 0 1325036022.292689 CXWv6p3arKYeMETxOg 130.126.142.250 50276 130.126.140.229 20000 WRITE RESPONSE 0 1325036024.820857 CXWv6p3arKYeMETxOg 130.126.142.250 50276 130.126.140.229 20000 CLOSE_FILE RESPONSE 0 -#close 2013-08-26-19-04-05 +#close 2014-08-16-15-58-48 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_read/output b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_read/output index 1a4971a9e3..feb59be3f3 100644 --- a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_read/output +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_file_read/output @@ -1,45 +1,45 @@ dnp3_header_block, T, 25605, 50, 196, 4, 3 -dnp3_application_request_header, T, 25 +dnp3_application_request_header, T, 206, 25 dnp3_object_header, T, 17923, 91, 1, 1, 0 dnp3_object_prefix, T, 36 dnp3_header_block, F, 25605, 29, 68, 3, 4 -dnp3_application_response_header, F, 129, 4096 +dnp3_application_response_header, F, 206, 129, 4096 dnp3_object_header, F, 17924, 91, 1, 1, 0 dnp3_object_prefix, F, 13 dnp3_response_data_object, F, 255 dnp3_header_block, T, 25605, 22, 196, 4, 3 -dnp3_application_request_header, T, 1 +dnp3_application_request_header, T, 207, 1 dnp3_object_header, T, 17925, 91, 1, 1, 0 dnp3_object_prefix, T, 8 dnp3_file_transport, T, 305419896, 0 ^J dnp3_header_block, F, 25605, 255, 68, 3, 4 -dnp3_application_response_header, F, 129, 4096 +dnp3_application_response_header, F, 239, 129, 4096 dnp3_object_header, F, 17925, 91, 1, 1, 0 dnp3_object_prefix, F, 838 dnp3_file_transport, F, 305419896, 2147483648 0000 ef bb bf 3c 3f 78 6d 6c 20 76 65 72 73 69 6f 6e .......^J0150 0d 0a 20 20 3c 21 2d 2d 44 6f 63 75 6d 65 6e 74 .. ^M^J^M^J^M^J^M^J^M^J^M^J^M^J
^M^J^M^J

Hello

^M^J^M^J

 

^M^J^M^J

I send u smtp pcap file

^M^J^M^J

Find the attachment

^M^J^M^J

 

^M^J^M^J

GPS

^M^J^M^J
^M^J^M^J^M^J^M^J^M^J^M^J, mime_type=text/html, mime_types=[[strength=45, mime=text/html], [strength=41, mime=text/html], [strength=-20, mime=text/plain]], info=, u2_events=] + +1254722770.692804 file_over_new_connection + [0] f: fa_file = [id=Ft4M3f2yMvLlmwtbq9, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=, fuids=[Fel9gs4OtNEV6gUJZ5]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=4], socks=, ssh=, syslog=]^J}, last_active=1254722770.692804, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=3000, bof_buffer=^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J
^M^J^M^J

Hello

^M^J^M^J

 

^M^J^M^J

I send u smtp pcap file

^M^J^M^J

Find the attachment

^M^J^M^J

 

^M^J^M^J

GPS

^M^J^M^J
^M^J^M^J^M^J^M^J^M^J^M^J, mime_type=text/html, mime_types=[[strength=45, mime=text/html], [strength=41, mime=text/html], [strength=-20, mime=text/plain]], info=[ts=1254722770.692804, fuid=Ft4M3f2yMvLlmwtbq9, tx_hosts={^J^J}, rx_hosts={^J^J}, conn_uids={^J^J}, source=SMTP, depth=0, analyzers={^J^J}, mime_type=text/html, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=] + [1] c: connection = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^ISMTP^J}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^J}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^J}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=, fuids=[Fel9gs4OtNEV6gUJZ5]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=4], socks=, ssh=, syslog=] + [2] is_orig: bool = F + 1254722770.692804 file_state_remove - [0] f: fa_file = [id=Ft4M3f2yMvLlmwtbq9, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=, fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=4], socks=, ssh=, syslog=]^J}, last_active=1254722770.692804, seen_bytes=1868, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=1024, bof_buffer=^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J
^M^J^M^J

Hello

^M^J^M^J

 

^M^J^M^J

I send u smtp pcap file

^M^J^M^J

Find the attachment

^M^J^M^J

 

^M^J^M^J

GPS

^M^J^M^J
^M^J^M^J^M^J^M^J^M^J^M^J, mime_type=text/html, mime_types=[[strength=45, mime=text/html], [strength=41, mime=text/html], [strength=-20, mime=text/plain]], info=[ts=1254722770.692804, fuid=Ft4M3f2yMvLlmwtbq9, tx_hosts={^J^I74.53.140.153^J}, rx_hosts={^J^I10.10.1.4^J}, conn_uids={^J^ICjhGID4nQcgTWjvg4c^J}, source=SMTP, depth=4, analyzers={^J^J}, mime_type=text/html, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=] 1254722770.692804 get_file_handle [0] tag: enum = Analyzer::ANALYZER_SMTP @@ -393,17 +393,17 @@ [1] c: connection = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^ISMTP^J}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^J}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^J}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=NEWS.txt], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=] [2] is_orig: bool = F -1254722770.692823 file_new - [0] f: fa_file = [id=FL9Y0d45OI4LpS6fmh, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=NEWS.txt], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=]^J}, last_active=1254722770.692823, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=1024, bof_buffer=Version 4.9.9.1^M^J* Many bug fixes^M^J* Improved editor^M^J^M^JVersion 4.9.9.0^M^J* Support for latest Mingw compiler system builds^M^J* Bug fixes^M^J^M^JVersion 4.9.8.9^M^J* New code tooltip display^M^J* Improved Indent/Unindent and Remove Comment^M^J* Improved automatic indent^M^J* Added support for the "interface" keyword^M^J* WebUpdate should now report installation problems from PackMan^M^J* New splash screen and association icons^M^J* Improved installer^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.7^M^J* Added support for GCC > 3.2^M^J* Debug variables are now resent during next debug session^M^J* Watched Variables not in correct context are now kept and updated when it is needed^M^J* Added new compiler/linker options: ^M^J - Strip executable^M^J - Generate instructions for a specific machine (i386, i486, i586, i686, pentium, pentium-mmx, pentiumpro, pentium2, pentium3, pentium4, ^M^J k6, k6-2, k6-3, athlon, athlon-tbird, athlon-4, athlon-xp, athlon-mp, winchip-c6, winchip2, k8, c3 and c3-2)^M^J - Enable use of processor specific built-in functions (mmmx, sse, , mime_type=text/plain, mime_types=[[strength=-20, mime=text/plain]], info=, u2_events=] - -1254722770.692823 file_over_new_connection - [0] f: fa_file = [id=FL9Y0d45OI4LpS6fmh, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=NEWS.txt], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=]^J}, last_active=1254722770.692823, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=1024, bof_buffer=Version 4.9.9.1^M^J* Many bug fixes^M^J* Improved editor^M^J^M^JVersion 4.9.9.0^M^J* Support for latest Mingw compiler system builds^M^J* Bug fixes^M^J^M^JVersion 4.9.8.9^M^J* New code tooltip display^M^J* Improved Indent/Unindent and Remove Comment^M^J* Improved automatic indent^M^J* Added support for the "interface" keyword^M^J* WebUpdate should now report installation problems from PackMan^M^J* New splash screen and association icons^M^J* Improved installer^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.7^M^J* Added support for GCC > 3.2^M^J* Debug variables are now resent during next debug session^M^J* Watched Variables not in correct context are now kept and updated when it is needed^M^J* Added new compiler/linker options: ^M^J - Strip executable^M^J - Generate instructions for a specific machine (i386, i486, i586, i686, pentium, pentium-mmx, pentiumpro, pentium2, pentium3, pentium4, ^M^J k6, k6-2, k6-3, athlon, athlon-tbird, athlon-4, athlon-xp, athlon-mp, winchip-c6, winchip2, k8, c3 and c3-2)^M^J - Enable use of processor specific built-in functions (mmmx, sse, , mime_type=text/plain, mime_types=[[strength=-20, mime=text/plain]], info=[ts=1254722770.692823, fuid=FL9Y0d45OI4LpS6fmh, tx_hosts={^J^J}, rx_hosts={^J^J}, conn_uids={^J^J}, source=SMTP, depth=0, analyzers={^J^J}, mime_type=text/plain, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=] - [1] c: connection = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^ISMTP^J}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^J}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^J}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=NEWS.txt], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=] - [2] is_orig: bool = F - 1254722770.695115 new_connection [0] c: connection = [id=[orig_h=192.168.1.1, orig_p=3/icmp, resp_h=10.10.1.4, resp_p=4/icmp], orig=[size=0, state=0, num_pkts=0, num_bytes_ip=0, flow_label=0], resp=[size=0, state=0, num_pkts=0, num_bytes_ip=0, flow_label=0], start_time=1254722770.695115, duration=0.0, service={^J^J}, addl=, hot=0, history=, uid=CCvvfg3TEfuqmmG4bh, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=] +1254722771.469814 file_new + [0] f: fa_file = [id=FL9Y0d45OI4LpS6fmh, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=NEWS.txt], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=]^J}, last_active=1254722771.469814, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=3000, bof_buffer=Version 4.9.9.1^M^J* Many bug fixes^M^J* Improved editor^M^J^M^JVersion 4.9.9.0^M^J* Support for latest Mingw compiler system builds^M^J* Bug fixes^M^J^M^JVersion 4.9.8.9^M^J* New code tooltip display^M^J* Improved Indent/Unindent and Remove Comment^M^J* Improved automatic indent^M^J* Added support for the "interface" keyword^M^J* WebUpdate should now report installation problems from PackMan^M^J* New splash screen and association icons^M^J* Improved installer^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.7^M^J* Added support for GCC > 3.2^M^J* Debug variables are now resent during next debug session^M^J* Watched Variables not in correct context are now kept and updated when it is needed^M^J* Added new compiler/linker options: ^M^J - Strip executable^M^J - Generate instructions for a specific machine (i386, i486, i586, i686, pentium, pentium-mmx, pentiumpro, pentium2, pentium3, pentium4, ^M^J k6, k6-2, k6-3, athlon, athlon-tbird, athlon-4, athlon-xp, athlon-mp, winchip-c6, winchip2, k8, c3 and c3-2)^M^J - Enable use of processor specific built-in functions (mmmx, sse, sse2, pni, 3dnow)^M^J* "Default" button in Compiler Options is back^M^J* Error messages parsing improved^M^J* Bug fixes^M^J^M^JVersion 4.9.8.5^M^J* Added the possibility to modify the value of a variable during debugging (right click on a watch variable and select "Modify value")^M^J* During Dev-C++ First Time COnfiguration window, users can now choose between using or not class browser and code completion features.^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.4^M^J* Added the possibility to specify an include directory for the code completion cache to be created at Dev-C++ first startup^M^J* Improved code completion cache^M^J* WebUpdate will now backup downloaded DevPaks in Dev-C++\Packages directory, and Dev-C++ executable in devcpp.exe.BACKUP^M^J* Big speed up in function parameters listing while editing^M^J* Bug fixes^M^J^M^JVersion 4.9.8.3^M^J* On Dev-C++ first time configuration dialog, a code completion cache of all the standard ^M^J include files can now be generated.^M^J* Improved WebUpdate module^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.2^M^J* New debug feature for DLLs: attach to a running process^M^J* New project option: Use custom Makefile. ^M^J* New WebUpdater module.^M^J* Allow user to specify an alternate configuration file in Environment Options ^M^J (still can be overriden by using "-c" command line parameter).^M^J* Lots of bug fixes.^M^J^M^JVersion 4.9.8.1^M^J* When creating a DLL, the created static lib respects now the project-defined output directory^M^J^M^JVersion 4.9.8.0^M^J* Changed position of compiler/linker parameters in Project Options.^M^J* Improved help file^M^J* Bug fixes^M^J^M^JVersion 4.9.7.9^M^J* Resource errors are now reported in the Resource sheet^M^J* Many bug fixes^M^J^M^JVersion 4.9.7.8^M^J* Made whole bottom report control floating instead of only debug output.^M^J* Many bug fixes^M^J^M^JVersion 4.9.7.7^M^J* Printing settings are now saved^M^J* New environment options : "watch variable under mouse" and "Report watch errors"^M^J* Bug fixes^M^J^M^JVersion 4.9.7.6^M^J* Debug variable browser^M^J* Added possibility to include in a Template the Project's directories , mime_type=text/plain, mime_types=[[strength=-20, mime=text/plain]], info=, u2_events=] + +1254722771.469814 file_over_new_connection + [0] f: fa_file = [id=FL9Y0d45OI4LpS6fmh, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=NEWS.txt], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=]^J}, last_active=1254722771.469814, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=3000, bof_buffer=Version 4.9.9.1^M^J* Many bug fixes^M^J* Improved editor^M^J^M^JVersion 4.9.9.0^M^J* Support for latest Mingw compiler system builds^M^J* Bug fixes^M^J^M^JVersion 4.9.8.9^M^J* New code tooltip display^M^J* Improved Indent/Unindent and Remove Comment^M^J* Improved automatic indent^M^J* Added support for the "interface" keyword^M^J* WebUpdate should now report installation problems from PackMan^M^J* New splash screen and association icons^M^J* Improved installer^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.7^M^J* Added support for GCC > 3.2^M^J* Debug variables are now resent during next debug session^M^J* Watched Variables not in correct context are now kept and updated when it is needed^M^J* Added new compiler/linker options: ^M^J - Strip executable^M^J - Generate instructions for a specific machine (i386, i486, i586, i686, pentium, pentium-mmx, pentiumpro, pentium2, pentium3, pentium4, ^M^J k6, k6-2, k6-3, athlon, athlon-tbird, athlon-4, athlon-xp, athlon-mp, winchip-c6, winchip2, k8, c3 and c3-2)^M^J - Enable use of processor specific built-in functions (mmmx, sse, sse2, pni, 3dnow)^M^J* "Default" button in Compiler Options is back^M^J* Error messages parsing improved^M^J* Bug fixes^M^J^M^JVersion 4.9.8.5^M^J* Added the possibility to modify the value of a variable during debugging (right click on a watch variable and select "Modify value")^M^J* During Dev-C++ First Time COnfiguration window, users can now choose between using or not class browser and code completion features.^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.4^M^J* Added the possibility to specify an include directory for the code completion cache to be created at Dev-C++ first startup^M^J* Improved code completion cache^M^J* WebUpdate will now backup downloaded DevPaks in Dev-C++\Packages directory, and Dev-C++ executable in devcpp.exe.BACKUP^M^J* Big speed up in function parameters listing while editing^M^J* Bug fixes^M^J^M^JVersion 4.9.8.3^M^J* On Dev-C++ first time configuration dialog, a code completion cache of all the standard ^M^J include files can now be generated.^M^J* Improved WebUpdate module^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.2^M^J* New debug feature for DLLs: attach to a running process^M^J* New project option: Use custom Makefile. ^M^J* New WebUpdater module.^M^J* Allow user to specify an alternate configuration file in Environment Options ^M^J (still can be overriden by using "-c" command line parameter).^M^J* Lots of bug fixes.^M^J^M^JVersion 4.9.8.1^M^J* When creating a DLL, the created static lib respects now the project-defined output directory^M^J^M^JVersion 4.9.8.0^M^J* Changed position of compiler/linker parameters in Project Options.^M^J* Improved help file^M^J* Bug fixes^M^J^M^JVersion 4.9.7.9^M^J* Resource errors are now reported in the Resource sheet^M^J* Many bug fixes^M^J^M^JVersion 4.9.7.8^M^J* Made whole bottom report control floating instead of only debug output.^M^J* Many bug fixes^M^J^M^JVersion 4.9.7.7^M^J* Printing settings are now saved^M^J* New environment options : "watch variable under mouse" and "Report watch errors"^M^J* Bug fixes^M^J^M^JVersion 4.9.7.6^M^J* Debug variable browser^M^J* Added possibility to include in a Template the Project's directories , mime_type=text/plain, mime_types=[[strength=-20, mime=text/plain]], info=[ts=1254722771.469814, fuid=FL9Y0d45OI4LpS6fmh, tx_hosts={^J^J}, rx_hosts={^J^J}, conn_uids={^J^J}, source=SMTP, depth=0, analyzers={^J^J}, mime_type=text/plain, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=] + [1] c: connection = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^ISMTP^J}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^J}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^J}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=NEWS.txt], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=] + [2] is_orig: bool = F + 1254722771.858334 mime_end_entity [0] c: connection = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=14699, state=4, num_pkts=23, num_bytes_ip=21438, flow_label=0], resp=[size=462, state=4, num_pkts=15, num_bytes_ip=1070, flow_label=0], start_time=1254722767.529046, duration=4.329288, service={^J^ISMTP^J}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^J}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^J}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=NEWS.txt], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9, FL9Y0d45OI4LpS6fmh]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=] @@ -413,7 +413,7 @@ [2] is_orig: bool = T 1254722771.858334 file_state_remove - [0] f: fa_file = [id=FL9Y0d45OI4LpS6fmh, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=14699, state=4, num_pkts=23, num_bytes_ip=21438, flow_label=0], resp=[size=462, state=4, num_pkts=15, num_bytes_ip=1070, flow_label=0], start_time=1254722767.529046, duration=4.329288, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=, fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9, FL9Y0d45OI4LpS6fmh]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=]^J}, last_active=1254722771.858316, seen_bytes=10809, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=1024, bof_buffer=Version 4.9.9.1^M^J* Many bug fixes^M^J* Improved editor^M^J^M^JVersion 4.9.9.0^M^J* Support for latest Mingw compiler system builds^M^J* Bug fixes^M^J^M^JVersion 4.9.8.9^M^J* New code tooltip display^M^J* Improved Indent/Unindent and Remove Comment^M^J* Improved automatic indent^M^J* Added support for the "interface" keyword^M^J* WebUpdate should now report installation problems from PackMan^M^J* New splash screen and association icons^M^J* Improved installer^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.7^M^J* Added support for GCC > 3.2^M^J* Debug variables are now resent during next debug session^M^J* Watched Variables not in correct context are now kept and updated when it is needed^M^J* Added new compiler/linker options: ^M^J - Strip executable^M^J - Generate instructions for a specific machine (i386, i486, i586, i686, pentium, pentium-mmx, pentiumpro, pentium2, pentium3, pentium4, ^M^J k6, k6-2, k6-3, athlon, athlon-tbird, athlon-4, athlon-xp, athlon-mp, winchip-c6, winchip2, k8, c3 and c3-2)^M^J - Enable use of processor specific built-in functions (mmmx, sse, , mime_type=text/plain, mime_types=[[strength=-20, mime=text/plain]], info=[ts=1254722770.692823, fuid=FL9Y0d45OI4LpS6fmh, tx_hosts={^J^I74.53.140.153^J}, rx_hosts={^J^I10.10.1.4^J}, conn_uids={^J^ICjhGID4nQcgTWjvg4c^J}, source=SMTP, depth=5, analyzers={^J^J}, mime_type=text/plain, filename=NEWS.txt, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=] + [0] f: fa_file = [id=FL9Y0d45OI4LpS6fmh, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=14699, state=4, num_pkts=23, num_bytes_ip=21438, flow_label=0], resp=[size=462, state=4, num_pkts=15, num_bytes_ip=1070, flow_label=0], start_time=1254722767.529046, duration=4.329288, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=, fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9, FL9Y0d45OI4LpS6fmh]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=]^J}, last_active=1254722771.858316, seen_bytes=10809, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=3000, bof_buffer=Version 4.9.9.1^M^J* Many bug fixes^M^J* Improved editor^M^J^M^JVersion 4.9.9.0^M^J* Support for latest Mingw compiler system builds^M^J* Bug fixes^M^J^M^JVersion 4.9.8.9^M^J* New code tooltip display^M^J* Improved Indent/Unindent and Remove Comment^M^J* Improved automatic indent^M^J* Added support for the "interface" keyword^M^J* WebUpdate should now report installation problems from PackMan^M^J* New splash screen and association icons^M^J* Improved installer^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.7^M^J* Added support for GCC > 3.2^M^J* Debug variables are now resent during next debug session^M^J* Watched Variables not in correct context are now kept and updated when it is needed^M^J* Added new compiler/linker options: ^M^J - Strip executable^M^J - Generate instructions for a specific machine (i386, i486, i586, i686, pentium, pentium-mmx, pentiumpro, pentium2, pentium3, pentium4, ^M^J k6, k6-2, k6-3, athlon, athlon-tbird, athlon-4, athlon-xp, athlon-mp, winchip-c6, winchip2, k8, c3 and c3-2)^M^J - Enable use of processor specific built-in functions (mmmx, sse, sse2, pni, 3dnow)^M^J* "Default" button in Compiler Options is back^M^J* Error messages parsing improved^M^J* Bug fixes^M^J^M^JVersion 4.9.8.5^M^J* Added the possibility to modify the value of a variable during debugging (right click on a watch variable and select "Modify value")^M^J* During Dev-C++ First Time COnfiguration window, users can now choose between using or not class browser and code completion features.^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.4^M^J* Added the possibility to specify an include directory for the code completion cache to be created at Dev-C++ first startup^M^J* Improved code completion cache^M^J* WebUpdate will now backup downloaded DevPaks in Dev-C++\Packages directory, and Dev-C++ executable in devcpp.exe.BACKUP^M^J* Big speed up in function parameters listing while editing^M^J* Bug fixes^M^J^M^JVersion 4.9.8.3^M^J* On Dev-C++ first time configuration dialog, a code completion cache of all the standard ^M^J include files can now be generated.^M^J* Improved WebUpdate module^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.2^M^J* New debug feature for DLLs: attach to a running process^M^J* New project option: Use custom Makefile. ^M^J* New WebUpdater module.^M^J* Allow user to specify an alternate configuration file in Environment Options ^M^J (still can be overriden by using "-c" command line parameter).^M^J* Lots of bug fixes.^M^J^M^JVersion 4.9.8.1^M^J* When creating a DLL, the created static lib respects now the project-defined output directory^M^J^M^JVersion 4.9.8.0^M^J* Changed position of compiler/linker parameters in Project Options.^M^J* Improved help file^M^J* Bug fixes^M^J^M^JVersion 4.9.7.9^M^J* Resource errors are now reported in the Resource sheet^M^J* Many bug fixes^M^J^M^JVersion 4.9.7.8^M^J* Made whole bottom report control floating instead of only debug output.^M^J* Many bug fixes^M^J^M^JVersion 4.9.7.7^M^J* Printing settings are now saved^M^J* New environment options : "watch variable under mouse" and "Report watch errors"^M^J* Bug fixes^M^J^M^JVersion 4.9.7.6^M^J* Debug variable browser^M^J* Added possibility to include in a Template the Project's directories , mime_type=text/plain, mime_types=[[strength=-20, mime=text/plain]], info=[ts=1254722771.469814, fuid=FL9Y0d45OI4LpS6fmh, tx_hosts={^J^I74.53.140.153^J}, rx_hosts={^J^I10.10.1.4^J}, conn_uids={^J^ICjhGID4nQcgTWjvg4c^J}, source=SMTP, depth=5, analyzers={^J^J}, mime_type=text/plain, filename=NEWS.txt, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=] 1254722771.858334 get_file_handle [0] tag: enum = Analyzer::ANALYZER_SMTP From d600d41a558bb9d4a0e07d3bc389a1e6bd8441bd Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Mon, 3 Nov 2014 13:52:58 -0500 Subject: [PATCH 025/299] Add Windows detection based on CryptoAPI HTTP traffic as a software framework policy script. --- .../software/windows-version-detection.bro | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 scripts/policy/frameworks/software/windows-version-detection.bro diff --git a/scripts/policy/frameworks/software/windows-version-detection.bro b/scripts/policy/frameworks/software/windows-version-detection.bro new file mode 100644 index 0000000000..c9c4f45a80 --- /dev/null +++ b/scripts/policy/frameworks/software/windows-version-detection.bro @@ -0,0 +1,67 @@ +##! Windows systems access a Microsoft Certificate Revocation List (CRL) periodically. The +##! user agent for these requests reveals which version of Crypt32.dll installed on the system, +##! which can uniquely identify the version of Windows that's running. +##! +##! This script will log the version of Windows that was identified to the Software framework. + +@load base/frameworks/software + +module OS; + +export { + redef enum Software::Type += { + ## Identifier for Windows operating system versions + WINDOWS, + }; + + type Software::name_and_version: record { + name : string; + version: Software::Version; + }; + + const crypto_api_mapping: table[string] of Software::name_and_version = { + ["Microsoft-CryptoAPI/5.131.2195.6661"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2195, $minor3=6661, $addl="2000 SP4"]], + ["Microsoft-CryptoAPI/5.131.2195.6824"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2195, $minor3=6824, $addl="2000 with MS04-11"]], + ["Microsoft-CryptoAPI/5.131.2195.6926"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2195, $minor3=6926, $addl="2000 with Hotfix 98830"]], + + ["Microsoft-CryptoAPI/5.131.2600.0"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=0, $addl="XP SP0"]], + ["Microsoft-CryptoAPI/5.131.2600.1106"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=1106, $addl="XP SP1"]], + ["Microsoft-CryptoAPI/5.131.2600.2180"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=2180, $addl="XP SP2"]], + ["Microsoft-CryptoAPI/5.131.2600.3180"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=3180, $addl="XP SP3 Beta 1"]], + ["Microsoft-CryptoAPI/5.131.2600.3205"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=3205, $addl="XP SP3 Beta 2"]], + ["Microsoft-CryptoAPI/5.131.2600.3249"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=3249, $addl="XP SP3 RC Beta"]], + ["Microsoft-CryptoAPI/5.131.2600.3264"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=3264, $addl="XP SP3 RC1"]], + ["Microsoft-CryptoAPI/5.131.2600.3282"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=3282, $addl="XP SP3 RC1 Update"]], + ["Microsoft-CryptoAPI/5.131.2600.3300"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=3300, $addl="XP SP3 RC2"]], + ["Microsoft-CryptoAPI/5.131.2600.3311"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=3311, $addl="XP SP3 RC2 Update"]], + ["Microsoft-CryptoAPI/5.131.2600.5508"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=5508, $addl="XP SP3 RC2 Update 2"]], + ["Microsoft-CryptoAPI/5.131.2600.5512"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=5512, $addl="XP SP3"]], + + ["Microsoft-CryptoAPI/5.131.3790.0"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=3790, $minor3=0, $addl="XP x64 or Server 2003 SP0"]], + ["Microsoft-CryptoAPI/5.131.3790.1830"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=3790, $minor3=1830, $addl="XP x64 or Server 2003 SP1"]], + ["Microsoft-CryptoAPI/5.131.3790.3959"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=3790, $minor3=3959, $addl="XP x64 or Server 2003 SP2"]], + ["Microsoft-CryptoAPI/5.131.3790.5235"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=3790, $minor3=5235, $addl="XP x64 or Server 2003 with MS13-095"]], + + ["Microsoft-CryptoAPI/6.0"] = [$name="Windows", $version=[$major=6, $minor=0, $addl="Vista or Server 2008"]], + ["Microsoft-CryptoAPI/6.1"] = [$name="Windows", $version=[$major=6, $minor=1, $addl="7 or Server 2008 R2"]], + ["Microsoft-CryptoAPI/6.2"] = [$name="Windows", $version=[$major=6, $minor=2, $addl="8 or Server 2012"]], + ["Microsoft-CryptoAPI/6.3"] = [$name="Windows", $version=[$major=6, $minor=3, $addl="8.1 or Server 2012 R2"]], + } &redef; +} + +event HTTP::log_http(rec: HTTP::Info) &priority=5 + { + if ( rec?$host && rec?$user_agent && rec$host == "crl.microsoft.com" && + /Microsoft-CryptoAPI\// in rec$user_agent ) + { + if ( rec$user_agent !in crypto_api_mapping ) + { + Software::found(rec$id, [$unparsed_version=sub(rec$user_agent, /Microsoft-CryptoAPI/, "Unknown CryptoAPI Version"), $host=rec$id$orig_h, $software_type=WINDOWS]); + } + else + { + local result = crypto_api_mapping[rec$user_agent]; + Software::found(rec$id, [$version=result$version, $name=result$name, $host=rec$id$orig_h, $software_type=WINDOWS]); + } + } + } From 7ee34981aa0873207a39c6077164a50fddca9071 Mon Sep 17 00:00:00 2001 From: Seth Hall Date: Wed, 5 Nov 2014 11:31:48 -0500 Subject: [PATCH 026/299] Improve TAR file detection and other small changes. - Remove all of the x-c detections. Nearly all false positives. - Remove the back up TAR detections. Not very helpful. - Remove one of the x-elc detections that was too loose and caused many false positives. --- .../base/frameworks/files/magic/general.sig | 6 +- .../base/frameworks/files/magic/libmagic.sig | 72 +++++++++---------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/scripts/base/frameworks/files/magic/general.sig b/scripts/base/frameworks/files/magic/general.sig index a36e32ef28..500c4f7be0 100644 --- a/scripts/base/frameworks/files/magic/general.sig +++ b/scripts/base/frameworks/files/magic/general.sig @@ -6,8 +6,8 @@ signature file-plaintext { } signature file-tar { - file-magic /^([[:print:]\x00]){100}(([[:digit:]\x00\x20]){8}){3}/ - file-mime "application/x-tar", 150 + file-magic /^[[:print:]\x00]{100}([[:digit:]\x20]{7}\x00){3}([[:digit:]\x20]{11}\x00){2}([[:digit:]\x00\x20]{7}[\x20\x00])[0-7\x00]/ + file-mime "application/x-tar", 100 } signature file-zip { @@ -120,7 +120,7 @@ signature file-python { } signature file-php { - file-magic /.*<\?php/ + file-magic /^.*<\?php/ file-mime "text/x-php", 40 } diff --git a/scripts/base/frameworks/files/magic/libmagic.sig b/scripts/base/frameworks/files/magic/libmagic.sig index 92e1da68ae..72ec40dff8 100644 --- a/scripts/base/frameworks/files/magic/libmagic.sig +++ b/scripts/base/frameworks/files/magic/libmagic.sig @@ -616,10 +616,10 @@ signature file-magic-auto116 { } # >257 string,=ustar \000 (len=8), ["GNU tar archive"], swap_endian=0 -signature file-magic-auto117 { - file-mime "application/x-tar", 110 - file-magic /(.{257})(ustar \x00)/ -} +#signature file-magic-auto117 { +# file-mime "application/x-tar", 110 +# file-magic /(.{257})(ustar \x00)/ +#} # >0 string,=257 string,=ustar\000 (len=6), ["POSIX tar archive"], swap_endian=0 -signature file-magic-auto131 { - file-mime "application/x-tar", 90 - file-magic /(.{257})(ustar\x00)/ -} +#signature file-magic-auto131 { +# file-mime "application/x-tar", 90 +# file-magic /(.{257})(ustar\x00)/ +#} # >0 string,=AC1.40 (len=6), ["DWG AutoDesk AutoCAD Release 1.40"], swap_endian=0 signature file-magic-auto132 { @@ -2882,10 +2882,10 @@ signature file-magic-auto480 { } # >0 string,=\n( (len=2), ["Emacs v18 byte-compiled Lisp data"], swap_endian=0 -signature file-magic-auto481 { - file-mime "application/x-elc", 50 - file-magic /(\x0a\x28)/ -} +#signature file-magic-auto481 { +# file-mime "application/x-elc", 50 +# file-magic /(\x0a\x28)/ +#} # >0 string,=\021\t (len=2), ["Award BIOS Logo, 136 x 126"], swap_endian=0 signature file-magic-auto482 { @@ -3148,10 +3148,10 @@ signature file-magic-auto521 { } # >0 regex,=^class[ \t\n]+ (len=12), ["C++ source text"], swap_endian=0 -signature file-magic-auto522 { - file-mime "text/x-c++", 47 - file-magic /(.*)(class[ \x09\x0a]+[[:alnum:]_]+)(.*)(\x7b)(.*)(public:)/ -} +#signature file-magic-auto522 { +# file-mime "text/x-c++", 47 +# file-magic /(.*)(class[ \x09\x0a]+[[:alnum:]_]+)(.*)(\x7b)(.*)(public:)/ +#} # >0 search/1,=This is Info file (len=17), ["GNU Info text"], swap_endian=0 signature file-magic-auto528 { @@ -3363,10 +3363,10 @@ signature file-magic-auto556 { } # >0 regex,=^extern[ \t\n]+ (len=13), ["C source text"], swap_endian=0 -signature file-magic-auto557 { - file-mime "text/x-c", 43 - file-magic /(.*)(extern[ \x09\x0a]+)/ -} +#signature file-magic-auto557 { +# file-mime "text/x-c", 43 +# file-magic /(.*)(extern[ \x09\x0a]+)/ +#} # >0 search/4096,=% -*-latex-*- (len=13), ["LaTeX document text"], swap_endian=0 signature file-magic-auto558 { @@ -3382,10 +3382,10 @@ signature file-magic-auto558 { #} # >0 regex,=^struct[ \t\n]+ (len=13), ["C source text"], swap_endian=0 -signature file-magic-auto560 { - file-mime "text/x-c", 43 - file-magic /(.*)(struct[ \x09\x0a]+)/ -} +#signature file-magic-auto560 { +# file-mime "text/x-c", 43 +# file-magic /(.*)(struct[ \x09\x0a]+)/ +#} # >0 search/w/1,=#!/bin/nodejs (len=13), ["Node.js script text executable"], swap_endian=0 signature file-magic-auto561 { @@ -3438,10 +3438,10 @@ signature file-magic-auto567 { } # >0 regex,=^char[ \t\n]+ (len=11), ["C source text"], swap_endian=0 -signature file-magic-auto568 { - file-mime "text/x-c", 41 - file-magic /(.*)(char[ \x09\x0a]+)/ -} +#signature file-magic-auto568 { +# file-mime "text/x-c", 41 +# file-magic /(.*)(char[ \x09\x0a]+)/ +#} # >0 search/1,=#! (len=2), [""], swap_endian=0 # >>0 regex,=^#!.*/bin/perl$ (len=15), ["Perl script text executable"], swap_endian=0 @@ -3524,10 +3524,10 @@ signature file-magic-auto578 { } # >0 search/8192,=main( (len=5), ["C source text"], swap_endian=0 -signature file-magic-auto581 { - file-mime "text/x-c", 40 - file-magic /(.*)(main\x28)/ -} +#signature file-magic-auto581 { +# file-mime "text/x-c", 40 +# file-magic /(.*)(main\x28)/ +#} # Not specific enough. # >0 search/1,=\" (len=2), ["troff or preprocessor input text"], swap_endian=0 @@ -3556,10 +3556,10 @@ signature file-magic-auto584 { #} # >0 regex,=^#include (len=9), ["C source text"], swap_endian=0 -signature file-magic-auto586 { - file-mime "text/x-c", 39 - file-magic /(.*)(#include)/ -} +#signature file-magic-auto586 { +# file-mime "text/x-c", 39 +# file-magic /(.*)(#include)/ +#} # >0 search/1,=.\" (len=3), ["troff or preprocessor input text"], swap_endian=0 #signature file-magic-auto587 { From 3c42350e77eff3c163bc38e4c15d2533c6c8bef8 Mon Sep 17 00:00:00 2001 From: akasza Date: Wed, 5 Nov 2014 20:44:03 -0800 Subject: [PATCH 027/299] uri parsing function --- scripts/base/utils/urls.bro | 58 +++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/scripts/base/utils/urls.bro b/scripts/base/utils/urls.bro index 8ef9ed7e2d..6c9eada67a 100644 --- a/scripts/base/utils/urls.bro +++ b/scripts/base/utils/urls.bro @@ -3,6 +3,16 @@ ## A regular expression for matching and extracting URLs. const url_regex = /^([a-zA-Z\-]{3,5})(:\/\/[^\/?#"'\r\n><]*)([^?#"'\r\n><]*)([^[:blank:]\r\n"'><]*|\??[^"'\r\n><]*)/ &redef; +type uri_record: record { + protocol: string &optional; + # this could be a domain name or an IP address + netlocation: string; + portnum: count &optional; + path: string &optional; + file_name: string &optional; + file_ext: string &optional; +}; + ## Extracts URLs discovered in arbitrary text. function find_all_urls(s: string): string_set { @@ -23,3 +33,51 @@ function find_all_urls_without_scheme(s: string): string_set return return_urls; } + +function decompose_uri(s: string): uri_record + { + local parts: string_array; + local u: uri = [$netlocation=""]; + + if (/:\/\// in s) + { + parts = split1(s, /:\/\//); + u$protocol = parts[1]; + s = parts[2]; + } + if (/\// in s) + { + parts = split1(s, /\//); + s = parts[1]; + u$path = fmt("/%s", parts[2]); + + if (|u$path| > 1) + { + local last_token: string = find_last(u$path, /\/.+/); + local full_filename = split1(last_token, /\//)[2]; + if (/\./ in full_filename) + { + u$file_name = split1(full_filename, /\./)[1]; + u$file_ext = split1(full_filename, /\./)[2]; + u$path = subst_string(u$path, fmt("%s.%s", u$file_name, u$file_ext), ""); + } + else + { + u$file_name = full_filename; + u$path = subst_string(u$path, u$file_name, ""); + } + } + } + if (/:/ in s) + { + parts = split1(s, /:/); + u$netlocation = parts[1]; + u$portnum = to_count(parts[2]); + } + else + { + u$netlocation = s; + } + + return u; + } From 69ce4d30382a90d121132bb0fcc699f6ed726b3e Mon Sep 17 00:00:00 2001 From: akasza Date: Thu, 6 Nov 2014 19:47:28 -0800 Subject: [PATCH 028/299] uri_decompose complete, need btests --- scripts/base/utils/urls.bro | 47 +++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/scripts/base/utils/urls.bro b/scripts/base/utils/urls.bro index 6c9eada67a..9beb424489 100644 --- a/scripts/base/utils/urls.bro +++ b/scripts/base/utils/urls.bro @@ -3,14 +3,16 @@ ## A regular expression for matching and extracting URLs. const url_regex = /^([a-zA-Z\-]{3,5})(:\/\/[^\/?#"'\r\n><]*)([^?#"'\r\n><]*)([^[:blank:]\r\n"'><]*|\??[^"'\r\n><]*)/ &redef; -type uri_record: record { - protocol: string &optional; +type URI: record { + scheme: string &optional; # this could be a domain name or an IP address netlocation: string; portnum: count &optional; path: string &optional; file_name: string &optional; file_ext: string &optional; + params_k: table[count] of string; + params_v: table[count] of string; }; ## Extracts URLs discovered in arbitrary text. @@ -34,11 +36,49 @@ function find_all_urls_without_scheme(s: string): string_set return return_urls; } -function decompose_uri(s: string): uri_record +function decompose_uri(s: string): URI { local parts: string_array; local u: uri = [$netlocation=""]; + if ( /\?/ in s) + { + local k: table[count] of string; + local v: table[count] of string; + u$params_k = k; + u$params_v = v; + + parts = split1(s, /\?/); + s = parts[1]; + local query: string = parts[2]; + if (/&/ in query) + { + local opv: table[count] of string = split(query, /&/); + + for (each in opv) + { + if (/=/ in opv[each]) + { + parts = split1(opv[each], /=/); + + # why does the order here matter? + u$params_k[each] = parts[1]; + u$params_v[each] = parts[2]; + } + else + { + # malformed URI + # domain.tld/path/file.ext?foo& + } + } + } + else + { + parts = split1(query, /=/); + u$params_k[0] = parts[1]; + u$params_v[0] = parts[2]; + } + } if (/:\/\// in s) { parts = split1(s, /:\/\//); @@ -78,6 +118,5 @@ function decompose_uri(s: string): uri_record { u$netlocation = s; } - return u; } From ea79c07730268cfa7ac515860ad3c5d678b93c87 Mon Sep 17 00:00:00 2001 From: akasza Date: Thu, 6 Nov 2014 19:52:03 -0800 Subject: [PATCH 029/299] uri parsing complete --- scripts/base/utils/urls.bro | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scripts/base/utils/urls.bro b/scripts/base/utils/urls.bro index 9beb424489..1f42d517d6 100644 --- a/scripts/base/utils/urls.bro +++ b/scripts/base/utils/urls.bro @@ -61,15 +61,9 @@ function decompose_uri(s: string): URI { parts = split1(opv[each], /=/); - # why does the order here matter? u$params_k[each] = parts[1]; u$params_v[each] = parts[2]; } - else - { - # malformed URI - # domain.tld/path/file.ext?foo& - } } } else From f99bc98800c0b6ba678da5e800adf71aaa401cca Mon Sep 17 00:00:00 2001 From: Johanna Amann Date: Mon, 17 Nov 2014 09:51:47 -0800 Subject: [PATCH 030/299] for dh key exchanges, use p as the parameter for weak key exchanges. Y can be a few bytes smaller due to the modulo operation - this is ok. --- scripts/policy/protocols/ssl/weak-keys.bro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/policy/protocols/ssl/weak-keys.bro b/scripts/policy/protocols/ssl/weak-keys.bro index e849c3c06c..82cc3a2b5f 100644 --- a/scripts/policy/protocols/ssl/weak-keys.bro +++ b/scripts/policy/protocols/ssl/weak-keys.bro @@ -65,7 +65,7 @@ event ssl_dh_server_params(c: connection, p: string, q: string, Ys: string) &pri if ( ! addr_matches_host(c$id$resp_h, notify_weak_keys) ) return; - local key_length = |Ys| * 8; # key length in bits + local key_length = |p| * 8; # length of the used prime number in bits if ( key_length < notify_minimal_key_length ) NOTICE([$note=Weak_Key, From f214158cc5f646b5e580ea786835812f9557aff1 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 18 Nov 2014 12:40:16 -0600 Subject: [PATCH 031/299] BIT-1288: Improve coercion of &default expressions. --- src/Attr.cc | 11 ++++++++ src/Attr.h | 7 +++++ .../language.attr-default-coercion/out | 5 ++++ .../Baseline/language.named-table-ctors/out | 2 +- .../btest/language/attr-default-coercion.bro | 26 +++++++++++++++++++ 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 testing/btest/Baseline/language.attr-default-coercion/out create mode 100644 testing/btest/language/attr-default-coercion.bro diff --git a/src/Attr.cc b/src/Attr.cc index d6d0f6e68d..13106b02b7 100644 --- a/src/Attr.cc +++ b/src/Attr.cc @@ -265,6 +265,14 @@ void Attributes::CheckAttr(Attr* a) // Ok. break; + Expr* e = a->AttrExpr(); + if ( check_and_promote_expr(e, type) ) + { + a->SetAttrExpr(e); + // Ok. + break; + } + a->AttrExpr()->Error("&default value has inconsistent type", type); } @@ -297,8 +305,11 @@ void Attributes::CheckAttr(Attr* a) Expr* e = a->AttrExpr(); if ( check_and_promote_expr(e, ytype) ) + { + a->SetAttrExpr(e); // Ok. break; + } Error("&default value has inconsistent type 2"); } diff --git a/src/Attr.h b/src/Attr.h index 7becbb27eb..86f5a28521 100644 --- a/src/Attr.h +++ b/src/Attr.h @@ -45,6 +45,13 @@ public: attr_tag Tag() const { return tag; } Expr* AttrExpr() const { return expr; } + // Up to the caller to decide if previous expr can be unref'd since it may + // not always be safe. e.g. expressions (at time of writing) don't always + // keep careful track of referencing their operands, so doing something + // like SetAttrExpr(coerce(AttrExpr())) must not completely unref the + // previous expr as the new expr depends on it. + void SetAttrExpr(Expr* e) { expr = e; } + int RedundantAttrOkay() const { return tag == ATTR_REDEF || tag == ATTR_OPTIONAL; } diff --git a/testing/btest/Baseline/language.attr-default-coercion/out b/testing/btest/Baseline/language.attr-default-coercion/out new file mode 100644 index 0000000000..e6768c77b6 --- /dev/null +++ b/testing/btest/Baseline/language.attr-default-coercion/out @@ -0,0 +1,5 @@ +7.0 +[i=1, d=3.0] +237, 101.0 +-5, 101.0 +-37, -8.1 diff --git a/testing/btest/Baseline/language.named-table-ctors/out b/testing/btest/Baseline/language.named-table-ctors/out index 273afdd205..5a305f4d4b 100644 --- a/testing/btest/Baseline/language.named-table-ctors/out +++ b/testing/btest/Baseline/language.named-table-ctors/out @@ -16,7 +16,7 @@ [one] = 1.0, [three] = 3.0 } -0 +0.0 { [42] = forty-two, [37] = thirty-seven diff --git a/testing/btest/language/attr-default-coercion.bro b/testing/btest/language/attr-default-coercion.bro new file mode 100644 index 0000000000..14590d0033 --- /dev/null +++ b/testing/btest/language/attr-default-coercion.bro @@ -0,0 +1,26 @@ +# @TEST-EXEC: bro -b %INPUT >out +# @TEST-EXEC: btest-diff out + +type my_table: table[string] of double; + +type my_record: record { + i: int &default = 1; + d: double &default = 3; +}; + +global t: my_table &default = 7; +global r = my_record(); + +function foo(i: int &default = 237, d: double &default = 101) + { + print i, d; + } + +event bro_init() + { + print t["nope"]; + print r; + foo(); + foo(-5); + foo(-37, -8.1); + } From 5436faed6935e64eb1664e0df50998ed2598b5c8 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 19 Nov 2014 10:56:59 -0600 Subject: [PATCH 032/299] Disable verbose bison output. These logs aren't generally useful to build everytime, just when working on and debugging one of the various grammars and at least I haven't needed to look at them in years. Also, Ninja builds don't seem to work because of them (can probably improve the related CMake macros so the verbose logs do play nice with Ninja, but doesn't seem worth effort right now, see previous comment). --- CHANGES | 4 ++++ VERSION | 2 +- src/CMakeLists.txt | 8 ++++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index faf441eaae..8689b23c05 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,8 @@ +2.3-310 | 2014-11-19 10:56:59 -0600 + + * Disable verbose bison output. (Jon Siwek) + 2.3-309 | 2014-11-18 12:17:53 -0800 * New decompose_uri() function in base/utils/urls that splits a URI diff --git a/VERSION b/VERSION index 748ef210f1..f10ef60374 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3-309 +2.3-310 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9f94f8f1e3..13c6e45006 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,7 +48,7 @@ set(BISON_FLAGS "--debug") bison_target(BIFParser builtin-func.y ${CMAKE_CURRENT_BINARY_DIR}/bif_parse.cc HEADER ${CMAKE_CURRENT_BINARY_DIR}/bif_parse.h - VERBOSE ${CMAKE_CURRENT_BINARY_DIR}/bif_parse.output + #VERBOSE ${CMAKE_CURRENT_BINARY_DIR}/bif_parse.output COMPILE_FLAGS "${BISON_FLAGS}") flex_target(BIFScanner builtin-func.l ${CMAKE_CURRENT_BINARY_DIR}/bif_lex.cc) add_flex_bison_dependency(BIFScanner BIFParser) @@ -57,7 +57,7 @@ add_flex_bison_dependency(BIFScanner BIFParser) bison_target(RuleParser rule-parse.y ${CMAKE_CURRENT_BINARY_DIR}/rup.cc HEADER ${CMAKE_CURRENT_BINARY_DIR}/rup.h - VERBOSE ${CMAKE_CURRENT_BINARY_DIR}/rule_parse.output + #VERBOSE ${CMAKE_CURRENT_BINARY_DIR}/rule_parse.output COMPILE_FLAGS "${BISON_FLAGS}") replace_yy_prefix_target(${CMAKE_CURRENT_BINARY_DIR}/rup.cc ${CMAKE_CURRENT_BINARY_DIR}/rule-parse.cc @@ -72,7 +72,7 @@ flex_target(RuleScanner rule-scan.l ${CMAKE_CURRENT_BINARY_DIR}/rule-scan.cc bison_target(REParser re-parse.y ${CMAKE_CURRENT_BINARY_DIR}/rep.cc HEADER ${CMAKE_CURRENT_BINARY_DIR}/re-parse.h - VERBOSE ${CMAKE_CURRENT_BINARY_DIR}/re_parse.output + #VERBOSE ${CMAKE_CURRENT_BINARY_DIR}/re_parse.output COMPILE_FLAGS "${BISON_FLAGS}") replace_yy_prefix_target(${CMAKE_CURRENT_BINARY_DIR}/rep.cc ${CMAKE_CURRENT_BINARY_DIR}/re-parse.cc @@ -85,7 +85,7 @@ add_flex_bison_dependency(REScanner REParser) bison_target(Parser parse.y ${CMAKE_CURRENT_BINARY_DIR}/p.cc HEADER ${CMAKE_CURRENT_BINARY_DIR}/broparse.h - VERBOSE ${CMAKE_CURRENT_BINARY_DIR}/parse.output + #VERBOSE ${CMAKE_CURRENT_BINARY_DIR}/parse.output COMPILE_FLAGS "${BISON_FLAGS}") replace_yy_prefix_target(${CMAKE_CURRENT_BINARY_DIR}/p.cc ${CMAKE_CURRENT_BINARY_DIR}/parse.cc From 6055b56f5c8e6acba33be2efcb93b69a80961103 Mon Sep 17 00:00:00 2001 From: Gilbert Clark Date: Mon, 24 Nov 2014 14:28:17 -0500 Subject: [PATCH 033/299] Incremental --- src/Func.cc | 98 ++++++++++++++++++++------------------------ src/plugin/Plugin.cc | 54 ++++++++++++------------ src/plugin/Plugin.h | 84 ++++++++++++++++++------------------- 3 files changed, 114 insertions(+), 122 deletions(-) diff --git a/src/Func.cc b/src/Func.cc index 409bdcae25..ccc5698b2c 100644 --- a/src/Func.cc +++ b/src/Func.cc @@ -249,38 +249,34 @@ TraversalCode Func::Traverse(TraversalCallback* cb) const ValWrapper* Func::HandlePluginResult(ValWrapper* plugin_result, val_list* args, function_flavor flavor) const { - // We either have not received a plugin result, or the plugin result hasn't been processed (read: fall into ::Call method) - if(!plugin_result) - return NULL; + // We either have not received a plugin result, or the plugin result hasn't been processed (read: fall into ::Call method) + if(!plugin_result) + return NULL; - if(!plugin_result->processed) - { - if(plugin_result->value) - { - Unref(plugin_result->value); - plugin_result->value = NULL; - } - delete plugin_result; - return NULL; - } + if(!plugin_result->processed) + { + if(plugin_result->value) + { + Unref(plugin_result->value); + plugin_result->value = NULL; + } + delete plugin_result; + return NULL; + } switch ( flavor ) { case FUNC_FLAVOR_EVENT: - if(plugin_result->value) - { - char sbuf[1024]; - snprintf(sbuf, 1024, "plugin returned non-void result for event %s", this->Name()); - reporter->InternalError(sbuf); - } + if(plugin_result->value) + { + reporter->InternalError("plugin returned non-void result for event %s", this->Name()); + } break; case FUNC_FLAVOR_HOOK: if ( plugin_result->value->Type()->Tag() != TYPE_BOOL ) - { - char sbuf[1024]; - snprintf(sbuf, 1024, "plugin returned non-bool for hook %s", this->Name()); - reporter->InternalError(sbuf); - } + { + reporter->InternalError("plugin returned non-bool for hook %s", this->Name()); + } break; case FUNC_FLAVOR_FUNCTION: @@ -289,19 +285,15 @@ ValWrapper* Func::HandlePluginResult(ValWrapper* plugin_result, val_list* args, if ( (! yt) || yt->Tag() == TYPE_VOID ) { - if(plugin_result && plugin_result->value) - { - char sbuf[1024]; - snprintf(sbuf, 1024, "plugin returned non-void result for void method %s", this->Name()); - reporter->InternalError(sbuf); - } - } + if(plugin_result && plugin_result->value) + { + reporter->InternalError("plugin returned non-void result for void method %s", this->Name()); + } + } else if ( plugin_result->value && plugin_result->value->Type()->Tag() != yt->Tag() && yt->Tag() != TYPE_ANY) { - char sbuf[1024]; - snprintf(sbuf, 1024, "plugin returned wrong type (got %d, expecting %d) for %s", plugin_result->value->Type()->Tag(), yt->Tag(), this->Name()); - reporter->InternalError(sbuf); - } + reporter->InternalError("plugin returned wrong type (got %d, expecting %d) for %s", plugin_result->value->Type()->Tag(), yt->Tag(), this->Name()); + } break; } @@ -358,13 +350,13 @@ Val* BroFunc::Call(val_list* args, Frame* parent) const ValWrapper* plugin_result = PLUGIN_HOOK_WITH_RESULT(HOOK_CALL_FUNCTION, HookCallFunction(this, parent, args), 0); - plugin_result = HandlePluginResult(plugin_result, args, Flavor()); - if(plugin_result) - { - Val *result = plugin_result->value; - delete plugin_result; - return result; - } + plugin_result = HandlePluginResult(plugin_result, args, Flavor()); + if(plugin_result) + { + Val *result = plugin_result->value; + delete plugin_result; + return result; + } if ( bodies.empty() ) { @@ -455,11 +447,11 @@ Val* BroFunc::Call(val_list* args, Frame* parent) const // Warn if the function returns something, but we returned from // the function without an explicit return, or without a value. else if ( FType()->YieldType() && FType()->YieldType()->Tag() != TYPE_VOID && - (flow != FLOW_RETURN /* we fell off the end */ || - ! result /* explicit return with no result */) && - ! f->HasDelayed() ) + (flow != FLOW_RETURN /* we fell off the end */ || + ! result /* explicit return with no result */) && + ! f->HasDelayed() ) reporter->Warning("non-void function returns without a value: %s", - Name()); + Name()); if ( result && g_trace_state.DoTrace() ) { @@ -580,13 +572,13 @@ Val* BuiltinFunc::Call(val_list* args, Frame* parent) const ValWrapper* plugin_result = PLUGIN_HOOK_WITH_RESULT(HOOK_CALL_FUNCTION, HookCallFunction(this, parent, args), 0); - plugin_result = HandlePluginResult(plugin_result, args, FUNC_FLAVOR_FUNCTION); - if(plugin_result) - { - Val *result = plugin_result->value; - delete plugin_result; - return result; - } + plugin_result = HandlePluginResult(plugin_result, args, FUNC_FLAVOR_FUNCTION); + if(plugin_result) + { + Val *result = plugin_result->value; + delete plugin_result; + return result; + } if ( g_trace_state.DoTrace() ) { diff --git a/src/plugin/Plugin.cc b/src/plugin/Plugin.cc index 1e98532ba6..9a571743af 100644 --- a/src/plugin/Plugin.cc +++ b/src/plugin/Plugin.cc @@ -83,12 +83,12 @@ void HookArgument::Describe(ODesc* d) const d->Add(""); break; - case FRAME: - if ( arg.frame ) - arg.frame->Describe(d); - else - d->Add(""); - break; + case FRAME: + if ( arg.frame ) + arg.frame->Describe(d); + else + d->Add(""); + break; case FUNC: if ( arg.func ) @@ -131,25 +131,25 @@ void HookArgument::Describe(ODesc* d) const case VOIDP: d->Add(""); break; - - case WRAPPED_VAL: - if ( arg.wrapper ) - { - d->Add("wrapped("); - if(arg.wrapper->value) - { - arg.wrapper->value->Describe(d); - } - else - d->Add(""); - d->Add(")"); - } - else - { - d->Add(""); - } + + case WRAPPED_VAL: + if ( arg.wrapper ) + { + d->Add("wrapped("); + if(arg.wrapper->value) + { + arg.wrapper->value->Describe(d); + } + else + d->Add(""); + d->Add(")"); + } + else + { + d->Add(""); + } - break; + break; } } @@ -226,7 +226,7 @@ void Plugin::InitPostScript() Plugin::bif_item_list Plugin::BifItems() const { - return bif_items; + return bif_items; } void Plugin::Done() @@ -399,7 +399,7 @@ void Plugin::Describe(ODesc* d) const type = ""; } - d->Add(" "); + d->Add(" "); d->Add("["); d->Add(type); d->Add("] "); @@ -414,7 +414,7 @@ void Plugin::Describe(ODesc* d) const HookType hook = (*i).first; int prio = (*i).second; - d->Add(" Implements "); + d->Add(" Implements "); d->Add(hook_name(hook)); d->Add(" (priority "); d->Add(prio); diff --git a/src/plugin/Plugin.h b/src/plugin/Plugin.h index 65acb37b7a..af47a5f4bf 100644 --- a/src/plugin/Plugin.h +++ b/src/plugin/Plugin.h @@ -34,24 +34,24 @@ class Plugin; * Plugins' function handlers return a result of this type. */ struct ValWrapper { - Val* value; //< value being wrapped by this object - bool processed; //< true if execution should *STOP* (read: the plugin is replacing a method), and false if execution should *CONTINUE* (read: bro should execute a method) + Val* value; //< value being wrapped by this object + bool processed; //< true if execution should *STOP* (read: the plugin is replacing a method), and false if execution should *CONTINUE* (read: bro should execute a method) - /** - Wrapper for a specific value. If we're setting a value, we assume we've processed something. - - @param value value to be wrapped - */ - ValWrapper(Val* value) - : value(value), processed(true) { } + /** + Wrapper for a specific value. If we're setting a value, we assume we've processed something. + + @param value value to be wrapped + */ + ValWrapper(Val* value) + : value(value), processed(true) { } - /** - Wrapper for a specific value. If we're setting 'processed', we assume there's a reason we're not setting a Val and set that to NULL. - - @param processed whether or not an execution of a function was handled by the plugin - */ - ValWrapper(bool processed) - : value(NULL), processed(processed) { } + /** + Wrapper for a specific value. If we're setting 'processed', we assume there's a reason we're not setting a Val and set that to NULL. + + @param processed whether or not an execution of a function was handled by the plugin + */ + ValWrapper(bool processed) + : value(NULL), processed(processed) { } }; /** @@ -237,15 +237,15 @@ public: */ HookArgument(void* p) { type = VOIDP; arg.voidp = p; } - /** - * Constructor with a ValWrapper argument. - */ - HookArgument(ValWrapper* a) { type = WRAPPED_VAL; arg.wrapper = a; } + /** + * Constructor with a ValWrapper argument. + */ + HookArgument(ValWrapper* a) { type = WRAPPED_VAL; arg.wrapper = a; } - /** - * Constructor with a Frame argument. - */ - HookArgument(Frame* f) { type = FRAME; arg.frame = f; } + /** + * Constructor with a Frame argument. + */ + HookArgument(Frame* f) { type = FRAME; arg.frame = f; } /** * Returns the value for a boolen argument. The argument's type must @@ -289,17 +289,17 @@ public: */ const Val* AsVal() const { assert(type == VAL); return arg.val; } - /** - * Returns the value for a Bro wrapped value argument. The argument's type must - * match accordingly. - */ - const ValWrapper* AsValWrapper() const { assert(type == VAL_WRAPPER); return arg.wrapper; } + /** + * Returns the value for a Bro wrapped value argument. The argument's type must + * match accordingly. + */ + const ValWrapper* AsValWrapper() const { assert(type == VAL_WRAPPER); return arg.wrapper; } - /** - * Returns the value for a Bro frame argument. The argument's type must - * match accordingly. - */ - const Frame* AsFrame() const { assert(type == FRAME); return arg.frame; } + /** + * Returns the value for a Bro frame argument. The argument's type must + * match accordingly. + */ + const Frame* AsFrame() const { assert(type == FRAME); return arg.frame; } /** * Returns the value for a list of Bro values argument. The argument's type must @@ -332,10 +332,10 @@ private: double double_; const Event* event; const Func* func; - const Frame* frame; + const Frame* frame; int int_; const Val* val; - const ValWrapper* wrapper; + const ValWrapper* wrapper; const val_list* vals; const void* voidp; } arg; @@ -564,7 +564,7 @@ protected: * actually has code to execute for it. By calling this method, the * plugin tells Bro to raise the event even if there's no correspondong * handler; it will then go into HookQueueEvent() just as any other. - * + * * @param handler The event handler being interested in. */ void RequestEvent(EventHandlerPtr handler); @@ -621,11 +621,11 @@ protected: * counting. * * @return If the plugin handled the call, a ValWrapper with the - * processed flag set to true, and a value set on the object with - * a+1 reference count containing the result value to pass back to the - * interpreter. If the plugin did not handle the call, it may either - * return NULL *or* return a ValWrapper with the processed flag set to - * 'false'. + * processed flag set to true, and a value set on the object with + * a+1 reference count containing the result value to pass back to the + * interpreter. If the plugin did not handle the call, it may either + * return NULL *or* return a ValWrapper with the processed flag set to + * 'false'. */ virtual ValWrapper* HookCallFunction(const Func* func, Frame *parent, val_list* args); From 616ed2257226cbbf2bf5d82d1625ed2f800a4510 Mon Sep 17 00:00:00 2001 From: Gilbert Clark Date: Mon, 24 Nov 2014 16:30:12 -0500 Subject: [PATCH 034/299] Small fixes --- src/plugin/Manager.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/plugin/Manager.cc b/src/plugin/Manager.cc index eb8681c1cd..1445ed9a63 100644 --- a/src/plugin/Manager.cc +++ b/src/plugin/Manager.cc @@ -331,7 +331,7 @@ void Manager::InitPreScript() assert(! init); for ( plugin_list::iterator i = Manager::ActivePluginsInternal()->begin(); - i != Manager::ActivePluginsInternal()->end(); i++ ) + i != Manager::ActivePluginsInternal()->end(); i++ ) { Plugin* plugin = *i; plugin->DoConfigure(); @@ -346,7 +346,7 @@ void Manager::InitBifs() bif_init_func_map* bifs = BifFilesInternal(); for ( plugin_list::iterator i = Manager::ActivePluginsInternal()->begin(); - i != Manager::ActivePluginsInternal()->end(); i++ ) + i != Manager::ActivePluginsInternal()->end(); i++ ) { bif_init_func_map::const_iterator b = bifs->find((*i)->Name()); @@ -363,7 +363,7 @@ void Manager::InitPostScript() assert(init); for ( plugin_list::iterator i = Manager::ActivePluginsInternal()->begin(); - i != Manager::ActivePluginsInternal()->end(); i++ ) + i != Manager::ActivePluginsInternal()->end(); i++ ) (*i)->InitPostScript(); } @@ -372,7 +372,7 @@ void Manager::FinishPlugins() assert(init); for ( plugin_list::iterator i = Manager::ActivePluginsInternal()->begin(); - i != Manager::ActivePluginsInternal()->end(); i++ ) + i != Manager::ActivePluginsInternal()->end(); i++ ) (*i)->Done(); Manager::ActivePluginsInternal()->clear(); @@ -505,13 +505,13 @@ void Manager::DisableHook(HookType hook, Plugin* plugin) void Manager::RequestEvent(EventHandlerPtr handler, Plugin* plugin) { DBG_LOG(DBG_PLUGINS, "Plugin %s requested event %s", - plugin->Name().c_str(), handler->Name()); + plugin->Name().c_str(), handler->Name()); handler->SetGenerateAlways(); } void Manager::RequestBroObjDtor(BroObj* obj, Plugin* plugin) { - obj->NotifyPluginsOnDtor(); + obj->NotifyPluginsOnDtor(); } int Manager::HookLoadFile(const string& file) @@ -566,14 +566,14 @@ std::pair Manager::HookCallFunction(const Func* func, Frame* parent, if ( HavePluginForHook(META_HOOK_PRE) ) { args.push_back(HookArgument(func)); - args.push_back(HookArgument(parent)); + args.push_back(HookArgument(parent)); args.push_back(HookArgument(vargs)); MetaHookPre(HOOK_CALL_FUNCTION, args); } hook_list* l = hooks[HOOK_CALL_FUNCTION]; - std::pair v; + std::pair v = std::pair(NULL, false); if ( l ) for ( hook_list::iterator i = l->begin(); i != l->end(); ++i ) @@ -583,10 +583,10 @@ std::pair Manager::HookCallFunction(const Func* func, Frame* parent, v = p->HookCallFunction(func, parent, vargs); if ( v.second ) - { + { break; } - } + } if ( HavePluginForHook(META_HOOK_POST) ) MetaHookPost(HOOK_CALL_FUNCTION, args, HookArgument(v)); @@ -674,7 +674,7 @@ void Manager::HookBroObjDtor(void* obj) const { HookArgumentList args; - if ( HavePluginForHook(META_HOOK_PRE) ) + if ( HavePluginForHook(META_HOOK_PRE) ) { args.push_back(obj); MetaHookPre(HOOK_BRO_OBJ_DTOR, args); From cda7c93704ff3ea7201cd17af19759efdc9dd491 Mon Sep 17 00:00:00 2001 From: Gilbert Clark Date: Mon, 24 Nov 2014 16:35:26 -0500 Subject: [PATCH 035/299] More small fixes --- src/plugin/Manager.cc | 8 ++++---- src/plugin/Plugin.cc | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugin/Manager.cc b/src/plugin/Manager.cc index 1445ed9a63..c63c47dfd0 100644 --- a/src/plugin/Manager.cc +++ b/src/plugin/Manager.cc @@ -583,11 +583,11 @@ std::pair Manager::HookCallFunction(const Func* func, Frame* parent, v = p->HookCallFunction(func, parent, vargs); if ( v.second ) - { + { break; - } - } - + } + } + if ( HavePluginForHook(META_HOOK_POST) ) MetaHookPost(HOOK_CALL_FUNCTION, args, HookArgument(v)); diff --git a/src/plugin/Plugin.cc b/src/plugin/Plugin.cc index a54829d883..0c2d2dba40 100644 --- a/src/plugin/Plugin.cc +++ b/src/plugin/Plugin.cc @@ -293,7 +293,7 @@ int Plugin::HookLoadFile(const std::string& file, const std::string& ext) std::pair Plugin::HookCallFunction(const Func* func, Frame *parent, val_list* args) { - std::pair result(NULL, false); + std::pair result(NULL, false); return result; } From cd21b7f1303adcafcfe5949ba394cc8c407ec429 Mon Sep 17 00:00:00 2001 From: Johanna Amann Date: Tue, 25 Nov 2014 11:18:07 -0800 Subject: [PATCH 036/299] Fix x509 analyzer to correctly return ecdsa as the key_type for ecdsa certs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Returned dsa so far. Bug found by MichaÅ‚ PurzyÅ„ski --- src/file_analysis/analyzer/x509/X509.cc | 2 +- .../scripts.base.protocols.ssl.ecdsa/ssl.log | 10 ++++++++++ .../scripts.base.protocols.ssl.ecdsa/x509.log | 11 +++++++++++ testing/btest/Traces/tls/ecdsa-cert.pcap | Bin 0 -> 4238 bytes .../btest/scripts/base/protocols/ssl/ecdsa.test | 3 +++ 5 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 testing/btest/Baseline/scripts.base.protocols.ssl.ecdsa/ssl.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.ssl.ecdsa/x509.log create mode 100644 testing/btest/Traces/tls/ecdsa-cert.pcap create mode 100644 testing/btest/scripts/base/protocols/ssl/ecdsa.test diff --git a/src/file_analysis/analyzer/x509/X509.cc b/src/file_analysis/analyzer/x509/X509.cc index 78b6bdd645..69f399c9dc 100644 --- a/src/file_analysis/analyzer/x509/X509.cc +++ b/src/file_analysis/analyzer/x509/X509.cc @@ -147,7 +147,7 @@ RecordVal* file_analysis::X509::ParseCertificate(X509Val* cert_val) #ifndef OPENSSL_NO_EC else if ( pkey->type == EVP_PKEY_EC ) { - pX509Cert->Assign(8, new StringVal("dsa")); + pX509Cert->Assign(8, new StringVal("ecdsa")); pX509Cert->Assign(11, KeyCurve(pkey)); } #endif diff --git a/testing/btest/Baseline/scripts.base.protocols.ssl.ecdsa/ssl.log b/testing/btest/Baseline/scripts.base.protocols.ssl.ecdsa/ssl.log new file mode 100644 index 0000000000..66b30f7b7f --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.ssl.ecdsa/ssl.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path ssl +#open 2014-11-25-19-14-54 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version cipher curve server_name resumed last_alert next_protocol established cert_chain_fuids client_cert_chain_fuids subject issuer client_subject client_issuer +#types time string addr port addr port string string string string bool string string bool vector[string] vector[string] string string string string +1416942644.508914 CXWv6p3arKYeMETxOg 192.168.4.149 49422 23.92.19.75 443 TLSv12 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 secp384r1 - F - - T Fi6J8q3lDJpbQWAnvi,FDXMnz1NjsQeaBxCU (empty) CN=pantz.org,OU=PositiveSSL,OU=Domain Control Validated CN=COMODO ECC Domain Validation Secure Server CA,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB - - +#close 2014-11-25-19-14-54 diff --git a/testing/btest/Baseline/scripts.base.protocols.ssl.ecdsa/x509.log b/testing/btest/Baseline/scripts.base.protocols.ssl.ecdsa/x509.log new file mode 100644 index 0000000000..efed125f6a --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.ssl.ecdsa/x509.log @@ -0,0 +1,11 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path x509 +#open 2014-11-25-19-15-51 +#fields ts id certificate.version certificate.serial certificate.subject certificate.issuer certificate.not_valid_before certificate.not_valid_after certificate.key_alg certificate.sig_alg certificate.key_type certificate.key_length certificate.exponent certificate.curve san.dns san.uri san.email san.ip basic_constraints.ca basic_constraints.path_len +#types time string count string string string time time string string string count string string vector[string] vector[string] vector[string] vector[addr] bool count +1416942644.593119 Fi6J8q3lDJpbQWAnvi 3 F0AFBBF558BF4D1B71FED9CB33793EE4 CN=pantz.org,OU=PositiveSSL,OU=Domain Control Validated CN=COMODO ECC Domain Validation Secure Server CA,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB 1415404800.000000 1573171199.000000 id-ecPublicKey ecdsa-with-SHA256 ecdsa 384 - secp384r1 pantz.org,www.pantz.org - - - F - +1416942644.593119 FDXMnz1NjsQeaBxCU 3 510601E63B50673C55EE4E19DA304CA8 CN=COMODO ECC Domain Validation Secure Server CA,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB CN=COMODO ECC Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB 1394668800.000000 1868054399.000000 id-ecPublicKey ecdsa-with-SHA384 ecdsa 256 - prime256v1 - - - - T 0 +#close 2014-11-25-19-15-52 diff --git a/testing/btest/Traces/tls/ecdsa-cert.pcap b/testing/btest/Traces/tls/ecdsa-cert.pcap new file mode 100644 index 0000000000000000000000000000000000000000..8ad7fb70afe96dfe933dd1c5da618a344de1927c GIT binary patch literal 4238 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 literal 0 HcmV?d00001 diff --git a/testing/btest/scripts/base/protocols/ssl/ecdsa.test b/testing/btest/scripts/base/protocols/ssl/ecdsa.test new file mode 100644 index 0000000000..a2db7c2cb5 --- /dev/null +++ b/testing/btest/scripts/base/protocols/ssl/ecdsa.test @@ -0,0 +1,3 @@ +# @TEST-EXEC: bro -C -r $TRACES/tls/ecdsa-cert.pcap %INPUT +# @TEST-EXEC: btest-diff ssl.log +# @TEST-EXEC: btest-diff x509.log From 1e2ba6ebfb22a3abf1d31452794f76b81cb86c19 Mon Sep 17 00:00:00 2001 From: Johanna Amann Date: Tue, 25 Nov 2014 13:11:06 -0800 Subject: [PATCH 037/299] make sslv2 protocol tests more strict - in its current state they triggered on http traffic over port 443 sometimes. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sorry, no test because that specific traffic is a tad hard to get. Found by MichaÅ‚ PurzyÅ„ski. --- src/analyzer/protocol/ssl/ssl-protocol.pac | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyzer/protocol/ssl/ssl-protocol.pac b/src/analyzer/protocol/ssl/ssl-protocol.pac index 8e7f7a221d..6011f2b837 100644 --- a/src/analyzer/protocol/ssl/ssl-protocol.pac +++ b/src/analyzer/protocol/ssl/ssl-protocol.pac @@ -36,7 +36,7 @@ type SSLRecord(is_orig: bool) = record { } &length = length+5, &byteorder=bigendian, &let { version : int = - $context.connection.determine_ssl_record_layer(head0, head1, head2, head3, head4); + $context.connection.determine_ssl_record_layer(head0, head1, head2, head3, head4, is_orig); content_type : int = case version of { SSLv20 -> head2+300; @@ -748,7 +748,7 @@ refine connection SSL_Conn += { %} function determine_ssl_record_layer(head0 : uint8, head1 : uint8, - head2 : uint8, head3: uint8, head4: uint8) : int + head2 : uint8, head3: uint8, head4: uint8, is_orig: bool) : int %{ // re-check record layer version to be sure that we still are synchronized with // the data stream @@ -768,7 +768,7 @@ refine connection SSL_Conn += { if ( head0 & 0x80 ) { - if ( head2 == 0x01 ) // SSLv2 client hello. + if ( head2 == 0x01 && is_orig ) // SSLv2 client hello. { uint16 version = (head3 << 8) | head4; if ( version != SSLv20 && version != SSLv30 && version != TLSv10 && @@ -782,7 +782,7 @@ refine connection SSL_Conn += { return SSLv20; } - else if ( head2 == 0x04 ) // SSLv2 server hello. This connection will continue using SSLv2. + else if ( head2 == 0x04 && head4 < 2 && !is_orig ) // SSLv2 server hello. This connection will continue using SSLv2. { record_layer_version_ = SSLv20; return SSLv20; From 529668670a56b9aeb54916fb0cd4fb0ac11a50c0 Mon Sep 17 00:00:00 2001 From: Johanna Amann Date: Tue, 25 Nov 2014 14:57:10 -0800 Subject: [PATCH 038/299] make the SSL analyzer skip further processing once encountering situations which are very probably non-recoverable. Current behavior could lead to us jumping in in the middle of an old 443 stream and interpreting some data as ssl before failing again. --- src/analyzer/protocol/ssl/ssl-analyzer.pac | 19 +++++++++++++++---- src/analyzer/protocol/ssl/ssl-protocol.pac | 5 +++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/analyzer/protocol/ssl/ssl-analyzer.pac b/src/analyzer/protocol/ssl/ssl-analyzer.pac index 64d5d78df6..e74236b923 100644 --- a/src/analyzer/protocol/ssl/ssl-analyzer.pac +++ b/src/analyzer/protocol/ssl/ssl-analyzer.pac @@ -112,7 +112,10 @@ refine connection SSL_Conn += { cipher_suites24 : uint24[]) : bool %{ if ( ! version_ok(version) ) + { bro_analyzer()->ProtocolViolation(fmt("unsupported client SSL version 0x%04x", version)); + bro_analyzer()->SetSkip(true); + } else bro_analyzer()->ProtocolConfirmation(); @@ -152,7 +155,10 @@ refine connection SSL_Conn += { comp_method : uint8) : bool %{ if ( ! version_ok(version) ) + { bro_analyzer()->ProtocolViolation(fmt("unsupported server SSL version 0x%04x", version)); + bro_analyzer()->SetSkip(true); + } if ( ssl_server_hello ) { @@ -202,6 +208,7 @@ refine connection SSL_Conn += { // This should be impossible due to the binpac parser // and protocol description bro_analyzer()->ProtocolViolation(fmt("Impossible extension length: %lu", length)); + bro_analyzer()->SetSkip(true); return true; } @@ -392,7 +399,11 @@ refine connection SSL_Conn += { function proc_check_v2_server_hello_version(version: uint16) : bool %{ if ( version != SSLv20 ) + { bro_analyzer()->ProtocolViolation(fmt("Invalid version in SSL server hello. Version: %d", version)); + bro_analyzer()->SetSkip(true); + return false; + } return true; %} @@ -479,13 +490,13 @@ refine typeattr ServerHello += &let { }; refine typeattr V2ServerHello += &let { - proc : bool = $context.connection.proc_server_hello(rec, server_version, 0, - conn_id_data, 0, 0, ciphers, 0); - check_v2 : bool = $context.connection.proc_check_v2_server_hello_version(server_version); + proc : bool = $context.connection.proc_server_hello(rec, server_version, 0, + conn_id_data, 0, 0, ciphers, 0) &if(check_v2 == true); + cert : bool = $context.connection.proc_v2_certificate(rec, cert_data) - &requires(proc); + &requires(proc) &if(check_v2 == true); }; refine typeattr Certificate += &let { diff --git a/src/analyzer/protocol/ssl/ssl-protocol.pac b/src/analyzer/protocol/ssl/ssl-protocol.pac index 8e7f7a221d..9038039ef9 100644 --- a/src/analyzer/protocol/ssl/ssl-protocol.pac +++ b/src/analyzer/protocol/ssl/ssl-protocol.pac @@ -759,6 +759,7 @@ refine connection SSL_Conn += { version != TLSv11 && version != TLSv12 ) { bro_analyzer()->ProtocolViolation(fmt("Invalid version late in TLS connection. Packet reported version: %d", version)); + bro_analyzer()->SetSkip(true); return UNKNOWN_VERSION; } } @@ -775,6 +776,7 @@ refine connection SSL_Conn += { version != TLSv11 && version != TLSv12 ) { bro_analyzer()->ProtocolViolation(fmt("Invalid version in SSL client hello. Version: %d", version)); + bro_analyzer()->SetSkip(true); return UNKNOWN_VERSION; } @@ -791,6 +793,7 @@ refine connection SSL_Conn += { else // this is not SSL or TLS. { bro_analyzer()->ProtocolViolation(fmt("Invalid headers in SSL connection. Head1: %d, head2: %d, head3: %d", head1, head2, head3)); + bro_analyzer()->SetSkip(true); return UNKNOWN_VERSION; } } @@ -800,6 +803,7 @@ refine connection SSL_Conn += { version != TLSv11 && version != TLSv12 ) { bro_analyzer()->ProtocolViolation(fmt("Invalid version in TLS connection. Version: %d", version)); + bro_analyzer()->SetSkip(true); return UNKNOWN_VERSION; } @@ -810,6 +814,7 @@ refine connection SSL_Conn += { } bro_analyzer()->ProtocolViolation(fmt("Invalid type in TLS connection. Version: %d, Type: %d", version, head0)); + bro_analyzer()->SetSkip(true); return UNKNOWN_VERSION; %} From d87476b4030fc3f4816a84213c318340bb854f65 Mon Sep 17 00:00:00 2001 From: Johanna Amann Date: Tue, 25 Nov 2014 15:01:12 -0800 Subject: [PATCH 039/299] and just to be safe - also require the &if check in binpac --- src/analyzer/protocol/ssl/ssl-analyzer.pac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyzer/protocol/ssl/ssl-analyzer.pac b/src/analyzer/protocol/ssl/ssl-analyzer.pac index e74236b923..2433886d14 100644 --- a/src/analyzer/protocol/ssl/ssl-analyzer.pac +++ b/src/analyzer/protocol/ssl/ssl-analyzer.pac @@ -493,10 +493,10 @@ refine typeattr V2ServerHello += &let { check_v2 : bool = $context.connection.proc_check_v2_server_hello_version(server_version); proc : bool = $context.connection.proc_server_hello(rec, server_version, 0, - conn_id_data, 0, 0, ciphers, 0) &if(check_v2 == true); + conn_id_data, 0, 0, ciphers, 0) &requires(check_v2) &if(check_v2 == true); cert : bool = $context.connection.proc_v2_certificate(rec, cert_data) - &requires(proc) &if(check_v2 == true); + &requires(proc) &requires(check_v2) &if(check_v2 == true); }; refine typeattr Certificate += &let { From fc71572aad9d12209ae608ef3e9c0bfff2a9fc4c Mon Sep 17 00:00:00 2001 From: Hilko Bengen Date: Wed, 26 Nov 2014 20:55:22 +0100 Subject: [PATCH 040/299] BIFScanner: Make filename->symbol transformation more robust When trying to build bro from a path that contained a plus sign, an invalid symbol name for the #ifdef guard was generated. --- src/builtin-func.l | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/builtin-func.l b/src/builtin-func.l index 076d2bf99b..68f627fd53 100644 --- a/src/builtin-func.l +++ b/src/builtin-func.l @@ -1,4 +1,5 @@ %{ +#include #include #include #include "bif_arg.h" @@ -223,7 +224,7 @@ void init_alternative_mode() for ( char* p = guard; *p; p++ ) { - if ( strchr("/.-", *p) ) + if ( !isalnum (*p) ) *p = '_'; } From 5836feb64dd5c7b3a2dae7f59dbfaf8e1eae1407 Mon Sep 17 00:00:00 2001 From: Johanna Amann Date: Thu, 27 Nov 2014 10:00:48 -0800 Subject: [PATCH 041/299] Do not change global event parameters in exec.bro Addresses BIT-1294 --- scripts/base/utils/exec.bro | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/base/utils/exec.bro b/scripts/base/utils/exec.bro index f38bf82978..37ec35cb2c 100644 --- a/scripts/base/utils/exec.bro +++ b/scripts/base/utils/exec.bro @@ -96,8 +96,9 @@ event Exec::file_line(description: Input::EventDescription, tpe: Input::Event, s result$files[track_file][|result$files[track_file]|] = s; } -event Input::end_of_data(name: string, source:string) +event Input::end_of_data(orig_name: string, source:string) { + local name = orig_name; local parts = split1(name, /_/); name = parts[1]; From ebb2240e97547ae03a88746d1f2031fff5e472b2 Mon Sep 17 00:00:00 2001 From: Michal Purzynski Date: Thu, 27 Nov 2014 19:41:20 +0100 Subject: [PATCH 042/299] Update windows-version-detection.bro --- scripts/policy/frameworks/software/windows-version-detection.bro | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/policy/frameworks/software/windows-version-detection.bro b/scripts/policy/frameworks/software/windows-version-detection.bro index bf6580077a..0162dddf75 100644 --- a/scripts/policy/frameworks/software/windows-version-detection.bro +++ b/scripts/policy/frameworks/software/windows-version-detection.bro @@ -47,6 +47,7 @@ export { ["Microsoft-CryptoAPI/6.1"] = [$name="Windows", $version=[$major=6, $minor=1, $addl="7 or Server 2008 R2"]], ["Microsoft-CryptoAPI/6.2"] = [$name="Windows", $version=[$major=6, $minor=2, $addl="8 or Server 2012"]], ["Microsoft-CryptoAPI/6.3"] = [$name="Windows", $version=[$major=6, $minor=3, $addl="8.1 or Server 2012 R2"]], + ["Microsoft-CryptoAPI/6.4"] = [$name="Windows", $version=[$major=6, $minor=4, $addl="10 Technical Preview"]], } &redef; } From b0383c22d693a1bf51a426fa79a869353c0ff076 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Mon, 1 Dec 2014 10:21:41 -0600 Subject: [PATCH 043/299] Delete prebuilt python bytecode files from git. BIT-1291 #close --- doc/ext/bro_lexer/__init__.pyc | Bin 147 -> 0 bytes doc/ext/bro_lexer/bro.pyc | Bin 2585 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/ext/bro_lexer/__init__.pyc delete mode 100644 doc/ext/bro_lexer/bro.pyc diff --git a/doc/ext/bro_lexer/__init__.pyc b/doc/ext/bro_lexer/__init__.pyc deleted file mode 100644 index 1b03eaf1c181333ebdea52d9acb9162159cb5795..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 147 zcmZSn%*%Bma*kgz0~9avGdZ6kgf6;E+HFgb)aqgqvi?NeUg9ri5@Q3``lyB+RspWi+z1z64p)Xje&0 zHvL22qQCkWeXc%0`<){v(5VxzzCE}7_Ut(;m;auc_)`b(0x|rQ@qB`>dW|8(za=6e z`j*(SM870xF2rXce%%=p;i%ZQ#}KXe z4?Z?6peHbF$&NgDBM)SzPhr{`bh0E*l^!S4IEeC4+7=npP-UBES;7t{Y;QpI3}5xC zB!m=161+eXw24!hI1(5kazt@N6qI(xMGx9BAy0|jifF@Sobd$XQ$n6*P&oz@$KbS( zldPH~usg+git!o7XM{W}mso-+EYC59e1^ffV=yD+sF(}Si#d7@wHFHEOd$+s zhtG>A26KhMrT;1BhjHb&2rolBj@Vc5JWs1ioW}wt7sT!?v$)D(u;}i^p`P#@>#k9x z-AjzG3k2Am7vT-jM&1@_8ZI%uDdc6gxpfR~6WG1N!aI}-O7C4x%Yu*#l!{?yY??yb zxhg_OJQ&8Ua@-m(d6k`)2~!jtLjmeT(7~ZD!SjS*;k?4m^X#+w6=r|zgJN-r0#lS5 z;=#@}v9)clp`tX^De8@%tt?Tam{(T_O#wC5>fibG_S5=HuUS(zB)08_Rd;364Qq}8 zt}(7Uj@^(invHs`$*N6tiQJ#ocl`P%1cBxYt}5!24^LLAn-dIg0=cn5aSJ?%lB;TJ zgq|)0e*oBi7#ctFv%YC%(*I}zUr7@rvC=f^UYtu4%1CNF5+fsMWt{ho+>^03@*qtz zZQ4m@+Ibx4UJ{#L+jOF& z*z9+Ek#yXa`ZVO189yGFR+2=Zl*5@vnSoH=LgY5=4F$zlW_wsNmfEDg#w<2WFq1)& zg+@WHGzfHz%3EZmeTtxa{bI<>o(y!7(eiYY*ze6t!haFI{4q}UV{)JxEsD@Nynh5!zC~>=9p~`jk*1PbJKz+Vb*H+%Ug!v`3OSQE zW;V}A>}-U9HvEA8kC|{LM7gMaXP4+QuEz z1Rv9=)i)L&KWc6`YLZk~yzlFvOZhoN{)b1l<2Kypt(E2Pn`(yLJ+vhnvgz(u9eaJ< z;i!#5=rxZ4HEvZ?A7)9SIjc9*Bvq@BxUSuBUBA_iGrhOu-ffx#xY1QXUWfOVzXi3m z`oreoH_P{r9L{g!gX=E2F|HDBb(e)MxVf=jZJwB{l8JpCi}35v^J2d*J&#|fo>x={ zro@Xh{3`XM9xeo>=}@W9NTJxo>msw1Q?DH*D1KVn>$21Hq~AxqGJcoIG)n@u+(zT! zCZPr1!zVGOE$$jbx=JJ+!q<$bd@Y}rK!Vdvr zv(4?zq2HkWGC$(bKcG|iGJb`HsWM(NY3lLUxHUTPCMo}_U?|j?@?EUjGLt2)X zd7%OyWNAg6e3UU?;(~7SV&MB1*ZoR5*rNS%@r4AeF{@%#O7t8nmCKXXB+VfyLsl+T zFfW(MW)iq!&034rJhZc=X=@s~N$c!T1HaO&HGL9cx(p5fy|rmynyNG%bm%D5N<=@E vwjdFl)Ga!$|61@qIB}lmN0H|-@h@9q^wqvNO^=GQc}xezx&>>ZR4M%n2{>iM From 127a61597ed4cc136cdc61a38cc2bf8f9a3c9e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Benencia?= Date: Mon, 1 Dec 2014 10:43:41 -0600 Subject: [PATCH 044/299] Add/invoke "distclean" for testing directories. BIT-1292 #close --- Makefile | 1 + testing/Makefile | 4 ++++ testing/btest/Makefile | 7 +++++++ 3 files changed, 12 insertions(+) diff --git a/Makefile b/Makefile index 49d9a6173c..9feaecd656 100644 --- a/Makefile +++ b/Makefile @@ -48,6 +48,7 @@ bindist: distclean: rm -rf $(BUILD) + $(MAKE) -C testing $@ test: @( cd testing && make ) diff --git a/testing/Makefile b/testing/Makefile index d56ee4e0e1..122262f865 100644 --- a/testing/Makefile +++ b/testing/Makefile @@ -5,6 +5,10 @@ all: make-verbose coverage brief: make-brief coverage +distclean: + @rm -f coverage.log + $(MAKE) -C btest $@ + make-verbose: @for repo in $(DIRS); do (cd $$repo && make -s ); done diff --git a/testing/btest/Makefile b/testing/btest/Makefile index 261c7262b0..56bf8f0a7e 100644 --- a/testing/btest/Makefile +++ b/testing/btest/Makefile @@ -21,6 +21,13 @@ cleanup: @rm -f $(DIAG) @rm -f .tmp/script-coverage* +distclean: cleanup + @rm -rf .btest.failed.dat \ + coverage.log \ + diag.log \ + .tmp/ + + # Updates the three coverage tests that usually need tweaking when # scripts get added/removed. update-coverage-tests: From fe9e7d015e9d0437d0540bda4662af4dbb1a44c3 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Mon, 1 Dec 2014 12:10:27 -0600 Subject: [PATCH 045/299] Update submodules/changes/version. --- CHANGES | 15 +++++++++++++++ VERSION | 2 +- aux/broctl | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 9d4fae48b1..ce92872eb3 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,19 @@ +2.3-326 | 2014-12-01 12:10:27 -0600 + + * BIFScanner: fix invalid characters in generated preprocessor macros. + (Hilko Bengen) + + * BIT-1294: fix exec.bro from mutating Input::end_of_data event + parameters. (Johanna Amann) + + * Add/invoke "distclean" for testing directories. (Raúl Benencia) + + * Delete prebuilt python bytecode files from git. (Jon Siwek) + + * Add Windows detection based on CryptoAPI HTTP traffic as a software + framework policy script. (Vlad Grigorescu) + 2.3-316 | 2014-11-25 17:35:06 -0800 * Make the SSL analyzer skip further processing once encountering diff --git a/VERSION b/VERSION index d6becd7005..aefaa2de2d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3-316 +2.3-326 diff --git a/aux/broctl b/aux/broctl index 39e865dec9..9bd12e1cbb 160000 --- a/aux/broctl +++ b/aux/broctl @@ -1 +1 @@ -Subproject commit 39e865dec9611b9b53b609cbc8df519cebae0a1e +Subproject commit 9bd12e1cbb249ce35e65beddc2c77fcde3f58a87 From e4c9c58b9e16332a83129c5426586dabacf92b71 Mon Sep 17 00:00:00 2001 From: Daniel Thayer Date: Mon, 1 Dec 2014 20:58:37 -0600 Subject: [PATCH 046/299] Add man page for Bro --- CMakeLists.txt | 6 ++ man/CMakeLists.txt | 5 ++ man/bro.8 | 160 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 man/CMakeLists.txt create mode 100644 man/bro.8 diff --git a/CMakeLists.txt b/CMakeLists.txt index 22d63a89d5..7a287ef5b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,11 @@ if (NOT BRO_SCRIPT_INSTALL_PATH) set(BRO_SCRIPT_INSTALL_PATH ${BRO_ROOT_DIR}/share/bro) endif () +if (NOT BRO_MAN_INSTALL_PATH) + # set the default Bro man page installation path (user did not specify one) + set(BRO_MAN_INSTALL_PATH ${BRO_ROOT_DIR}/share/man) +endif () + # sanitize the Bro script install directory into an absolute path # (CMake is confused by ~ as a representation of home directory) get_filename_component(BRO_SCRIPT_INSTALL_PATH ${BRO_SCRIPT_INSTALL_PATH} @@ -175,6 +180,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_subdirectory(src) add_subdirectory(scripts) add_subdirectory(doc) +add_subdirectory(man) include(CheckOptionalBuildSources) diff --git a/man/CMakeLists.txt b/man/CMakeLists.txt new file mode 100644 index 0000000000..a369ee32b3 --- /dev/null +++ b/man/CMakeLists.txt @@ -0,0 +1,5 @@ + +install(DIRECTORY . DESTINATION ${BRO_MAN_INSTALL_PATH}/man8 FILES_MATCHING + PATTERN "*.8" +) + diff --git a/man/bro.8 b/man/bro.8 new file mode 100644 index 0000000000..ca8a8823fc --- /dev/null +++ b/man/bro.8 @@ -0,0 +1,160 @@ +.TH BRO "8" "November 2014" "bro" "System Administration Utilities" +.SH NAME +bro \- passive network traffic analyzer +.SH SYNOPSIS +.B bro +\/\fP [options] [file ...] +.SH DESCRIPTION +Bro is primarily a security monitor that inspects all traffic on a link in +depth for signs of suspicious activity. More generally, however, Bro +supports a wide range of traffic analysis tasks even outside of the +security domain, including performance measurements and helping with +trouble-shooting. + +Bro comes with built-in functionality for a range of analysis and detection +tasks, including detecting malware by interfacing to external registries, +reporting vulnerable versions of software seen on the network, identifying +popular web applications, detecting SSH brute-forcing, validating SSL +certificate chains, among others. +.SH OPTIONS +.TP +.B +policy file, or read stdin +.TP +\fB\-a\fR,\ \-\-parse\-only +exit immediately after parsing scripts +.TP +\fB\-b\fR,\ \-\-bare\-mode +don't load scripts from the base/ directory +.TP +\fB\-d\fR,\ \-\-debug\-policy +activate policy file debugging +.TP +\fB\-e\fR,\ \-\-exec +augment loaded policies by given code +.TP +\fB\-f\fR,\ \-\-filter +tcpdump filter +.TP +\fB\-g\fR,\ \-\-dump\-config +dump current config into .state dir +.TP +\fB\-h\fR,\ \-\-help|\-? +command line help +.TP +\fB\-i\fR,\ \-\-iface +read from given interface +.TP +\fB\-p\fR,\ \-\-prefix +add given prefix to policy file resolution +.TP +\fB\-r\fR,\ \-\-readfile +read from given tcpdump file +.TP +\fB\-y\fR,\ \-\-flowfile [=] +read from given flow file +.TP +\fB\-Y\fR,\ \-\-netflow :[=] +read flow from socket +.TP +\fB\-s\fR,\ \-\-rulefile +read rules from given file +.TP +\fB\-t\fR,\ \-\-tracefile +activate execution tracing +.TP +\fB\-w\fR,\ \-\-writefile +write to given tcpdump file +.TP +\fB\-v\fR,\ \-\-version +print version and exit +.TP +\fB\-x\fR,\ \-\-print\-state +print contents of state file +.TP +\fB\-z\fR,\ \-\-analyze +run the specified policy file analysis +.TP +\fB\-C\fR,\ \-\-no\-checksums +ignore checksums +.TP +\fB\-D\fR,\ \-\-dfa\-size +DFA state cache size +.TP +\fB\-F\fR,\ \-\-force\-dns +force DNS +.TP +\fB\-I\fR,\ \-\-print\-id +print out given ID +.TP +\fB\-K\fR,\ \-\-md5\-hashkey +set key for MD5\-keyed hashing +.TP +\fB\-L\fR,\ \-\-rule\-benchmark +benchmark for rules +.TP +\fB\-N\fR,\ \-\-print\-plugins +print available plugins and exit (\fB\-NN\fR for verbose) +.TP +\fB\-O\fR,\ \-\-optimize +optimize policy script +.TP +\fB\-P\fR,\ \-\-prime\-dns +prime DNS +.TP +\fB\-R\fR,\ \-\-replay +replay events +.TP +\fB\-S\fR,\ \-\-debug\-rules +enable rule debugging +.TP +\fB\-T\fR,\ \-\-re\-level +set 'RE_level' for rules +.TP +\fB\-U\fR,\ \-\-status\-file +Record process status in file +.TP +\fB\-W\fR,\ \-\-watchdog +activate watchdog timer +.TP +\fB\-X\fR,\ \-\-broxygen +generate documentation based on config file +.TP +\fB\-\-pseudo\-realtime[=\fR] +enable pseudo\-realtime for performance evaluation (default 1) +.TP +\fB\-\-load\-seeds\fR +load seeds from given file +.TP +\fB\-\-save\-seeds\fR +save seeds to given file +.SH ENVIRONMENTS +.TP +.B BROPATH +file search path (.:/usr/share/bro:/usr/share/bro/policy:/usr/share/bro/site) +.TP +.B BRO_PREFIXES +prefix list () +.TP +.B BRO_DNS_FAKE +disable DNS lookups (off) +.TP +.B BRO_SEED_FILE +file to load seeds from (not set) +.TP +.B BRO_LOG_SUFFIX +ASCII log file extension (.log) +.TP +.B BRO_PROFILER_FILE +Output file for script execution statistics (not set) +.TP +.B BRO_DISABLE_BROXYGEN +Disable Broxygen documentation support (not set) +.IP +Supported log formats: Ascii,SQLite +.SH AUTHOR +.B bro +was written by The Bro Project . +.PP +This manual page was written by Raúl Benencia +for the Debian project (but may be used by others). From cc7286b628be8556773810d705c6bbfd5b44e6d7 Mon Sep 17 00:00:00 2001 From: Daniel Thayer Date: Mon, 1 Dec 2014 22:43:17 -0600 Subject: [PATCH 047/299] Fix some "make doc" warnings and update some doc tests --- doc/logs/index.rst | 2 +- src/bro.bif | 3 +-- ...lude-doc_scripting_data_struct_vector_declaration_bro.btest | 2 +- .../include-doc_scripting_data_type_pattern_01_bro.btest | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/logs/index.rst b/doc/logs/index.rst index a8fb951c80..b6b0b084ec 100644 --- a/doc/logs/index.rst +++ b/doc/logs/index.rst @@ -113,7 +113,7 @@ default, including: As you can see, some log files are specific to a particular protocol, while others aggregate information across different types of activity. For a complete list of log files and a description of its purpose, -see :doc:`List of Log Files <../script-reference/list-of-log-files>`. +see :doc:`Log Files <../script-reference/log-files>`. .. _bro-cut: diff --git a/src/bro.bif b/src/bro.bif index 83ad49689b..4e685eb84a 100644 --- a/src/bro.bif +++ b/src/bro.bif @@ -2163,8 +2163,7 @@ function counts_to_addr%(v: index_vec%): addr ## ## e: The :bro:type:`enum` to convert. ## -## Returns: The :bro:type:`enum` as the :bro:type:`int` value that it -## to corresponds. +## Returns: The :bro:type:`int` value that corresponds to the :bro:type:`enum`. function enum_to_int%(e: any%): int %{ if ( e->Type()->Tag() != TYPE_ENUM ) diff --git a/testing/btest/doc/sphinx/include-doc_scripting_data_struct_vector_declaration_bro.btest b/testing/btest/doc/sphinx/include-doc_scripting_data_struct_vector_declaration_bro.btest index 575b5a18b4..4f1260e4ed 100644 --- a/testing/btest/doc/sphinx/include-doc_scripting_data_struct_vector_declaration_bro.btest +++ b/testing/btest/doc/sphinx/include-doc_scripting_data_struct_vector_declaration_bro.btest @@ -14,6 +14,6 @@ event bro_init() print fmt("contents of v1: %s", v1); print fmt("length of v1: %d", |v1|); - print fmt("contents of v1: %s", v2); + print fmt("contents of v2: %s", v2); print fmt("length of v2: %d", |v2|); } diff --git a/testing/btest/doc/sphinx/include-doc_scripting_data_type_pattern_01_bro.btest b/testing/btest/doc/sphinx/include-doc_scripting_data_type_pattern_01_bro.btest index f108efb795..cca008116e 100644 --- a/testing/btest/doc/sphinx/include-doc_scripting_data_type_pattern_01_bro.btest +++ b/testing/btest/doc/sphinx/include-doc_scripting_data_type_pattern_01_bro.btest @@ -4,7 +4,7 @@ data_type_pattern_01.bro event bro_init() { - local test_string = "The quick brown fox jumped over the lazy dog."; + local test_string = "The quick brown fox jumps over the lazy dog."; local test_pattern = /quick|lazy/; if ( test_pattern in test_string ) From 3f590859bb6a8b22863884096e6d30411279bb3d Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 2 Dec 2014 10:12:48 -0600 Subject: [PATCH 048/299] Fix compound assignment to require proper L-value. Allows for catching more invalid assignments at parse-time instead of aborting at runtime after realizing an assignment won't work. Addresses BIT-1295. --- src/Expr.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Expr.cc b/src/Expr.cc index c7ea906865..671f9b2d41 100644 --- a/src/Expr.cc +++ b/src/Expr.cc @@ -1438,7 +1438,7 @@ bool AddExpr::DoUnserialize(UnserialInfo* info) } AddToExpr::AddToExpr(Expr* arg_op1, Expr* arg_op2) -: BinaryExpr(EXPR_ADD_TO, arg_op1, arg_op2) +: BinaryExpr(EXPR_ADD_TO, arg_op1->MakeLvalue(), arg_op2) { if ( IsError() ) return; @@ -1562,7 +1562,7 @@ bool SubExpr::DoUnserialize(UnserialInfo* info) } RemoveFromExpr::RemoveFromExpr(Expr* arg_op1, Expr* arg_op2) -: BinaryExpr(EXPR_REMOVE_FROM, arg_op1, arg_op2) +: BinaryExpr(EXPR_REMOVE_FROM, arg_op1->MakeLvalue(), arg_op2) { if ( IsError() ) return; From cdbe459f206e42a784fd8df2617e9eabe231c77c Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 2 Dec 2014 12:30:46 -0600 Subject: [PATCH 049/299] Make using local IDs in @if directives an error. Addresses BIT-1296. --- src/scan.l | 40 +++++++++++++++++- .../btest/Baseline/language.at-if-invalid/out | 4 ++ testing/btest/language/at-if-invalid.bro | 41 +++++++++++++++++++ testing/btest/language/at-if.bro | 12 +++++- 4 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 testing/btest/Baseline/language.at-if-invalid/out create mode 100644 testing/btest/language/at-if-invalid.bro diff --git a/src/scan.l b/src/scan.l index 9ab830c4f1..a804004d66 100644 --- a/src/scan.l +++ b/src/scan.l @@ -26,6 +26,7 @@ #include "Reporter.h" #include "RE.h" #include "Net.h" +#include "Traverse.h" #include "analyzer/Analyzer.h" #include "broxygen/Manager.h" @@ -615,11 +616,48 @@ void end_RE() BEGIN(INITIAL); } +struct LocalNameFinder : public TraversalCallback { + + LocalNameFinder() + {} + + virtual TraversalCode PreExpr(const Expr* expr) + { + if ( expr->Tag() != EXPR_NAME ) + return TC_CONTINUE; + + const NameExpr* name_expr = static_cast(expr); + + if ( name_expr->Id()->IsGlobal() ) + return TC_CONTINUE; + + local_names.push_back(name_expr); + return TC_CONTINUE; + } + + std::vector local_names; +}; + void do_atif(Expr* expr) { ++current_depth; - Val* val = expr->Eval(0); + LocalNameFinder cb; + expr->Traverse(&cb); + Val* val = 0; + + if ( cb.local_names.empty() ) + val = expr->Eval(0); + else + for ( size_t i = 0; i < cb.local_names.size(); ++i ) + cb.local_names[i]->Error("referencing a local name in @if"); + + if ( ! val ) + { + expr->Error("invalid expression in @if"); + return; + } + if ( ! val->AsBool() ) { if_stack.append(current_depth); diff --git a/testing/btest/Baseline/language.at-if-invalid/out b/testing/btest/Baseline/language.at-if-invalid/out new file mode 100644 index 0000000000..63b93a3cf8 --- /dev/null +++ b/testing/btest/Baseline/language.at-if-invalid/out @@ -0,0 +1,4 @@ +error in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.at-if-invalid/at-if-invalid.bro, line 28: referencing a local name in @if (xyz) +error in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.at-if-invalid/at-if-invalid.bro, line 28: invalid expression in @if (F && foo(xyz)) +error in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.at-if-invalid/at-if-invalid.bro, line 36: referencing a local name in @if (local_true_condition) +error in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.at-if-invalid/at-if-invalid.bro, line 36: invalid expression in @if (T && TRUE_CONDITION && local_true_condition) diff --git a/testing/btest/language/at-if-invalid.bro b/testing/btest/language/at-if-invalid.bro new file mode 100644 index 0000000000..1be2b94304 --- /dev/null +++ b/testing/btest/language/at-if-invalid.bro @@ -0,0 +1,41 @@ +# @TEST-EXEC-FAIL: bro -b %INPUT >out 2>&1 +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff out + +function foo(c: count): bool + { return c == 42 ? T : F; } + +global TRUE_CONDITION = T; + +event bro_init() + { + local xyz = 0; + local local_true_condition = T; + + @if ( F ) + xyz += 1; + @endif + + @if ( foo(0) ) + xyz += 1; + @endif + + @if ( T && foo(42) ) + xyz += 2; + @endif + + xyz = 0; + + @if ( F && foo(xyz) ) + xyz += 1; + @else + xyz += 2; + @endif + + xyz = 0; + + @if ( T && TRUE_CONDITION && local_true_condition ) + xyz += 1; + @else + xyz += 2; + @endif + } diff --git a/testing/btest/language/at-if.bro b/testing/btest/language/at-if.bro index 0a3e87adfa..dddf9a22a5 100644 --- a/testing/btest/language/at-if.bro +++ b/testing/btest/language/at-if.bro @@ -6,6 +6,10 @@ function test_case(msg: string, expect: bool) print fmt("%s (%s)", msg, expect ? "PASS" : "FAIL"); } +function foo(c: count): bool + { return c == 42 ? T : F; } + +global TRUE_CONDITION = T; event bro_init() { @@ -17,7 +21,11 @@ event bro_init() xyz += 1; @endif - @if ( T ) + @if ( foo(0) ) + xyz += 1; + @endif + + @if ( T && foo(42) ) xyz += 2; @endif @@ -37,7 +45,7 @@ event bro_init() xyz = 0; - @if ( T ) + @if ( T && TRUE_CONDITION ) xyz += 1; @else xyz += 2; From 665e6b00f11efb80f4128846256dc005c309e9fd Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Thu, 4 Dec 2014 09:05:38 -0800 Subject: [PATCH 050/299] Updating doc baselines. --- .../output | 2 +- .../output | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/btest/Baseline/doc.sphinx.include-doc_scripting_data_struct_vector_declaration_bro/output b/testing/btest/Baseline/doc.sphinx.include-doc_scripting_data_struct_vector_declaration_bro/output index 575b5a18b4..4f1260e4ed 100644 --- a/testing/btest/Baseline/doc.sphinx.include-doc_scripting_data_struct_vector_declaration_bro/output +++ b/testing/btest/Baseline/doc.sphinx.include-doc_scripting_data_struct_vector_declaration_bro/output @@ -14,6 +14,6 @@ event bro_init() print fmt("contents of v1: %s", v1); print fmt("length of v1: %d", |v1|); - print fmt("contents of v1: %s", v2); + print fmt("contents of v2: %s", v2); print fmt("length of v2: %d", |v2|); } diff --git a/testing/btest/Baseline/doc.sphinx.include-doc_scripting_data_type_pattern_01_bro/output b/testing/btest/Baseline/doc.sphinx.include-doc_scripting_data_type_pattern_01_bro/output index f108efb795..cca008116e 100644 --- a/testing/btest/Baseline/doc.sphinx.include-doc_scripting_data_type_pattern_01_bro/output +++ b/testing/btest/Baseline/doc.sphinx.include-doc_scripting_data_type_pattern_01_bro/output @@ -4,7 +4,7 @@ data_type_pattern_01.bro event bro_init() { - local test_string = "The quick brown fox jumped over the lazy dog."; + local test_string = "The quick brown fox jumps over the lazy dog."; local test_pattern = /quick|lazy/; if ( test_pattern in test_string ) From 0a7d96dec34c0891905148ad1bf90386fd34d997 Mon Sep 17 00:00:00 2001 From: Daniel Thayer Date: Thu, 4 Dec 2014 23:46:03 -0600 Subject: [PATCH 051/299] Improve man page for Bro --- man/bro.8 | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/man/bro.8 b/man/bro.8 index ca8a8823fc..612adb8107 100644 --- a/man/bro.8 +++ b/man/bro.8 @@ -3,7 +3,7 @@ bro \- passive network traffic analyzer .SH SYNOPSIS .B bro -\/\fP [options] [file ...] +\/\fP [\fIoptions\fR] [\fIfile\fR ...] .SH DESCRIPTION Bro is primarily a security monitor that inspects all traffic on a link in depth for signs of suspicious activity. More generally, however, Bro @@ -102,6 +102,9 @@ optimize policy script \fB\-P\fR,\ \-\-prime\-dns prime DNS .TP +\fB\-Q\fR,\ \-\-time +print execution time summary to stderr +.TP \fB\-R\fR,\ \-\-replay replay events .TP @@ -128,33 +131,34 @@ load seeds from given file .TP \fB\-\-save\-seeds\fR save seeds to given file -.SH ENVIRONMENTS +.SH ENVIRONMENT .TP .B BROPATH -file search path (.:/usr/share/bro:/usr/share/bro/policy:/usr/share/bro/site) +file search path +.TP +.B BRO_PLUGIN_PATH +plugin search path +.TP +.B BRO_PLUGIN_ACTIVATE +plugins to always activate .TP .B BRO_PREFIXES -prefix list () +prefix list .TP .B BRO_DNS_FAKE -disable DNS lookups (off) +disable DNS lookups .TP .B BRO_SEED_FILE -file to load seeds from (not set) +file to load seeds from .TP .B BRO_LOG_SUFFIX -ASCII log file extension (.log) +ASCII log file extension .TP .B BRO_PROFILER_FILE -Output file for script execution statistics (not set) +Output file for script execution statistics .TP .B BRO_DISABLE_BROXYGEN -Disable Broxygen documentation support (not set) -.IP -Supported log formats: Ascii,SQLite +Disable Broxygen documentation support .SH AUTHOR .B bro was written by The Bro Project . -.PP -This manual page was written by Raúl Benencia -for the Debian project (but may be used by others). From 69724c5e1f87a69eaaceb573c2326937de3a5d57 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Mon, 8 Dec 2014 13:57:08 -0800 Subject: [PATCH 052/299] Updating submodule(s). [nomail] --- aux/broctl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aux/broctl b/aux/broctl index d0efcdb11d..18da923f58 160000 --- a/aux/broctl +++ b/aux/broctl @@ -1 +1 @@ -Subproject commit d0efcdb11d41505b8b1b5b145aa11391dc1d9033 +Subproject commit 18da923f5898c037a8f2e71bc2b8200b8f8c739d From c211a2c91aa006e0a93fe94b34cdf7d8ea4f45e1 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 10 Dec 2014 15:12:38 -0600 Subject: [PATCH 053/299] Fix PIA packet replay to deliver copy of IP header This prevented one from writing a packet-wise analyzer that needs access to IP headers and can be attached to a connection via signature match. None of the analyzers currently shipping are affected. And maybe it's unlikely there will be many that ever would be, but it's awkward for the API to omit IP headers in this special case (i.e. packets buffer for use with DPD signature matching). Addresses BIT-1298 --- src/IP.cc | 71 ++++++++++++++++++++++++++++++++ src/IP.h | 31 ++++++++++++++ src/analyzer/protocol/pia/PIA.cc | 13 +++--- src/analyzer/protocol/pia/PIA.h | 5 ++- 4 files changed, 113 insertions(+), 7 deletions(-) diff --git a/src/IP.cc b/src/IP.cc index 46702f2546..f1a0a80553 100644 --- a/src/IP.cc +++ b/src/IP.cc @@ -4,6 +4,8 @@ #include "Type.h" #include "Val.h" #include "Var.h" +#include +#include static RecordType* ip4_hdr_type = 0; static RecordType* ip6_hdr_type = 0; @@ -636,3 +638,72 @@ VectorVal* IPv6_Hdr_Chain::BuildVal() const return rval; } + +IP_Hdr::IP_Hdr(const IP_Hdr& other) + { + del = true; + + if ( other.ip4 ) + { + char* new_hdr = new char[other.HdrLen()]; + memcpy(new_hdr, other.ip4, other.HdrLen()); + ip4 = (const struct ip*)new_hdr; + ip6 = 0; + ip6_hdrs = 0; + } + else + { + ip4 = 0; + char* new_hdr = new char[other.HdrLen()]; + memcpy(new_hdr, other.ip6, other.HdrLen()); + ip6 = (const struct ip6_hdr*)new_hdr; + ip6_hdrs = other.ip6_hdrs->Copy(ip6); + } + } + +IP_Hdr& IP_Hdr::operator=(IP_Hdr other) + { + swap(*this, other); + return *this; + } + +IPv6_Hdr_Chain* IPv6_Hdr_Chain::Copy(const ip6_hdr* new_hdr) const + { + IPv6_Hdr_Chain* rval = new IPv6_Hdr_Chain; + rval->length = length; + +#ifdef ENABLE_MOBILE_IPV6 + if ( homeAddr ) + rval->homeAddr = new IPAddr(*homeAddr); +#endif + + if ( finalDst ) + rval->finalDst = new IPAddr(*finalDst); + + if ( chain.empty() ) + { + reporter->InternalWarning("empty IPv6 header chain"); + delete rval; + return 0; + } + + const u_char* new_data = (const u_char*)new_hdr; + const u_char* old_data = chain[0]->Data(); + + for ( size_t i = 0; i < chain.size(); ++i ) + { + int off = chain[i]->Data() - old_data; + rval->chain.push_back(new IPv6_Hdr(chain[i]->Type(), new_data + off)); + } + + return rval; + } + +void swap(IP_Hdr& a, IP_Hdr& b) + { + using std::swap; + swap(a.ip4, b.ip4); + swap(a.ip6, b.ip6); + swap(a.del, b.del); + swap(a.ip6_hdrs, b.ip6_hdrs); + } diff --git a/src/IP.h b/src/IP.h index fb936df1bc..c1e80c4aed 100644 --- a/src/IP.h +++ b/src/IP.h @@ -157,6 +157,12 @@ public: delete finalDst; } + /** + * @return a copy of the header chain, but with pointers to individual + * IPv6 headers now pointing within \a new_hdr. + */ + IPv6_Hdr_Chain* Copy(const struct ip6_hdr* new_hdr) const; + /** * Returns the number of headers in the chain. */ @@ -264,6 +270,14 @@ protected: // point to a fragment friend class FragReassembler; + IPv6_Hdr_Chain() : + length(0), +#ifdef ENABLE_MOBILE_IPV6 + homeAddr(0), +#endif + finalDst(0) + {} + /** * Initializes the header chain from an IPv6 header structure, and replaces * the first next protocol pointer field that points to a fragment header. @@ -353,6 +367,20 @@ public: { } + /** + * Copy constructor. The internal buffer of \a other which contains + * the header data must not be truncated. Also not that if that buffer + * points to a full packet payload, only the IP header portion is copied. + */ + IP_Hdr(const IP_Hdr& other); + + /** + * Copy assignment. The internal buffer of \a other which contains + * the header data must not be truncated. Also not that if that buffer + * points to a full packet payload, only the IP header portion is copied. + */ + IP_Hdr& operator=(IP_Hdr other); + /** * Destructor. */ @@ -553,7 +581,10 @@ public: */ RecordVal* BuildPktHdrVal() const; + friend void swap(IP_Hdr& a, IP_Hdr& b); + private: + const struct ip* ip4; const struct ip6_hdr* ip6; bool del; diff --git a/src/analyzer/protocol/pia/PIA.cc b/src/analyzer/protocol/pia/PIA.cc index 24d4565ce1..21c83367e4 100644 --- a/src/analyzer/protocol/pia/PIA.cc +++ b/src/analyzer/protocol/pia/PIA.cc @@ -22,6 +22,7 @@ void PIA::ClearBuffer(Buffer* buffer) for ( DataBlock* b = buffer->head; b; b = next ) { next = b->next; + delete b->ip; delete [] b->data; delete b; } @@ -31,7 +32,7 @@ void PIA::ClearBuffer(Buffer* buffer) } void PIA::AddToBuffer(Buffer* buffer, uint64 seq, int len, const u_char* data, - bool is_orig) + bool is_orig, const IP_Hdr* ip) { u_char* tmp = 0; @@ -42,6 +43,7 @@ void PIA::AddToBuffer(Buffer* buffer, uint64 seq, int len, const u_char* data, } DataBlock* b = new DataBlock; + b->ip = ip ? new IP_Hdr(*ip) : 0; b->data = tmp; b->is_orig = is_orig; b->len = len; @@ -59,9 +61,10 @@ void PIA::AddToBuffer(Buffer* buffer, uint64 seq, int len, const u_char* data, buffer->size += len; } -void PIA::AddToBuffer(Buffer* buffer, int len, const u_char* data, bool is_orig) +void PIA::AddToBuffer(Buffer* buffer, int len, const u_char* data, bool is_orig, + const IP_Hdr* ip) { - AddToBuffer(buffer, -1, len, data, is_orig); + AddToBuffer(buffer, -1, len, data, is_orig, ip); } void PIA::ReplayPacketBuffer(analyzer::Analyzer* analyzer) @@ -69,7 +72,7 @@ void PIA::ReplayPacketBuffer(analyzer::Analyzer* analyzer) DBG_LOG(DBG_ANALYZER, "PIA replaying %d total packet bytes", pkt_buffer.size); for ( DataBlock* b = pkt_buffer.head; b; b = b->next ) - analyzer->DeliverPacket(b->len, b->data, b->is_orig, -1, 0, 0); + analyzer->DeliverPacket(b->len, b->data, b->is_orig, -1, b->ip, 0); } void PIA::PIA_Done() @@ -96,7 +99,7 @@ void PIA::PIA_DeliverPacket(int len, const u_char* data, bool is_orig, uint64 se if ( (pkt_buffer.state == BUFFERING || new_state == BUFFERING) && len > 0 ) { - AddToBuffer(&pkt_buffer, seq, len, data, is_orig); + AddToBuffer(&pkt_buffer, seq, len, data, is_orig, ip); if ( pkt_buffer.size > dpd_buffer_size ) new_state = dpd_match_only_beginning ? SKIPPING : MATCHING_ONLY; diff --git a/src/analyzer/protocol/pia/PIA.h b/src/analyzer/protocol/pia/PIA.h index 1dc7dbb2bc..d6e07f68c3 100644 --- a/src/analyzer/protocol/pia/PIA.h +++ b/src/analyzer/protocol/pia/PIA.h @@ -49,6 +49,7 @@ protected: // Buffers one chunk of data. Used both for packet payload (incl. // sequence numbers for TCP) and chunks of a reassembled stream. struct DataBlock { + IP_Hdr* ip; const u_char* data; bool is_orig; int len; @@ -66,9 +67,9 @@ protected: }; void AddToBuffer(Buffer* buffer, uint64 seq, int len, - const u_char* data, bool is_orig); + const u_char* data, bool is_orig, const IP_Hdr* ip = 0); void AddToBuffer(Buffer* buffer, int len, - const u_char* data, bool is_orig); + const u_char* data, bool is_orig, const IP_Hdr* ip = 0); void ClearBuffer(Buffer* buffer); DataBlock* CurrentPacket() { return ¤t_packet; } From 15cc08c940319f6cb0633985cdb1aba69a5ae03c Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Fri, 12 Dec 2014 10:50:05 -0800 Subject: [PATCH 054/299] Updating submodule(s). [nomail] --- aux/broctl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aux/broctl b/aux/broctl index 18da923f58..90f9ca0ffa 160000 --- a/aux/broctl +++ b/aux/broctl @@ -1 +1 @@ -Subproject commit 18da923f5898c037a8f2e71bc2b8200b8f8c739d +Subproject commit 90f9ca0ffa2306f0d1d2ac208cdbb7787199f890 From d31b556b85e2d7c328d34d92afd198f20e9724dc Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Fri, 12 Dec 2014 14:14:24 -0600 Subject: [PATCH 055/299] Change IP_Hdr copy ctor/assign to explicit method Addresses BIT-1298 --- src/IP.cc | 41 +++++++------------------------- src/IP.h | 17 ++++--------- src/analyzer/protocol/pia/PIA.cc | 2 +- 3 files changed, 14 insertions(+), 46 deletions(-) diff --git a/src/IP.cc b/src/IP.cc index f1a0a80553..783c08da39 100644 --- a/src/IP.cc +++ b/src/IP.cc @@ -4,8 +4,6 @@ #include "Type.h" #include "Val.h" #include "Var.h" -#include -#include static RecordType* ip4_hdr_type = 0; static RecordType* ip6_hdr_type = 0; @@ -639,32 +637,20 @@ VectorVal* IPv6_Hdr_Chain::BuildVal() const return rval; } -IP_Hdr::IP_Hdr(const IP_Hdr& other) +IP_Hdr* IP_Hdr::Copy() const { - del = true; + char* new_hdr = new char[HdrLen()]; - if ( other.ip4 ) + if ( ip4 ) { - char* new_hdr = new char[other.HdrLen()]; - memcpy(new_hdr, other.ip4, other.HdrLen()); - ip4 = (const struct ip*)new_hdr; - ip6 = 0; - ip6_hdrs = 0; + memcpy(new_hdr, ip4, HdrLen()); + return new IP_Hdr((const struct ip*) new_hdr, true); } - else - { - ip4 = 0; - char* new_hdr = new char[other.HdrLen()]; - memcpy(new_hdr, other.ip6, other.HdrLen()); - ip6 = (const struct ip6_hdr*)new_hdr; - ip6_hdrs = other.ip6_hdrs->Copy(ip6); - } - } -IP_Hdr& IP_Hdr::operator=(IP_Hdr other) - { - swap(*this, other); - return *this; + memcpy(new_hdr, ip6, HdrLen()); + const struct ip6_hdr* new_ip6 = (const struct ip6_hdr*)new_hdr; + IPv6_Hdr_Chain* new_ip6_hdrs = ip6_hdrs->Copy(new_ip6); + return new IP_Hdr(new_ip6, true, 0, new_ip6_hdrs); } IPv6_Hdr_Chain* IPv6_Hdr_Chain::Copy(const ip6_hdr* new_hdr) const @@ -698,12 +684,3 @@ IPv6_Hdr_Chain* IPv6_Hdr_Chain::Copy(const ip6_hdr* new_hdr) const return rval; } - -void swap(IP_Hdr& a, IP_Hdr& b) - { - using std::swap; - swap(a.ip4, b.ip4); - swap(a.ip6, b.ip6); - swap(a.del, b.del); - swap(a.ip6_hdrs, b.ip6_hdrs); - } diff --git a/src/IP.h b/src/IP.h index c1e80c4aed..b91c9130e4 100644 --- a/src/IP.h +++ b/src/IP.h @@ -368,18 +368,11 @@ public: } /** - * Copy constructor. The internal buffer of \a other which contains - * the header data must not be truncated. Also not that if that buffer - * points to a full packet payload, only the IP header portion is copied. + * Copy a header. The internal buffer which contains the header data + * must not be truncated. Also note that if that buffer points to a full + * packet payload, only the IP header portion is copied. */ - IP_Hdr(const IP_Hdr& other); - - /** - * Copy assignment. The internal buffer of \a other which contains - * the header data must not be truncated. Also not that if that buffer - * points to a full packet payload, only the IP header portion is copied. - */ - IP_Hdr& operator=(IP_Hdr other); + IP_Hdr* Copy() const; /** * Destructor. @@ -581,8 +574,6 @@ public: */ RecordVal* BuildPktHdrVal() const; - friend void swap(IP_Hdr& a, IP_Hdr& b); - private: const struct ip* ip4; diff --git a/src/analyzer/protocol/pia/PIA.cc b/src/analyzer/protocol/pia/PIA.cc index 21c83367e4..69a0c5d312 100644 --- a/src/analyzer/protocol/pia/PIA.cc +++ b/src/analyzer/protocol/pia/PIA.cc @@ -43,7 +43,7 @@ void PIA::AddToBuffer(Buffer* buffer, uint64 seq, int len, const u_char* data, } DataBlock* b = new DataBlock; - b->ip = ip ? new IP_Hdr(*ip) : 0; + b->ip = ip ? ip->Copy() : 0; b->data = tmp; b->is_orig = is_orig; b->len = len; From cbbe7b52dc163b6a535545767cf36c596b2a9000 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 16 Dec 2014 14:05:15 -0600 Subject: [PATCH 056/299] Review/fix/change file reassembly functionality. - Re-arrange how some fa_file fields (e.g. source, connection info, mime type) get updated/set for consistency. - Add more robust mechanisms for flushing the reassembly buffer. The goal being to report all gaps and deliveries to file analyzers regardless of the state of the reassembly buffer at the time it has to be flushed. --- scripts/base/frameworks/files/main.bro | 3 +- scripts/base/init-bare.bro | 10 +- src/analyzer/protocol/tcp/TCP_Reassembler.h | 3 - src/file_analysis/Analyzer.h | 19 +- src/file_analysis/AnalyzerSet.cc | 8 +- src/file_analysis/AnalyzerSet.h | 6 +- src/file_analysis/File.cc | 232 ++++++++++-------- src/file_analysis/File.h | 29 +-- src/file_analysis/FileReassembler.cc | 64 ++++- src/file_analysis/FileReassembler.h | 32 ++- src/file_analysis/Manager.cc | 26 +- src/file_analysis/Manager.h | 4 +- src/file_analysis/analyzer/extract/Extract.cc | 1 - testing/btest/Baseline/core.bits_per_uid/128 | 4 +- testing/btest/Baseline/core.bits_per_uid/256 | 4 +- testing/btest/Baseline/core.bits_per_uid/32 | 4 +- testing/btest/Baseline/core.bits_per_uid/64 | 4 +- testing/btest/Baseline/core.bits_per_uid/96 | 4 +- testing/btest/Baseline/plugins.hooks/output | 87 ++++--- .../files.log | 6 +- .../bro..stdout | 5 +- .../out | 6 +- .../b.out | 5 +- .../files.log | 6 +- .../all-events-no-args.log | 8 +- .../all-events.log | 28 +-- 26 files changed, 370 insertions(+), 238 deletions(-) diff --git a/scripts/base/frameworks/files/main.bro b/scripts/base/frameworks/files/main.bro index f1f381a141..e335d4be9d 100644 --- a/scripts/base/frameworks/files/main.bro +++ b/scripts/base/frameworks/files/main.bro @@ -133,7 +133,7 @@ export { ## each file. const enable_reassembler = T &redef; - ## The default allow per-file reassembly buffer size. + ## The default per-file reassembly buffer size. const reassembly_buffer_size = 1048576 &redef; ## Allows the file reassembler to be used if it's necessary because the @@ -490,7 +490,6 @@ event file_mime_type(f: fa_file, mime_type: string) &priority=10 f$info$mime_type = mime_type; - if ( analyze_by_mime_type_automatically && mime_type in mime_type_to_analyzers ) { diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index b112d3ea0f..4a1bcfbe72 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -353,8 +353,9 @@ type connection: record { ## gives up and discards any internal state related to the file. const default_file_timeout_interval: interval = 2 mins &redef; -## Default amount of bytes that file analysis will buffer to provide -## data back in time to attached analyzers +## Default amount of bytes that file analysis will buffer in order to use +## for mime type matching. File analyzers attached at the time of mime type +## matching or later, will receive a copy of this buffer. const default_file_bof_buffer_size: count = 4096 &redef; ## A file that Bro is analyzing. This is Bro's type for describing the basic @@ -395,8 +396,9 @@ type fa_file: record { missing_bytes: count &default=0; ## The number of bytes in the file stream that were not delivered to - ## stream file analyzers. This could be overlapping bytes or - ## bytes that couldn't be reassembled. + ## stream file analyzers. Generally, this consists of bytes that + ## couldn't be reassembled, either because reassembly simply isn't + ## enabled, or due to size limitations of the reassembly buffer. overflow_bytes: count &default=0; ## The amount of time between receiving new data for this file that diff --git a/src/analyzer/protocol/tcp/TCP_Reassembler.h b/src/analyzer/protocol/tcp/TCP_Reassembler.h index 5d8badcef1..c2ed0175ca 100644 --- a/src/analyzer/protocol/tcp/TCP_Reassembler.h +++ b/src/analyzer/protocol/tcp/TCP_Reassembler.h @@ -11,9 +11,6 @@ namespace analyzer { namespace tcp { class TCP_Analyzer; -const int STOP_ON_GAP = 1; -const int PUNT_ON_PARTIAL = 1; - class TCP_Reassembler : public Reassembler { public: enum Type { diff --git a/src/file_analysis/Analyzer.h b/src/file_analysis/Analyzer.h index 619a72c81d..dcb8434a6f 100644 --- a/src/file_analysis/Analyzer.h +++ b/src/file_analysis/Analyzer.h @@ -111,6 +111,18 @@ public: */ void SetAnalyzerTag(const file_analysis::Tag& tag); + /** + * @return true if the analyzer has ever seen a stream-wise delivery. + */ + bool GotStreamDelivery() const + { return got_stream_delivery; } + + /** + * Flag the analyzer as having seen a stream-wise delivery. + */ + void SetGotStreamDelivery() + { got_stream_delivery = true; } + protected: /** @@ -123,7 +135,8 @@ protected: Analyzer(file_analysis::Tag arg_tag, RecordVal* arg_args, File* arg_file) : tag(arg_tag), args(arg_args->Ref()->AsRecordVal()), - file(arg_file) + file(arg_file), + got_stream_delivery(false) { id = ++id_counter; } @@ -140,7 +153,8 @@ protected: Analyzer(RecordVal* arg_args, File* arg_file) : tag(), args(arg_args->Ref()->AsRecordVal()), - file(arg_file) + file(arg_file), + got_stream_delivery(false) { id = ++id_counter; } @@ -151,6 +165,7 @@ private: file_analysis::Tag tag; /**< The particular type of the analyzer instance. */ RecordVal* args; /**< \c AnalyzerArgs val gives tunable analyzer params. */ File* file; /**< The file to which the analyzer is attached. */ + bool got_stream_delivery; static ID id_counter; }; diff --git a/src/file_analysis/AnalyzerSet.cc b/src/file_analysis/AnalyzerSet.cc index 2657a5b709..8425e5d3c7 100644 --- a/src/file_analysis/AnalyzerSet.cc +++ b/src/file_analysis/AnalyzerSet.cc @@ -72,20 +72,20 @@ bool AnalyzerSet::Add(file_analysis::Tag tag, RecordVal* args) return true; } -bool AnalyzerSet::QueueAdd(file_analysis::Tag tag, RecordVal* args, file_analysis::Analyzer* a) +Analyzer* AnalyzerSet::QueueAdd(file_analysis::Tag tag, RecordVal* args) { HashKey* key = GetKey(tag, args); - a = InstantiateAnalyzer(tag, args); + file_analysis::Analyzer* a = InstantiateAnalyzer(tag, args); if ( ! a ) { delete key; - return false; + return 0; } mod_queue.push(new AddMod(a, key)); - return true; + return a; } bool AnalyzerSet::AddMod::Perform(AnalyzerSet* set) diff --git a/src/file_analysis/AnalyzerSet.h b/src/file_analysis/AnalyzerSet.h index 839425980c..642792f776 100644 --- a/src/file_analysis/AnalyzerSet.h +++ b/src/file_analysis/AnalyzerSet.h @@ -57,10 +57,10 @@ public: * Queue the attachment of an analyzer to #file. * @param tag the analyzer tag of the file analyzer to add. * @param args an \c AnalyzerArgs value which specifies an analyzer. - * @param a an analyzer pointer to return the instantiated analyzer to the caller. - * @return true if analyzer was able to be instantiated, else false. + * @return if successful, a pointer to a newly instantiated analyzer else + * a null pointer. The caller does *not* take ownership of the memory. */ - bool QueueAdd(file_analysis::Tag tag, RecordVal* args, file_analysis::Analyzer* a); + file_analysis::Analyzer* QueueAdd(file_analysis::Tag tag, RecordVal* args); /** * Remove an analyzer from #file immediately. diff --git a/src/file_analysis/File.cc b/src/file_analysis/File.cc index e4e9b6dc9d..d893d7a088 100644 --- a/src/file_analysis/File.cc +++ b/src/file_analysis/File.cc @@ -74,8 +74,8 @@ void File::StaticInit() bof_buffer_idx = Idx("bof_buffer"); } -File::File(const string& file_id, Connection* conn, analyzer::Tag tag, - bool is_orig) +File::File(const string& file_id, const string& source_name, Connection* conn, + analyzer::Tag tag, bool is_orig) : id(file_id), val(0), file_reassembler(0), stream_offset(0), reassembly_max_buffer(0), did_mime_type(false), reassembly_enabled(false), postpone_timeout(false), done(false), @@ -87,12 +87,12 @@ File::File(const string& file_id, Connection* conn, analyzer::Tag tag, val = new RecordVal(fa_file_type); val->Assign(id_idx, new StringVal(file_id.c_str())); + SetSource(source_name); if ( conn ) { - // add source, connection, is_orig fields - SetSource(analyzer_mgr->GetComponentName(tag)); val->Assign(is_orig_idx, new Val(is_orig, TYPE_BOOL)); + UpdateConnectionFields(conn, is_orig); } UpdateLastActivityTime(); @@ -102,11 +102,7 @@ File::~File() { DBG_LOG(DBG_FILE_ANALYSIS, "[%s] Destroying File object", id.c_str()); Unref(val); - - if ( file_reassembler ) - { - delete file_reassembler; - } + delete file_reassembler; } void File::UpdateLastActivityTime() @@ -119,10 +115,10 @@ double File::GetLastActivityTime() const return val->Lookup(last_active_idx)->AsTime(); } -void File::UpdateConnectionFields(Connection* conn, bool is_orig) +bool File::UpdateConnectionFields(Connection* conn, bool is_orig) { if ( ! conn ) - return; + return false; Val* conns = val->Lookup(conns_idx); @@ -133,23 +129,28 @@ void File::UpdateConnectionFields(Connection* conn, bool is_orig) } Val* idx = get_conn_id_val(conn); - if ( ! conns->AsTableVal()->Lookup(idx) ) + + if ( conns->AsTableVal()->Lookup(idx) ) { - Val* conn_val = conn->BuildConnVal(); - conns->AsTableVal()->Assign(idx, conn_val); - - if ( FileEventAvailable(file_over_new_connection) ) - { - val_list* vl = new val_list(); - vl->append(val->Ref()); - vl->append(conn_val->Ref()); - vl->append(new Val(is_orig, TYPE_BOOL)); - - FileEvent(file_over_new_connection, vl); - } + Unref(idx); + return false; } + conns->AsTableVal()->Assign(idx, conn->BuildConnVal()); Unref(idx); + return true; + } + +void File::RaiseFileOverNewConnection(Connection* conn, bool is_orig) + { + if ( conn && FileEventAvailable(file_over_new_connection) ) + { + val_list* vl = new val_list(); + vl->append(val->Ref()); + vl->append(conn->BuildConnVal()->Ref()); + vl->append(new Val(is_orig, TYPE_BOOL)); + FileEvent(file_over_new_connection, vl); + } } uint64 File::LookupFieldDefaultCount(int idx) const @@ -252,20 +253,7 @@ bool File::AddAnalyzer(file_analysis::Tag tag, RecordVal* args) if ( done ) return false; - file_analysis::Analyzer *a = 0; - bool success = analyzers.QueueAdd(tag, args, a); - if ( success && a ) - { - // Catch up this analyzer with the BOF buffer - for ( size_t i = 0; i < bof_buffer.chunks.size(); ++i ) - { - if ( ! a->DeliverStream(bof_buffer.chunks[i]->Bytes(), bof_buffer.chunks[i]->Len()) ) - { - analyzers.QueueRemove(a->Tag(), a->Args()); - } - } - } - return success; + return analyzers.QueueAdd(tag, args) != 0; } bool File::RemoveAnalyzer(file_analysis::Tag tag, RecordVal* args) @@ -284,11 +272,8 @@ void File::EnableReassembly() void File::DisableReassembly() { reassembly_enabled = false; - if ( file_reassembler ) - { - delete file_reassembler; - file_reassembler = NULL; - } + delete file_reassembler; + file_reassembler = 0; } void File::SetReassemblyBuffer(uint64 max) @@ -298,11 +283,23 @@ void File::SetReassemblyBuffer(uint64 max) bool File::DetectMIME() { - RuleMatcher::MIME_Matches matches; + did_mime_type = true; - BroString *bs = concatenate(bof_buffer.chunks); - const u_char* data = bs->Bytes(); - uint64 len = bs->Len(); + Val* bof_buffer_val = val->Lookup(bof_buffer_idx); + + if ( ! bof_buffer_val ) + { + if ( bof_buffer.size == 0 ) + return false; + + BroString* bs = concatenate(bof_buffer.chunks); + bof_buffer_val = new StringVal(bs); + val->Assign(bof_buffer_idx, bof_buffer_val); + } + + RuleMatcher::MIME_Matches matches; + const u_char* data = bof_buffer_val->AsString()->Bytes(); + uint64 len = bof_buffer_val->AsString()->Len(); len = min(len, LookupFieldDefaultCount(bof_buffer_size_idx)); file_mgr->DetectMIME(data, len, &matches); @@ -338,45 +335,70 @@ bool File::BufferBOF(const u_char* data, uint64 len) bof_buffer.chunks.push_back(new BroString(data, len, 0)); bof_buffer.size += len; - if ( bof_buffer.size >= desired_size ) + if ( bof_buffer.size < desired_size ) + return true; + + bof_buffer.full = true; + + if ( bof_buffer.size > 0 ) { - bof_buffer.full = true; - } - - return true; - } - -void File::DeliverStream(const u_char* data, uint64 len) - { - // Buffer enough data for the BOF buffer - BufferBOF(data, len); - - // TODO: mime matching size needs defined. - if ( ! did_mime_type && - bof_buffer.size >= 1024 && - LookupFieldDefaultCount(missing_bytes_idx) == 0 ) - { - did_mime_type = true; - DetectMIME(); - - // TODO: this needs to be done elsewhere. For now it's here. BroString* bs = concatenate(bof_buffer.chunks); val->Assign(bof_buffer_idx, new StringVal(bs)); } - DBG_LOG(DBG_FILE_ANALYSIS, "[%s] %" PRIu64 " bytes in at offset %" PRIu64 "; %s [%s]", - id.c_str(), len, stream_offset, - IsComplete() ? "complete" : "incomplete", - fmt_bytes((const char*) data, min((uint64)40, len)), len > 40 ? "..." : ""); + return false; + } + +void File::DeliverStream(const u_char* data, uint64 len) + { + bool bof_was_full = bof_buffer.full; + // Buffer enough data for the BOF buffer + BufferBOF(data, len); + + if ( ! did_mime_type && bof_buffer.full && + LookupFieldDefaultCount(missing_bytes_idx) == 0 ) + DetectMIME(); + + DBG_LOG(DBG_FILE_ANALYSIS, + "[%s] %" PRIu64 " stream bytes in at offset %" PRIu64 "; %s [%s%s]", + id.c_str(), len, stream_offset, + IsComplete() ? "complete" : "incomplete", + fmt_bytes((const char*) data, min((uint64)40, len)), + len > 40 ? "..." : ""); file_analysis::Analyzer* a = 0; IterCookie* c = analyzers.InitForIteration(); + while ( (a = analyzers.NextEntry(c)) ) { - if ( !a->DeliverStream(data, len) ) + if ( ! a->GotStreamDelivery() ) { - analyzers.QueueRemove(a->Tag(), a->Args()); + int num_bof_chunks_behind = bof_buffer.chunks.size(); + + if ( ! bof_was_full ) + // We just added a chunk to the BOF buffer, don't count it + // as it will get delivered on its own. + num_bof_chunks_behind -= 1; + + uint64 bytes_delivered = 0; + + // Catch this analyzer up with the BOF buffer. + for ( int i = 0; i < num_bof_chunks_behind; ++i ) + { + if ( ! a->DeliverStream(bof_buffer.chunks[i]->Bytes(), + bof_buffer.chunks[i]->Len()) ) + analyzers.QueueRemove(a->Tag(), a->Args()); + + bytes_delivered += bof_buffer.chunks[i]->Len(); + } + + a->SetGotStreamDelivery(); + // May need to catch analyzer up on missed gap? + // Analyzer should be fully caught up to stream_offset now. } + + if ( ! a->DeliverStream(data, len) ) + analyzers.QueueRemove(a->Tag(), a->Args()); } stream_offset += len; @@ -389,21 +411,20 @@ void File::DeliverChunk(const u_char* data, uint64 len, uint64 offset) if ( file_reassembler ) { if ( reassembly_max_buffer > 0 && - reassembly_max_buffer < file_reassembler->TotalSize() ) + reassembly_max_buffer < file_reassembler->TotalSize() ) { - uint64 first_offset = file_reassembler->GetFirstBlockOffset(); - int gap_bytes = file_reassembler->TrimToSeq(first_offset); - + uint64 current_offset = stream_offset; + uint64 gap_bytes = file_reassembler->Flush(); + IncrementByteCount(gap_bytes, overflow_bytes_idx); + if ( FileEventAvailable(file_reassembly_overflow) ) { val_list* vl = new val_list(); vl->append(val->Ref()); - vl->append(new Val(stream_offset, TYPE_COUNT)); + vl->append(new Val(current_offset, TYPE_COUNT)); vl->append(new Val(gap_bytes, TYPE_COUNT)); FileEvent(file_reassembly_overflow, vl); } - - Gap(stream_offset, gap_bytes); } // Forward data to the reassembler. @@ -428,29 +449,28 @@ void File::DeliverChunk(const u_char* data, uint64 len, uint64 offset) IncrementByteCount(len, overflow_bytes_idx); } - DBG_LOG(DBG_FILE_ANALYSIS, "[%s] %" PRIu64 " bytes in; %s [%s]", - id.c_str(), len, - IsComplete() ? "complete" : "incomplete", - fmt_bytes((const char*) data, min((uint64)40, len)), len > 40 ? "..." : ""); + DBG_LOG(DBG_FILE_ANALYSIS, + "[%s] %" PRIu64 " chunk bytes in at offset %" PRIu64 "; %s [%s%s]", + id.c_str(), len, offset, + IsComplete() ? "complete" : "incomplete", + fmt_bytes((const char*) data, min((uint64)40, len)), + len > 40 ? "..." : ""); file_analysis::Analyzer* a = 0; IterCookie* c = analyzers.InitForIteration(); + while ( (a = analyzers.NextEntry(c)) ) { - if ( !a->DeliverChunk(data, len, offset) ) + if ( ! a->DeliverChunk(data, len, offset) ) { analyzers.QueueRemove(a->Tag(), a->Args()); } } if ( IsComplete() ) - { - // If the file is complete we can automatically go and close out the file from here. EndOfFile(); - } } - void File::DataIn(const u_char* data, uint64 len, uint64 offset) { analyzers.DrainModifications(); @@ -461,10 +481,7 @@ void File::DataIn(const u_char* data, uint64 len, uint64 offset) void File::DataIn(const u_char* data, uint64 len) { analyzers.DrainModifications(); - - uint64 offset = LookupFieldDefaultCount(seen_bytes_idx) + - LookupFieldDefaultCount(missing_bytes_idx); - DeliverChunk(data, len, offset); + DeliverChunk(data, len, stream_offset); analyzers.DrainModifications(); } @@ -475,20 +492,18 @@ void File::EndOfFile() if ( done ) return; - if ( ! did_mime_type ) - { + if ( ! did_mime_type && + LookupFieldDefaultCount(missing_bytes_idx) == 0 ) DetectMIME(); - // TODO: this also needs to be done elsewhere. - if ( bof_buffer.size > 0 ) - { - BroString* bs = concatenate(bof_buffer.chunks); - val->Assign(bof_buffer_idx, new StringVal(bs)); - } - } - analyzers.DrainModifications(); + if ( file_reassembler ) + { + file_reassembler->Flush(); + analyzers.DrainModifications(); + } + done = true; file_analysis::Analyzer* a = 0; @@ -507,9 +522,16 @@ void File::EndOfFile() void File::Gap(uint64 offset, uint64 len) { - DBG_LOG(DBG_FILE_ANALYSIS, "[%s] Gap of size %" PRIu64 " at offset %" PRIu64, + DBG_LOG(DBG_FILE_ANALYSIS, "[%s] Gap of size %" PRIu64 " at offset %," PRIu64, id.c_str(), len, offset); + if ( file_reassembler && ! file_reassembler->IsCurrentlyFlushing() ) + { + file_reassembler->FlushTo(offset + len); + // The reassembler will call us back with all the gaps we need to know. + return; + } + analyzers.DrainModifications(); file_analysis::Analyzer* a = 0; diff --git a/src/file_analysis/File.h b/src/file_analysis/File.h index bfd38a263c..645f7d5111 100644 --- a/src/file_analysis/File.h +++ b/src/file_analysis/File.h @@ -3,7 +3,6 @@ #ifndef FILE_ANALYSIS_FILE_H #define FILE_ANALYSIS_FILE_H -#include #include #include #include @@ -89,10 +88,10 @@ public: void SetTotalBytes(uint64 size); /** - * Compares "seen_bytes" field to "total_bytes" field of #val record to - * determine if the full file has been seen. - * @return false if "total_bytes" hasn't been set yet or "seen_bytes" is - * less than it, else true. + * @return true if file analysis is complete for the file, else false. + * It is incomplete if the total size is unknown or if the number of bytes + * streamed to analyzers (either as data delivers or gap information) + * matches the known total size. */ bool IsComplete() const; @@ -175,13 +174,14 @@ protected: * Constructor; only file_analysis::Manager should be creating these. * @param file_id an identifier string for the file in pretty hash form * (similar to connection uids). + * @param source_name the value for the source field to fill in. * @param conn a network connection over which the file is transferred. * @param tag the network protocol over which the file is transferred. * @param is_orig true if the file is being transferred from the originator * of the connection to the responder. False indicates the other * direction. */ - File(const string& file_id, Connection* conn = 0, + File(const string& file_id, const string& source_name, Connection* conn = 0, analyzer::Tag tag = analyzer::Tag::Error, bool is_orig = false); /** @@ -189,8 +189,14 @@ protected: * \c conn_id and UID taken from \a conn. * @param conn the connection over which a part of the file has been seen. * @param is_orig true if the connection originator is sending the file. + * @return true if the connection was previously unknown. */ - void UpdateConnectionFields(Connection* conn, bool is_orig); + bool UpdateConnectionFields(Connection* conn, bool is_orig); + + /** + * Raise the file_over_new_connection event with given arguments. + */ + void RaiseFileOverNewConnection(Connection* conn, bool is_orig); /** * Increment a byte count field of #val record by \a size. @@ -223,15 +229,10 @@ protected: */ bool BufferBOF(const u_char* data, uint64 len); - /** - * Forward any beginning-of-file buffered data on to DataIn stream. - */ - void ReplayBOF(); - /** * Does mime type detection via file magic signatures and assigns * strongest matching mime type (if available) to \c mime_type - * field in #val. It uses the data in the BOF buffer + * field in #val. It uses the data in the BOF buffer. * @return whether a mime type match was found. */ bool DetectMIME(); @@ -278,7 +279,7 @@ protected: protected: string id; /**< A pretty hash that likely identifies file */ RecordVal* val; /**< \c fa_file from script layer. */ - FileReassembler *file_reassembler; /**< A reassembler for the file if it's needed. */ + FileReassembler* file_reassembler; /**< A reassembler for the file if it's needed. */ uint64 stream_offset; /**< The offset of the file which has been forwarded. */ uint64 reassembly_max_buffer; /**< Maximum allowed buffer for reassembly. */ bool did_mime_type; /**< Whether the mime type ident has already been attempted. */ diff --git a/src/file_analysis/FileReassembler.cc b/src/file_analysis/FileReassembler.cc index 71e4c30bca..d2b4eda23d 100644 --- a/src/file_analysis/FileReassembler.cc +++ b/src/file_analysis/FileReassembler.cc @@ -8,7 +8,7 @@ namespace file_analysis { class File; FileReassembler::FileReassembler(File *f, uint64 starting_offset) - : Reassembler(starting_offset), the_file(f) + : Reassembler(starting_offset), the_file(f), flushing(false) { } @@ -16,6 +16,35 @@ FileReassembler::~FileReassembler() { } +uint64 FileReassembler::Flush() + { + if ( flushing ) + return 0; + + if ( last_block ) + { + // This is expected to call back into FileReassembler::Undelivered(). + flushing = true; + uint64 rval = TrimToSeq(last_block->upper); + flushing = false; + return rval; + } + + return 0; + } + +uint64 FileReassembler::FlushTo(uint64 sequence) + { + if ( flushing ) + return 0; + + flushing = true; + uint64 rval = TrimToSeq(sequence); + flushing = false; + last_reassem_seq = sequence; + return rval; + } + void FileReassembler::BlockInserted(DataBlock* start_block) { if ( start_block->seq > last_reassem_seq || @@ -28,7 +57,6 @@ void FileReassembler::BlockInserted(DataBlock* start_block) if ( b->seq == last_reassem_seq ) { // New stuff. uint64 len = b->Size(); - uint64 seq = last_reassem_seq; last_reassem_seq += len; the_file->DeliverStream(b->block, len); } @@ -40,7 +68,37 @@ void FileReassembler::BlockInserted(DataBlock* start_block) void FileReassembler::Undelivered(uint64 up_to_seq) { - // Not doing anything here yet. + // If we have blocks that begin below up_to_seq, deliver them. + DataBlock* b = blocks; + + while ( b ) + { + if ( b->seq < last_reassem_seq ) + { + // Already delivered this block. + b = b->next; + continue; + } + + if ( b->seq >= up_to_seq ) + // Block is beyond what we need to process at this point. + break; + + uint64 gap_at_seq = last_reassem_seq; + uint64 gap_len = b->seq - last_reassem_seq; + the_file->Gap(gap_at_seq, gap_len); + last_reassem_seq += gap_len; + BlockInserted(b); + // Inserting a block may cause trimming of what's buffered, + // so have to assume 'b' is invalid, hence re-assign to start. + b = blocks; + } + + if ( up_to_seq > last_reassem_seq ) + { + the_file->Gap(last_reassem_seq, up_to_seq - last_reassem_seq); + last_reassem_seq = up_to_seq; + } } void FileReassembler::Overlap(const u_char* b1, const u_char* b2, uint64 n) diff --git a/src/file_analysis/FileReassembler.h b/src/file_analysis/FileReassembler.h index c29563efc8..aa68e865ad 100644 --- a/src/file_analysis/FileReassembler.h +++ b/src/file_analysis/FileReassembler.h @@ -11,9 +11,6 @@ namespace file_analysis { class File; -//const int STOP_ON_GAP = 1; -//const int PUNT_ON_PARTIAL = 1; - class FileReassembler : public Reassembler { public: @@ -21,12 +18,35 @@ public: virtual ~FileReassembler(); void Done(); - uint64 GetFirstBlockOffset() { return blocks->seq; } // Checks if we have delivered all contents that we can possibly // deliver for this endpoint. void CheckEOF(); + /** + * Discards all contents of the reassembly buffer. This will spin through + * the buffer and call File::DeliverStream() and File::Gap() wherever + * appropriate. + * @return the number of new bytes now detected as gaps in the file. + */ + uint64 Flush(); + + /** + * Discards all contents of the reassembly buffer up to a given sequence + * number. This will spin through the buffer and call + * File::DeliverStream() and File::Gap() wherever appropriate. + * @param sequence the sequence number to flush until. + * @return the number of new bytes now detected as gaps in the file. + */ + uint64 FlushTo(uint64 sequence); + + /** + * @return whether the reassembler is currently is the process of flushing + * out the contents of its buffer. + */ + bool IsCurrentlyFlushing() const + { return flushing; } + protected: FileReassembler() { } @@ -36,10 +56,8 @@ protected: void BlockInserted(DataBlock* b); void Overlap(const u_char* b1, const u_char* b2, uint64 n); - unsigned int had_gap:1; - unsigned int did_EOF:1; - unsigned int skip_deliveries:1; File* the_file; + bool flushing; }; } // namespace analyzer::* diff --git a/src/file_analysis/Manager.cc b/src/file_analysis/Manager.cc index 191bd1e1e4..995d422a37 100644 --- a/src/file_analysis/Manager.cc +++ b/src/file_analysis/Manager.cc @@ -154,14 +154,12 @@ string Manager::DataIn(const u_char* data, uint64 len, analyzer::Tag tag, void Manager::DataIn(const u_char* data, uint64 len, const string& file_id, const string& source) { - File* file = GetFile(file_id); + File* file = GetFile(file_id, 0, analyzer::Tag::Error, false, false, + source.c_str()); if ( ! file ) return; - if ( file->GetSource().empty() ) - file->SetSource(source); - file->DataIn(data, len); if ( file->IsComplete() ) @@ -299,7 +297,8 @@ bool Manager::RemoveAnalyzer(const string& file_id, file_analysis::Tag tag, } File* Manager::GetFile(const string& file_id, Connection* conn, - analyzer::Tag tag, bool is_orig, bool update_conn) + analyzer::Tag tag, bool is_orig, bool update_conn, + const char* source_name) { if ( file_id.empty() ) return 0; @@ -311,15 +310,18 @@ File* Manager::GetFile(const string& file_id, Connection* conn, if ( ! rval ) { - rval = new File(file_id, conn, tag, is_orig); + rval = new File(file_id, + source_name ? source_name + : analyzer_mgr->GetComponentName(tag), + conn, tag, is_orig); id_map.Insert(file_id.c_str(), rval); rval->ScheduleInactivityTimer(); - // Generate file_new here so the manager knows about the file. + // Generate file_new after inserting it into manager's mapping + // in case script-layer calls back in to core from the event. rval->FileEvent(file_new); - // Same for file_over_new_connection which is generated by - // updating the connection fields. - rval->UpdateConnectionFields(conn, is_orig); + // Same for file_over_new_connection. + rval->RaiseFileOverNewConnection(conn, is_orig); if ( IsIgnored(file_id) ) return 0; @@ -328,8 +330,8 @@ File* Manager::GetFile(const string& file_id, Connection* conn, { rval->UpdateLastActivityTime(); - if ( update_conn ) - rval->UpdateConnectionFields(conn, is_orig); + if ( update_conn && rval->UpdateConnectionFields(conn, is_orig) ) + rval->RaiseFileOverNewConnection(conn, is_orig); } return rval; diff --git a/src/file_analysis/Manager.h b/src/file_analysis/Manager.h index 2e8efefcb0..93c8e7f613 100644 --- a/src/file_analysis/Manager.h +++ b/src/file_analysis/Manager.h @@ -319,6 +319,7 @@ protected: * this file isn't related to a connection). * @param update_conn whether we need to update connection-related field * in the \c fa_file record value associated with the file. + * @param an optional value of the source field to fill in. * @return the File object mapped to \a file_id or a null pointer if * analysis is being ignored for the associated file. An File * object may be created if a mapping doesn't exist, and if it did @@ -327,7 +328,8 @@ protected: */ File* GetFile(const string& file_id, Connection* conn = 0, analyzer::Tag tag = analyzer::Tag::Error, - bool is_orig = false, bool update_conn = true); + bool is_orig = false, bool update_conn = true, + const char* source_name = 0); /** * Try to retrieve a file that's being analyzed, using its identifier/hash. diff --git a/src/file_analysis/analyzer/extract/Extract.cc b/src/file_analysis/analyzer/extract/Extract.cc index 8b3ed4cdad..eeec8ef464 100644 --- a/src/file_analysis/analyzer/extract/Extract.cc +++ b/src/file_analysis/analyzer/extract/Extract.cc @@ -68,7 +68,6 @@ static bool check_limit_exceeded(uint64 lim, uint64 depth, uint64 len, uint64* n } else if ( depth + len > lim ) { - printf("exceeded the maximum extraction lenght depth: %llu len: %llu lim: %llu\n", depth, len, lim); *n = lim - depth; return true; } diff --git a/testing/btest/Baseline/core.bits_per_uid/128 b/testing/btest/Baseline/core.bits_per_uid/128 index 95ef343262..1cbf61a906 100644 --- a/testing/btest/Baseline/core.bits_per_uid/128 +++ b/testing/btest/Baseline/core.bits_per_uid/128 @@ -1,9 +1,9 @@ CUWkUyAuUGXfarKYeMETxOg Ck6kgXLOoSKlnQcgTWjvg4c -Cj4u32Pc5bifTEfuqmmG4bh Fj3nTWNjezo6G6xBmyo58Tf +Cj4u32Pc5bifTEfuqmmG4bh F4VAnSiNGSQhKEoCPd4zuQd CFrJExwHcSal5OKnoww6xl4 -C3PKsZ2Uye21VW0XPVINV8a FaJg8mtdsS86cWjSe4spPPl +C3PKsZ2Uye21VW0XPVINV8a FvBr89nD30GgGAp3wgtm6qf diff --git a/testing/btest/Baseline/core.bits_per_uid/256 b/testing/btest/Baseline/core.bits_per_uid/256 index 95ef343262..1cbf61a906 100644 --- a/testing/btest/Baseline/core.bits_per_uid/256 +++ b/testing/btest/Baseline/core.bits_per_uid/256 @@ -1,9 +1,9 @@ CUWkUyAuUGXfarKYeMETxOg Ck6kgXLOoSKlnQcgTWjvg4c -Cj4u32Pc5bifTEfuqmmG4bh Fj3nTWNjezo6G6xBmyo58Tf +Cj4u32Pc5bifTEfuqmmG4bh F4VAnSiNGSQhKEoCPd4zuQd CFrJExwHcSal5OKnoww6xl4 -C3PKsZ2Uye21VW0XPVINV8a FaJg8mtdsS86cWjSe4spPPl +C3PKsZ2Uye21VW0XPVINV8a FvBr89nD30GgGAp3wgtm6qf diff --git a/testing/btest/Baseline/core.bits_per_uid/32 b/testing/btest/Baseline/core.bits_per_uid/32 index a20d05dbd5..27965ff04a 100644 --- a/testing/btest/Baseline/core.bits_per_uid/32 +++ b/testing/btest/Baseline/core.bits_per_uid/32 @@ -1,9 +1,9 @@ CXWv6p30 CCyvnA30 -CjhGID40 F75yAm10 +CjhGID40 FmGk6O30 CdfHBz20 -CCvvfg30 Fuh3fj10 +CCvvfg30 Ftwuyy30 diff --git a/testing/btest/Baseline/core.bits_per_uid/64 b/testing/btest/Baseline/core.bits_per_uid/64 index b34eb4879d..e268d02801 100644 --- a/testing/btest/Baseline/core.bits_per_uid/64 +++ b/testing/btest/Baseline/core.bits_per_uid/64 @@ -1,9 +1,9 @@ CUWkUyAuUGXf0 CarKYeMETxOg0 -Ck6kgXLOoSKl0 Fj3nTWNjezo60 +Ck6kgXLOoSKl0 F4VAnSiNGSQh0 CnQcgTWjvg4c0 -Cj4u32Pc5bif0 FaJg8mtdsS860 +Cj4u32Pc5bif0 FvBr89nD30Gg0 diff --git a/testing/btest/Baseline/core.bits_per_uid/96 b/testing/btest/Baseline/core.bits_per_uid/96 index 3ba0f50e04..655122649b 100644 --- a/testing/btest/Baseline/core.bits_per_uid/96 +++ b/testing/btest/Baseline/core.bits_per_uid/96 @@ -1,9 +1,9 @@ CXWv6p3arKYeMETxOg CjhGID4nQcgTWjvg4c -CCvvfg3TEfuqmmG4bh F75yAm1G6xBmyo58Tf +CCvvfg3TEfuqmmG4bh FmGk6O3KEoCPd4zuQd CsRx2w45OKnoww6xl4 -CRJuHdVW0XPVINV8a Fuh3fj1cWjSe4spPPl +CRJuHdVW0XPVINV8a Ftwuyy3GAp3wgtm6qf diff --git a/testing/btest/Baseline/plugins.hooks/output b/testing/btest/Baseline/plugins.hooks/output index 31dd415e1b..f42c8ec042 100644 --- a/testing/btest/Baseline/plugins.hooks/output +++ b/testing/btest/Baseline/plugins.hooks/output @@ -189,7 +189,7 @@ 0.000000 MetaHookPost CallFunction(Log::__create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -> -0.000000 MetaHookPost CallFunction(Log::__write, (PacketFilter::LOG, [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::__write, (PacketFilter::LOG, [ts=1418743793.447552, node=bro, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, (Cluster::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, (Communication::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, (Conn::LOG)) -> @@ -283,8 +283,8 @@ 0.000000 MetaHookPost CallFunction(Log::create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -> -0.000000 MetaHookPost CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T])) -> -0.000000 MetaHookPost CallFunction(Log::write, (PacketFilter::LOG, [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1418743793.447552, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::write, (PacketFilter::LOG, [ts=1418743793.447552, node=bro, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(Notice::want_pp, ()) -> 0.000000 MetaHookPost CallFunction(PacketFilter::build, ()) -> 0.000000 MetaHookPost CallFunction(PacketFilter::combine_filters, (ip or not ip, and, )) -> @@ -724,7 +724,7 @@ 0.000000 MetaHookPre CallFunction(Log::__create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -0.000000 MetaHookPre CallFunction(Log::__write, (PacketFilter::LOG, [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::__write, (PacketFilter::LOG, [ts=1418743793.447552, node=bro, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, (Cluster::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, (Communication::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, (Conn::LOG)) @@ -818,8 +818,8 @@ 0.000000 MetaHookPre CallFunction(Log::create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) 0.000000 MetaHookPre CallFunction(Log::create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) 0.000000 MetaHookPre CallFunction(Log::create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -0.000000 MetaHookPre CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T])) -0.000000 MetaHookPre CallFunction(Log::write, (PacketFilter::LOG, [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1418743793.447552, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::write, (PacketFilter::LOG, [ts=1418743793.447552, node=bro, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(Notice::want_pp, ()) 0.000000 MetaHookPre CallFunction(PacketFilter::build, ()) 0.000000 MetaHookPre CallFunction(PacketFilter::combine_filters, (ip or not ip, and, )) @@ -1259,7 +1259,7 @@ 0.000000 | HookCallFunction Log::__create_stream(Weird::LOG, [columns=, ev=Weird::log_weird]) 0.000000 | HookCallFunction Log::__create_stream(X509::LOG, [columns=, ev=X509::log_x509]) 0.000000 | HookCallFunction Log::__create_stream(mysql::LOG, [columns=, ev=MySQL::log_mysql]) -0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1418743793.447552, node=bro, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction Log::add_default_filter(Cluster::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Communication::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Conn::LOG) @@ -1353,8 +1353,8 @@ 0.000000 | HookCallFunction Log::create_stream(Weird::LOG, [columns=, ev=Weird::log_weird]) 0.000000 | HookCallFunction Log::create_stream(X509::LOG, [columns=, ev=X509::log_x509]) 0.000000 | HookCallFunction Log::create_stream(mysql::LOG, [columns=, ev=MySQL::log_mysql]) -0.000000 | HookCallFunction Log::default_path_func(PacketFilter::LOG, , [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T]) -0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::default_path_func(PacketFilter::LOG, , [ts=1418743793.447552, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1418743793.447552, node=bro, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction Notice::want_pp() 0.000000 | HookCallFunction PacketFilter::build() 0.000000 | HookCallFunction PacketFilter::combine_filters(ip or not ip, and, ) @@ -1566,17 +1566,19 @@ 1362692527.008509 MetaHookPre UpdateNetworkTime(1362692527.008509) 1362692527.008509 | HookUpdateNetworkTime 1362692527.008509 1362692527.008509 | HookDrainEvents -1362692527.009512 MetaHookPost CallFunction(Files::__add_analyzers_for_mime_type, (FakNcS1Jfe01uljb3, text/plain, [chunk_event=, stream_event=, extract_filename=, extract_limit=0])) -> -1362692527.009512 MetaHookPost CallFunction(Files::add_analyzers_for_mime_type, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain)) -> -1362692527.009512 MetaHookPost CallFunction(Files::set_info, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=])) -> -1362692527.009512 MetaHookPost CallFunction(Files::set_info, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain]], info=, u2_events=])) -> +1362692527.009512 MetaHookPost CallFunction(Files::__enable_reassembly, (FakNcS1Jfe01uljb3)) -> +1362692527.009512 MetaHookPost CallFunction(Files::__set_reassembly_buffer, (FakNcS1Jfe01uljb3, 1048576)) -> +1362692527.009512 MetaHookPost CallFunction(Files::enable_reassembly, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1362692527.009512, fuid=FakNcS1Jfe01uljb3, tx_hosts={}, rx_hosts={}, conn_uids={}, source=HTTP, depth=0, analyzers={}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=])) -> +1362692527.009512 MetaHookPost CallFunction(Files::set_info, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=])) -> +1362692527.009512 MetaHookPost CallFunction(Files::set_info, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1362692527.009512, fuid=FakNcS1Jfe01uljb3, tx_hosts={}, rx_hosts={}, conn_uids={}, source=HTTP, depth=0, analyzers={}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=])) -> +1362692527.009512 MetaHookPost CallFunction(Files::set_reassembly_buffer_size, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1362692527.009512, fuid=FakNcS1Jfe01uljb3, tx_hosts={}, rx_hosts={}, conn_uids={}, source=HTTP, depth=0, analyzers={}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=], 1048576)) -> 1362692527.009512 MetaHookPost CallFunction(HTTP::code_in_range, (200, 100, 199)) -> 1362692527.009512 MetaHookPost CallFunction(HTTP::get_file_handle, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) -> 1362692527.009512 MetaHookPost CallFunction(HTTP::set_state, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, F)) -> 1362692527.009512 MetaHookPost CallFunction(HTTP::set_state, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, F)) -> 1362692527.009512 MetaHookPost CallFunction(HTTP::set_state, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=, status_msg=, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, F)) -> 1362692527.009512 MetaHookPost CallFunction(cat, (Analyzer::ANALYZER_HTTP, 1362692526.869344, F, 1, 1, 141.142.228.5:59856 > 192.150.187.43:80)) -> -1362692527.009512 MetaHookPost CallFunction(file_new, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain]], info=, u2_events=])) -> +1362692527.009512 MetaHookPost CallFunction(file_new, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=])) -> 1362692527.009512 MetaHookPost CallFunction(file_over_new_connection, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) -> 1362692527.009512 MetaHookPost CallFunction(fmt, (%s:%d > %s:%d, 141.142.228.5, 59856<...>/tcp)) -> 1362692527.009512 MetaHookPost CallFunction(get_file_handle, (Analyzer::ANALYZER_HTTP, [id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) -> @@ -1595,7 +1597,7 @@ 1362692527.009512 MetaHookPost CallFunction(set_file_handle, (Analyzer::ANALYZER_HTTP1362692526.869344F11141.142.228.5:59856 > 192.150.187.43:80)) -> 1362692527.009512 MetaHookPost CallFunction(split_all, (HTTP, <...>/)) -> 1362692527.009512 MetaHookPost DrainEvents() -> -1362692527.009512 MetaHookPost QueueEvent(file_new([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain]], info=, u2_events=])) -> false +1362692527.009512 MetaHookPost QueueEvent(file_new([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=])) -> false 1362692527.009512 MetaHookPost QueueEvent(file_over_new_connection([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) -> false 1362692527.009512 MetaHookPost QueueEvent(get_file_handle(Analyzer::ANALYZER_HTTP, [id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=, status_msg=, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=0], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) -> false 1362692527.009512 MetaHookPost QueueEvent(http_begin_entity([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=, status_msg=, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=0], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) -> false @@ -1610,17 +1612,19 @@ 1362692527.009512 MetaHookPost QueueEvent(http_header([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain; charset=UTF-8)) -> false 1362692527.009512 MetaHookPost QueueEvent(http_reply([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=, status_msg=, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=0], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], 1.1, 200, OK)) -> false 1362692527.009512 MetaHookPost UpdateNetworkTime(1362692527.009512) -> -1362692527.009512 MetaHookPre CallFunction(Files::__add_analyzers_for_mime_type, (FakNcS1Jfe01uljb3, text/plain, [chunk_event=, stream_event=, extract_filename=, extract_limit=0])) -1362692527.009512 MetaHookPre CallFunction(Files::add_analyzers_for_mime_type, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain)) -1362692527.009512 MetaHookPre CallFunction(Files::set_info, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=])) -1362692527.009512 MetaHookPre CallFunction(Files::set_info, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain]], info=, u2_events=])) +1362692527.009512 MetaHookPre CallFunction(Files::__enable_reassembly, (FakNcS1Jfe01uljb3)) +1362692527.009512 MetaHookPre CallFunction(Files::__set_reassembly_buffer, (FakNcS1Jfe01uljb3, 1048576)) +1362692527.009512 MetaHookPre CallFunction(Files::enable_reassembly, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1362692527.009512, fuid=FakNcS1Jfe01uljb3, tx_hosts={}, rx_hosts={}, conn_uids={}, source=HTTP, depth=0, analyzers={}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=])) +1362692527.009512 MetaHookPre CallFunction(Files::set_info, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=])) +1362692527.009512 MetaHookPre CallFunction(Files::set_info, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1362692527.009512, fuid=FakNcS1Jfe01uljb3, tx_hosts={}, rx_hosts={}, conn_uids={}, source=HTTP, depth=0, analyzers={}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=])) +1362692527.009512 MetaHookPre CallFunction(Files::set_reassembly_buffer_size, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1362692527.009512, fuid=FakNcS1Jfe01uljb3, tx_hosts={}, rx_hosts={}, conn_uids={}, source=HTTP, depth=0, analyzers={}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=], 1048576)) 1362692527.009512 MetaHookPre CallFunction(HTTP::code_in_range, (200, 100, 199)) 1362692527.009512 MetaHookPre CallFunction(HTTP::get_file_handle, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) 1362692527.009512 MetaHookPre CallFunction(HTTP::set_state, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, F)) 1362692527.009512 MetaHookPre CallFunction(HTTP::set_state, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, F)) 1362692527.009512 MetaHookPre CallFunction(HTTP::set_state, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=, status_msg=, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, F)) 1362692527.009512 MetaHookPre CallFunction(cat, (Analyzer::ANALYZER_HTTP, 1362692526.869344, F, 1, 1, 141.142.228.5:59856 > 192.150.187.43:80)) -1362692527.009512 MetaHookPre CallFunction(file_new, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain]], info=, u2_events=])) +1362692527.009512 MetaHookPre CallFunction(file_new, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=])) 1362692527.009512 MetaHookPre CallFunction(file_over_new_connection, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) 1362692527.009512 MetaHookPre CallFunction(fmt, (%s:%d > %s:%d, 141.142.228.5, 59856<...>/tcp)) 1362692527.009512 MetaHookPre CallFunction(get_file_handle, (Analyzer::ANALYZER_HTTP, [id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) @@ -1639,7 +1643,7 @@ 1362692527.009512 MetaHookPre CallFunction(set_file_handle, (Analyzer::ANALYZER_HTTP1362692526.869344F11141.142.228.5:59856 > 192.150.187.43:80)) 1362692527.009512 MetaHookPre CallFunction(split_all, (HTTP, <...>/)) 1362692527.009512 MetaHookPre DrainEvents() -1362692527.009512 MetaHookPre QueueEvent(file_new([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain]], info=, u2_events=])) +1362692527.009512 MetaHookPre QueueEvent(file_new([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=])) 1362692527.009512 MetaHookPre QueueEvent(file_over_new_connection([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) 1362692527.009512 MetaHookPre QueueEvent(get_file_handle(Analyzer::ANALYZER_HTTP, [id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=, status_msg=, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=0], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) 1362692527.009512 MetaHookPre QueueEvent(http_begin_entity([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=, status_msg=, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=0], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) @@ -1655,17 +1659,19 @@ 1362692527.009512 MetaHookPre QueueEvent(http_reply([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=, status_msg=, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=0], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], 1.1, 200, OK)) 1362692527.009512 MetaHookPre UpdateNetworkTime(1362692527.009512) 1362692527.009512 | HookUpdateNetworkTime 1362692527.009512 -1362692527.009512 | HookCallFunction Files::__add_analyzers_for_mime_type(FakNcS1Jfe01uljb3, text/plain, [chunk_event=, stream_event=, extract_filename=, extract_limit=0]) -1362692527.009512 | HookCallFunction Files::add_analyzers_for_mime_type([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain) -1362692527.009512 | HookCallFunction Files::set_info([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=]) -1362692527.009512 | HookCallFunction Files::set_info([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain]], info=, u2_events=]) +1362692527.009512 | HookCallFunction Files::__enable_reassembly(FakNcS1Jfe01uljb3) +1362692527.009512 | HookCallFunction Files::__set_reassembly_buffer(FakNcS1Jfe01uljb3, 1048576) +1362692527.009512 | HookCallFunction Files::enable_reassembly([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1362692527.009512, fuid=FakNcS1Jfe01uljb3, tx_hosts={}, rx_hosts={}, conn_uids={}, source=HTTP, depth=0, analyzers={}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=]) +1362692527.009512 | HookCallFunction Files::set_info([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=]) +1362692527.009512 | HookCallFunction Files::set_info([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1362692527.009512, fuid=FakNcS1Jfe01uljb3, tx_hosts={}, rx_hosts={}, conn_uids={}, source=HTTP, depth=0, analyzers={}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=]) +1362692527.009512 | HookCallFunction Files::set_reassembly_buffer_size([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1362692527.009512, fuid=FakNcS1Jfe01uljb3, tx_hosts={}, rx_hosts={}, conn_uids={}, source=HTTP, depth=0, analyzers={}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=], 1048576) 1362692527.009512 | HookCallFunction HTTP::code_in_range(200, 100, 199) 1362692527.009512 | HookCallFunction HTTP::get_file_handle([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F) 1362692527.009512 | HookCallFunction HTTP::set_state([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, F) 1362692527.009512 | HookCallFunction HTTP::set_state([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, F) 1362692527.009512 | HookCallFunction HTTP::set_state([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=, status_msg=, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, F) 1362692527.009512 | HookCallFunction cat(Analyzer::ANALYZER_HTTP, 1362692526.869344, F, 1, 1, 141.142.228.5:59856 > 192.150.187.43:80) -1362692527.009512 | HookCallFunction file_new([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain]], info=, u2_events=]) +1362692527.009512 | HookCallFunction file_new([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=]) 1362692527.009512 | HookCallFunction file_over_new_connection([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F) 1362692527.009512 | HookCallFunction fmt(%s:%d > %s:%d, 141.142.228.5, 59856<...>/tcp) 1362692527.009512 | HookCallFunction get_file_handle(Analyzer::ANALYZER_HTTP, [id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F) @@ -1684,7 +1690,7 @@ 1362692527.009512 | HookCallFunction set_file_handle(Analyzer::ANALYZER_HTTP1362692526.869344F11141.142.228.5:59856 > 192.150.187.43:80) 1362692527.009512 | HookCallFunction split_all(HTTP, <...>/) 1362692527.009512 | HookDrainEvents -1362692527.009512 | HookQueueEvent file_new([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain]], info=, u2_events=]) +1362692527.009512 | HookQueueEvent file_new([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=]) 1362692527.009512 | HookQueueEvent file_over_new_connection([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F) 1362692527.009512 | HookQueueEvent get_file_handle(Analyzer::ANALYZER_HTTP, [id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=, status_msg=, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=0], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F) 1362692527.009512 | HookQueueEvent http_begin_entity([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=, status_msg=, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=0], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F) @@ -1710,7 +1716,8 @@ 1362692527.009765 MetaHookPre UpdateNetworkTime(1362692527.009765) 1362692527.009765 | HookUpdateNetworkTime 1362692527.009765 1362692527.009765 | HookDrainEvents -1362692527.009775 MetaHookPost CallFunction(Files::set_info, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=])) -> +1362692527.009775 MetaHookPost CallFunction(Files::set_info, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=[FakNcS1Jfe01uljb3], resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1], irc=, u2_events=])) -> +1362692527.009775 MetaHookPost CallFunction(Files::set_info, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1], irc=, u2_events=])) -> 1362692527.009775 MetaHookPost CallFunction(HTTP::code_in_range, (200, 100, 199)) -> 1362692527.009775 MetaHookPost CallFunction(HTTP::get_file_handle, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) -> 1362692527.009775 MetaHookPost CallFunction(HTTP::set_state, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, F)) -> @@ -1721,7 +1728,8 @@ 1362692527.009775 MetaHookPost CallFunction(Log::write, (Files::LOG, [ts=1362692527.009512, fuid=FakNcS1Jfe01uljb3, tx_hosts={192.150.187.43}, rx_hosts={141.142.228.5}, conn_uids={CXWv6p3arKYeMETxOg}, source=HTTP, depth=0, analyzers={}, mime_type=text/plain, filename=, duration=262.0 usecs, local_orig=, is_orig=F, seen_bytes=4705, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=])) -> 1362692527.009775 MetaHookPost CallFunction(Log::write, (HTTP::LOG, [ts=1362692526.939527, uid=CXWv6p3arKYeMETxOg, id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1])) -> 1362692527.009775 MetaHookPost CallFunction(cat, (Analyzer::ANALYZER_HTTP, 1362692526.869344, F, 1, 1, 141.142.228.5:59856 > 192.150.187.43:80)) -> -1362692527.009775 MetaHookPost CallFunction(file_state_remove, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=])) -> +1362692527.009775 MetaHookPost CallFunction(file_mime_type, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain)) -> +1362692527.009775 MetaHookPost CallFunction(file_state_remove, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1], irc=, u2_events=])) -> 1362692527.009775 MetaHookPost CallFunction(fmt, (%s, Files::LOG)) -> 1362692527.009775 MetaHookPost CallFunction(fmt, (%s, HTTP::LOG)) -> 1362692527.009775 MetaHookPost CallFunction(fmt, (%s:%d > %s:%d, 141.142.228.5, 59856<...>/tcp)) -> @@ -1737,12 +1745,14 @@ 1362692527.009775 MetaHookPost CallFunction(to_lower, (Files)) -> 1362692527.009775 MetaHookPost CallFunction(to_lower, (HTTP)) -> 1362692527.009775 MetaHookPost DrainEvents() -> -1362692527.009775 MetaHookPost QueueEvent(file_state_remove([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=])) -> false +1362692527.009775 MetaHookPost QueueEvent(file_mime_type([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain)) -> false +1362692527.009775 MetaHookPost QueueEvent(file_state_remove([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1], irc=, u2_events=])) -> false 1362692527.009775 MetaHookPost QueueEvent(get_file_handle(Analyzer::ANALYZER_HTTP, [id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) -> false 1362692527.009775 MetaHookPost QueueEvent(http_end_entity([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) -> false 1362692527.009775 MetaHookPost QueueEvent(http_message_done([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, [start=1362692527.009512, interrupted=F, finish_msg=message ends normally, body_length=4705, content_gap_length=0, header_length=280])) -> false 1362692527.009775 MetaHookPost UpdateNetworkTime(1362692527.009775) -> -1362692527.009775 MetaHookPre CallFunction(Files::set_info, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=])) +1362692527.009775 MetaHookPre CallFunction(Files::set_info, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=[FakNcS1Jfe01uljb3], resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1], irc=, u2_events=])) +1362692527.009775 MetaHookPre CallFunction(Files::set_info, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1], irc=, u2_events=])) 1362692527.009775 MetaHookPre CallFunction(HTTP::code_in_range, (200, 100, 199)) 1362692527.009775 MetaHookPre CallFunction(HTTP::get_file_handle, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) 1362692527.009775 MetaHookPre CallFunction(HTTP::set_state, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, F)) @@ -1753,7 +1763,8 @@ 1362692527.009775 MetaHookPre CallFunction(Log::write, (Files::LOG, [ts=1362692527.009512, fuid=FakNcS1Jfe01uljb3, tx_hosts={192.150.187.43}, rx_hosts={141.142.228.5}, conn_uids={CXWv6p3arKYeMETxOg}, source=HTTP, depth=0, analyzers={}, mime_type=text/plain, filename=, duration=262.0 usecs, local_orig=, is_orig=F, seen_bytes=4705, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=])) 1362692527.009775 MetaHookPre CallFunction(Log::write, (HTTP::LOG, [ts=1362692526.939527, uid=CXWv6p3arKYeMETxOg, id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1])) 1362692527.009775 MetaHookPre CallFunction(cat, (Analyzer::ANALYZER_HTTP, 1362692526.869344, F, 1, 1, 141.142.228.5:59856 > 192.150.187.43:80)) -1362692527.009775 MetaHookPre CallFunction(file_state_remove, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=])) +1362692527.009775 MetaHookPre CallFunction(file_mime_type, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain)) +1362692527.009775 MetaHookPre CallFunction(file_state_remove, ([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1], irc=, u2_events=])) 1362692527.009775 MetaHookPre CallFunction(fmt, (%s, Files::LOG)) 1362692527.009775 MetaHookPre CallFunction(fmt, (%s, HTTP::LOG)) 1362692527.009775 MetaHookPre CallFunction(fmt, (%s:%d > %s:%d, 141.142.228.5, 59856<...>/tcp)) @@ -1769,13 +1780,15 @@ 1362692527.009775 MetaHookPre CallFunction(to_lower, (Files)) 1362692527.009775 MetaHookPre CallFunction(to_lower, (HTTP)) 1362692527.009775 MetaHookPre DrainEvents() -1362692527.009775 MetaHookPre QueueEvent(file_state_remove([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=])) +1362692527.009775 MetaHookPre QueueEvent(file_mime_type([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain)) +1362692527.009775 MetaHookPre QueueEvent(file_state_remove([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1], irc=, u2_events=])) 1362692527.009775 MetaHookPre QueueEvent(get_file_handle(Analyzer::ANALYZER_HTTP, [id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) 1362692527.009775 MetaHookPre QueueEvent(http_end_entity([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) 1362692527.009775 MetaHookPre QueueEvent(http_message_done([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, [start=1362692527.009512, interrupted=F, finish_msg=message ends normally, body_length=4705, content_gap_length=0, header_length=280])) 1362692527.009775 MetaHookPre UpdateNetworkTime(1362692527.009775) 1362692527.009775 | HookUpdateNetworkTime 1362692527.009775 -1362692527.009775 | HookCallFunction Files::set_info([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=]) +1362692527.009775 | HookCallFunction Files::set_info([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=[FakNcS1Jfe01uljb3], resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1], irc=, u2_events=]) +1362692527.009775 | HookCallFunction Files::set_info([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1], irc=, u2_events=]) 1362692527.009775 | HookCallFunction HTTP::code_in_range(200, 100, 199) 1362692527.009775 | HookCallFunction HTTP::get_file_handle([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F) 1362692527.009775 | HookCallFunction HTTP::set_state([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, F) @@ -1786,7 +1799,8 @@ 1362692527.009775 | HookCallFunction Log::write(Files::LOG, [ts=1362692527.009512, fuid=FakNcS1Jfe01uljb3, tx_hosts={192.150.187.43}, rx_hosts={141.142.228.5}, conn_uids={CXWv6p3arKYeMETxOg}, source=HTTP, depth=0, analyzers={}, mime_type=text/plain, filename=, duration=262.0 usecs, local_orig=, is_orig=F, seen_bytes=4705, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=]) 1362692527.009775 | HookCallFunction Log::write(HTTP::LOG, [ts=1362692526.939527, uid=CXWv6p3arKYeMETxOg, id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1]) 1362692527.009775 | HookCallFunction cat(Analyzer::ANALYZER_HTTP, 1362692526.869344, F, 1, 1, 141.142.228.5:59856 > 192.150.187.43:80) -1362692527.009775 | HookCallFunction file_state_remove([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=]) +1362692527.009775 | HookCallFunction file_mime_type([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain) +1362692527.009775 | HookCallFunction file_state_remove([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1], irc=, u2_events=]) 1362692527.009775 | HookCallFunction fmt(%s, Files::LOG) 1362692527.009775 | HookCallFunction fmt(%s, HTTP::LOG) 1362692527.009775 | HookCallFunction fmt(%s:%d > %s:%d, 141.142.228.5, 59856<...>/tcp) @@ -1802,7 +1816,8 @@ 1362692527.009775 | HookCallFunction to_lower(Files) 1362692527.009775 | HookCallFunction to_lower(HTTP) 1362692527.009775 | HookDrainEvents -1362692527.009775 | HookQueueEvent file_state_remove([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=4705, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], u2_events=]) +1362692527.009775 | HookQueueEvent file_mime_type([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain) +1362692527.009775 | HookQueueEvent file_state_remove([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1], irc=, u2_events=]) 1362692527.009775 | HookQueueEvent get_file_handle(Analyzer::ANALYZER_HTTP, [id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F) 1362692527.009775 | HookQueueEvent http_end_entity([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F) 1362692527.009775 | HookQueueEvent http_message_done([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, [start=1362692527.009512, interrupted=F, finish_msg=message ends normally, body_length=4705, content_gap_length=0, header_length=280]) diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.bifs.register_mime_type/files.log b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.bifs.register_mime_type/files.log index dcb1c18c97..ca56378a9b 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.bifs.register_mime_type/files.log +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.bifs.register_mime_type/files.log @@ -3,8 +3,8 @@ #empty_field (empty) #unset_field - #path files -#open 2014-10-08-03-58-17 +#open 2014-12-16-15-30-20 #fields ts fuid tx_hosts rx_hosts conn_uids source depth analyzers mime_type filename duration local_orig is_orig seen_bytes total_bytes missing_bytes overflow_bytes timedout parent_fuid md5 sha1 sha256 extracted #types time string set[addr] set[addr] set[string] string count set[string] string string interval bool bool count count count count bool string string string string string -1362692527.009765 FakNcS1Jfe01uljb3 192.150.187.43 141.142.228.5 CXWv6p3arKYeMETxOg HTTP 0 MD5 text/plain - 0.000010 - F 4705 4705 0 0 F - 397168fd09991a0e712254df7bc639ac - - - -#close 2014-10-08-03-58-17 +1362692527.009512 FakNcS1Jfe01uljb3 192.150.187.43 141.142.228.5 CXWv6p3arKYeMETxOg HTTP 0 MD5 text/plain - 0.000263 - F 4705 4705 0 0 F - 397168fd09991a0e712254df7bc639ac - - - +#close 2014-12-16-15-30-20 diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.bifs.set_timeout_interval/bro..stdout b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.bifs.set_timeout_interval/bro..stdout index e1e0eb2da4..89ee79cad4 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.bifs.set_timeout_interval/bro..stdout +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.bifs.set_timeout_interval/bro..stdout @@ -18,8 +18,11 @@ file #1, 0, 0 FILE_OVER_NEW_CONNECTION FILE_TIMEOUT FILE_TIMEOUT +FILE_GAP FILE_STATE_REMOVE -file #1, 0, 0 +file #1, 206024, 816896 [orig_h=192.168.72.14, orig_p=3257/tcp, resp_h=65.54.95.14, resp_p=80/tcp] +FILE_BOF_BUFFER +\x1b\xb8=\xb1\xff^PU^P\xce\xc3^ total bytes: 1022920 source: HTTP diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.multipart/out b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.multipart/out index 6499401f8d..d6b94e5372 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.multipart/out +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.multipart/out @@ -5,7 +5,7 @@ FILE_STATE_REMOVE file #0, 4, 0 [orig_h=141.142.228.5, orig_p=57262/tcp, resp_h=54.243.88.146, resp_p=80/tcp] FILE_BOF_BUFFER -test^M^J +test source: HTTP MD5: 098f6bcd4621d373cade4e832627b4f6 SHA1: a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 @@ -17,7 +17,7 @@ FILE_STATE_REMOVE file #1, 5, 0 [orig_h=141.142.228.5, orig_p=57262/tcp, resp_h=54.243.88.146, resp_p=80/tcp] FILE_BOF_BUFFER -test2^M^J +test2 source: HTTP MD5: ad0234829205b9033196ba818f7a872b SHA1: 109f4b3c50d7b0df729d299bc6f8e9ef9066971f @@ -29,7 +29,7 @@ FILE_STATE_REMOVE file #2, 5, 0 [orig_h=141.142.228.5, orig_p=57262/tcp, resp_h=54.243.88.146, resp_p=80/tcp] FILE_BOF_BUFFER -test3^M^J +test3 source: HTTP MD5: 8ad8757baa8564dc136c1e07507f4a98 SHA1: 3ebfa301dc59196f18593c45e519287a23297589 diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/b.out b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/b.out index 2b3d76e59d..36202f285b 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/b.out +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.http.partial-content/b.out @@ -17,8 +17,11 @@ FILE_NEW file #1, 0, 0 FILE_OVER_NEW_CONNECTION FILE_TIMEOUT +FILE_GAP FILE_STATE_REMOVE -file #1, 0, 0 +file #1, 206024, 816896 [orig_h=192.168.72.14, orig_p=3257/tcp, resp_h=65.54.95.14, resp_p=80/tcp] +FILE_BOF_BUFFER +\x1b\xb8=\xb1\xff^PU^P\xce\xc3^ total bytes: 1022920 source: HTTP diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.logging/files.log b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.logging/files.log index 7edaa67263..dfce362b50 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.logging/files.log +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.logging/files.log @@ -3,8 +3,8 @@ #empty_field (empty) #unset_field - #path files -#open 2014-10-08-03-59-03 +#open 2014-12-16-15-30-30 #fields ts fuid tx_hosts rx_hosts conn_uids source depth analyzers mime_type filename duration local_orig is_orig seen_bytes total_bytes missing_bytes overflow_bytes timedout parent_fuid md5 sha1 sha256 extracted #types time string set[addr] set[addr] set[string] string count set[string] string string interval bool bool count count count count bool string string string string string -1362692527.009765 FakNcS1Jfe01uljb3 192.150.187.43 141.142.228.5 CXWv6p3arKYeMETxOg HTTP 0 SHA256,DATA_EVENT,MD5,EXTRACT,SHA1 text/plain - 0.000010 - F 4705 4705 0 0 F - 397168fd09991a0e712254df7bc639ac 1dd7ac0398df6cbc0696445a91ec681facf4dc47 4e7c7ef0984119447e743e3ec77e1de52713e345cde03fe7df753a35849bed18 FakNcS1Jfe01uljb3-file -#close 2014-10-08-03-59-03 +1362692527.009512 FakNcS1Jfe01uljb3 192.150.187.43 141.142.228.5 CXWv6p3arKYeMETxOg HTTP 0 SHA256,DATA_EVENT,MD5,EXTRACT,SHA1 text/plain - 0.000263 - F 4705 4705 0 0 F - 397168fd09991a0e712254df7bc639ac 1dd7ac0398df6cbc0696445a91ec681facf4dc47 4e7c7ef0984119447e743e3ec77e1de52713e345cde03fe7df753a35849bed18 FakNcS1Jfe01uljb3-file +#close 2014-12-16-15-30-30 diff --git a/testing/btest/Baseline/scripts.policy.misc.dump-events/all-events-no-args.log b/testing/btest/Baseline/scripts.policy.misc.dump-events/all-events-no-args.log index da79bfd5a4..f5e53044b9 100644 --- a/testing/btest/Baseline/scripts.policy.misc.dump-events/all-events-no-args.log +++ b/testing/btest/Baseline/scripts.policy.misc.dump-events/all-events-no-args.log @@ -68,11 +68,9 @@ 1254722770.692743 get_file_handle 1254722770.692743 file_new 1254722770.692743 file_over_new_connection -1254722770.692786 file_mime_type 1254722770.692804 mime_end_entity 1254722770.692804 get_file_handle -1254722770.692804 file_new -1254722770.692804 file_over_new_connection +1254722770.692804 file_mime_type 1254722770.692804 file_state_remove 1254722770.692804 get_file_handle 1254722770.692804 mime_end_entity @@ -85,10 +83,8 @@ 1254722770.692804 get_file_handle 1254722770.692804 file_new 1254722770.692804 file_over_new_connection -1254722770.692823 file_mime_type 1254722770.695115 new_connection -1254722771.469814 file_new -1254722771.469814 file_over_new_connection +1254722771.494181 file_mime_type 1254722771.858334 mime_end_entity 1254722771.858334 get_file_handle 1254722771.858334 file_state_remove diff --git a/testing/btest/Baseline/scripts.policy.misc.dump-events/all-events.log b/testing/btest/Baseline/scripts.policy.misc.dump-events/all-events.log index c01df6bc41..1aa93d5a04 100644 --- a/testing/btest/Baseline/scripts.policy.misc.dump-events/all-events.log +++ b/testing/btest/Baseline/scripts.policy.misc.dump-events/all-events.log @@ -297,7 +297,7 @@ [2] is_orig: bool = F 1254722770.692743 file_new - [0] f: fa_file = [id=Fel9gs4OtNEV6gUJZ5, parent_id=, source=SMTP, is_orig=F, conns=, last_active=1254722770.692743, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=] + [0] f: fa_file = [id=Fel9gs4OtNEV6gUJZ5, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=1610, state=4, num_pkts=9, num_bytes_ip=518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163697, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=], fuids=[]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=3], socks=, ssh=, syslog=]^J}, last_active=1254722770.692743, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=] 1254722770.692743 file_over_new_connection [0] f: fa_file = [id=Fel9gs4OtNEV6gUJZ5, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=1610, state=4, num_pkts=9, num_bytes_ip=518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163697, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=], fuids=[]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=3], socks=, ssh=, syslog=]^J}, last_active=1254722770.692743, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1254722770.692743, fuid=Fel9gs4OtNEV6gUJZ5, tx_hosts={^J^J}, rx_hosts={^J^J}, conn_uids={^J^J}, source=SMTP, depth=0, analyzers={^J^J}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=] @@ -313,7 +313,7 @@ [2] is_orig: bool = T 1254722770.692743 file_mime_type - [0] f: fa_file = [id=Fel9gs4OtNEV6gUJZ5, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=1610, state=4, num_pkts=9, num_bytes_ip=518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163697, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=, fuids=[Fel9gs4OtNEV6gUJZ5]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=3], socks=, ssh=, syslog=]^J}, last_active=1254722770.692743, seen_bytes=77, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1254722770.692743, fuid=Fel9gs4OtNEV6gUJZ5, tx_hosts={^J^I74.53.140.153^J}, rx_hosts={^J^I10.10.1.4^J}, conn_uids={^J^ICjhGID4nQcgTWjvg4c^J}, source=SMTP, depth=3, analyzers={^J^J}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=] + [0] f: fa_file = [id=Fel9gs4OtNEV6gUJZ5, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=1610, state=4, num_pkts=9, num_bytes_ip=518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163697, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=, fuids=[Fel9gs4OtNEV6gUJZ5]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=3], socks=, ssh=, syslog=]^J}, last_active=1254722770.692743, seen_bytes=77, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=Hello^M^J^M^J ^M^J^M^JI send u smtp pcap file ^M^J^M^JFind the attachment^M^J^M^J ^M^J^M^JGPS^M^J^M^J, info=[ts=1254722770.692743, fuid=Fel9gs4OtNEV6gUJZ5, tx_hosts={^J^I74.53.140.153^J}, rx_hosts={^J^I10.10.1.4^J}, conn_uids={^J^ICjhGID4nQcgTWjvg4c^J}, source=SMTP, depth=3, analyzers={^J^J}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=] [1] mime_type: string = text/plain 1254722770.692743 file_state_remove @@ -341,17 +341,13 @@ [2] is_orig: bool = F 1254722770.692743 file_new - [0] f: fa_file = [id=Ft4M3f2yMvLlmwtbq9, parent_id=, source=SMTP, is_orig=F, conns=, last_active=1254722770.692743, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=] + [0] f: fa_file = [id=Ft4M3f2yMvLlmwtbq9, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=1610, state=4, num_pkts=9, num_bytes_ip=518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163697, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=], fuids=[Fel9gs4OtNEV6gUJZ5]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=4], socks=, ssh=, syslog=]^J}, last_active=1254722770.692743, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=] 1254722770.692743 file_over_new_connection [0] f: fa_file = [id=Ft4M3f2yMvLlmwtbq9, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=1610, state=4, num_pkts=9, num_bytes_ip=518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163697, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=], fuids=[Fel9gs4OtNEV6gUJZ5]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=4], socks=, ssh=, syslog=]^J}, last_active=1254722770.692743, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1254722770.692743, fuid=Ft4M3f2yMvLlmwtbq9, tx_hosts={^J^J}, rx_hosts={^J^J}, conn_uids={^J^J}, source=SMTP, depth=0, analyzers={^J^J}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=] [1] c: connection = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=1610, state=4, num_pkts=9, num_bytes_ip=518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163697, service={^J^ISMTP^J}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^J}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^J}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=], fuids=[Fel9gs4OtNEV6gUJZ5]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=4], socks=, ssh=, syslog=] [2] is_orig: bool = F -1254722770.692786 file_mime_type - [0] f: fa_file = [id=Ft4M3f2yMvLlmwtbq9, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=1610, state=4, num_pkts=9, num_bytes_ip=518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163697, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=4], socks=, ssh=, syslog=]^J}, last_active=1254722770.692786, seen_bytes=1013, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1254722770.692743, fuid=Ft4M3f2yMvLlmwtbq9, tx_hosts={^J^I74.53.140.153^J}, rx_hosts={^J^I10.10.1.4^J}, conn_uids={^J^ICjhGID4nQcgTWjvg4c^J}, source=SMTP, depth=4, analyzers={^J^J}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=] - [1] mime_type: string = text/html - 1254722770.692804 mime_end_entity [0] c: connection = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^ISMTP^J}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^J}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^J}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=4], socks=, ssh=, syslog=] @@ -360,8 +356,12 @@ [1] c: connection = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^ISMTP^J}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^J}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^J}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=, fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=4], socks=, ssh=, syslog=] [2] is_orig: bool = T +1254722770.692804 file_mime_type + [0] f: fa_file = [id=Ft4M3f2yMvLlmwtbq9, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=, fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=4], socks=, ssh=, syslog=]^J}, last_active=1254722770.692804, seen_bytes=1868, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J
^M^J^M^J

Hello

^M^J^M^J

 

^M^J^M^J

I send u smtp pcap file

^M^J^M^J

Find the attachment

^M^J^M^J

 

^M^J^M^J

GPS

^M^J^M^J
^M^J^M^J^M^J^M^J^M^J^M^J, info=[ts=1254722770.692743, fuid=Ft4M3f2yMvLlmwtbq9, tx_hosts={^J^I74.53.140.153^J}, rx_hosts={^J^I10.10.1.4^J}, conn_uids={^J^ICjhGID4nQcgTWjvg4c^J}, source=SMTP, depth=4, analyzers={^J^J}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=] + [1] mime_type: string = text/html + 1254722770.692804 file_state_remove - [0] f: fa_file = [id=Ft4M3f2yMvLlmwtbq9, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=, fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=4], socks=, ssh=, syslog=]^J}, last_active=1254722770.692804, seen_bytes=1868, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J^M^J
^M^J^M^J

Hello

^M^J^M^J

 

^M^J^M^J

I send u smtp pcap file

^M^J^M^J

Find the attachment

^M^J^M^J

 

^M^J^M^J

GPS

^M^J^M^J
^M^J^M^J^M^J^M^J^M^J^M^J, info=[ts=1254722770.692743, fuid=Ft4M3f2yMvLlmwtbq9, tx_hosts={^J^I74.53.140.153^J}, rx_hosts={^J^I10.10.1.4^J}, conn_uids={^J^ICjhGID4nQcgTWjvg4c^J}, source=SMTP, depth=4, analyzers={^J^J}, mime_type=text/html, filename=, duration=61.0 usecs, local_orig=, is_orig=F, seen_bytes=1868, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=] 1254722770.692804 get_file_handle [0] tag: enum = Analyzer::ANALYZER_SMTP @@ -402,20 +402,20 @@ [2] is_orig: bool = F 1254722770.692804 file_new - [0] f: fa_file = [id=FL9Y0d45OI4LpS6fmh, parent_id=, source=SMTP, is_orig=F, conns=, last_active=1254722770.692804, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=] + [0] f: fa_file = [id=FL9Y0d45OI4LpS6fmh, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=NEWS.txt], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=]^J}, last_active=1254722770.692804, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=] 1254722770.692804 file_over_new_connection [0] f: fa_file = [id=FL9Y0d45OI4LpS6fmh, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=NEWS.txt], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=]^J}, last_active=1254722770.692804, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1254722770.692804, fuid=FL9Y0d45OI4LpS6fmh, tx_hosts={^J^J}, rx_hosts={^J^J}, conn_uids={^J^J}, source=SMTP, depth=0, analyzers={^J^J}, mime_type=, filename=, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=] [1] c: connection = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^ISMTP^J}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^J}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^J}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=NEWS.txt], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=] [2] is_orig: bool = F -1254722770.692823 file_mime_type - [0] f: fa_file = [id=FL9Y0d45OI4LpS6fmh, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=NEWS.txt], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9, FL9Y0d45OI4LpS6fmh]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=]^J}, last_active=1254722770.692823, seen_bytes=966, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=[ts=1254722770.692804, fuid=FL9Y0d45OI4LpS6fmh, tx_hosts={^J^I74.53.140.153^J}, rx_hosts={^J^I10.10.1.4^J}, conn_uids={^J^ICjhGID4nQcgTWjvg4c^J}, source=SMTP, depth=5, analyzers={^J^J}, mime_type=, filename=NEWS.txt, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=] - [1] mime_type: string = text/plain - 1254722770.695115 new_connection [0] c: connection = [id=[orig_h=192.168.1.1, orig_p=3/icmp, resp_h=10.10.1.4, resp_p=4/icmp], orig=[size=0, state=0, num_pkts=0, num_bytes_ip=0, flow_label=0], resp=[size=0, state=0, num_pkts=0, num_bytes_ip=0, flow_label=0], start_time=1254722770.695115, duration=0.0, service={^J^J}, addl=, hot=0, history=, uid=CCvvfg3TEfuqmmG4bh, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=] +1254722771.494181 file_mime_type + [0] f: fa_file = [id=FL9Y0d45OI4LpS6fmh, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=4530, state=4, num_pkts=11, num_bytes_ip=3518, flow_label=0], resp=[size=462, state=4, num_pkts=10, num_bytes_ip=870, flow_label=0], start_time=1254722767.529046, duration=3.163758, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=NEWS.txt], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9, FL9Y0d45OI4LpS6fmh]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=]^J}, last_active=1254722771.494181, seen_bytes=4027, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=Version 4.9.9.1^M^J* Many bug fixes^M^J* Improved editor^M^J^M^JVersion 4.9.9.0^M^J* Support for latest Mingw compiler system builds^M^J* Bug fixes^M^J^M^JVersion 4.9.8.9^M^J* New code tooltip display^M^J* Improved Indent/Unindent and Remove Comment^M^J* Improved automatic indent^M^J* Added support for the "interface" keyword^M^J* WebUpdate should now report installation problems from PackMan^M^J* New splash screen and association icons^M^J* Improved installer^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.7^M^J* Added support for GCC > 3.2^M^J* Debug variables are now resent during next debug session^M^J* Watched Variables not in correct context are now kept and updated when it is needed^M^J* Added new compiler/linker options: ^M^J - Strip executable^M^J - Generate instructions for a specific machine (i386, i486, i586, i686, pentium, pentium-mmx, pentiumpro, pentium2, pentium3, pentium4, ^M^J k6, k6-2, k6-3, athlon, athlon-tbird, athlon-4, athlon-xp, athlon-mp, winchip-c6, winchip2, k8, c3 and c3-2)^M^J - Enable use of processor specific built-in functions (mmmx, sse, sse2, pni, 3dnow)^M^J* "Default" button in Compiler Options is back^M^J* Error messages parsing improved^M^J* Bug fixes^M^J^M^JVersion 4.9.8.5^M^J* Added the possibility to modify the value of a variable during debugging (right click on a watch variable and select "Modify value")^M^J* During Dev-C++ First Time COnfiguration window, users can now choose between using or not class browser and code completion features.^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.4^M^J* Added the possibility to specify an include directory for the code completion cache to be created at Dev-C++ first startup^M^J* Improved code completion cache^M^J* WebUpdate will now backup downloaded DevPaks in Dev-C++\Packages directory, and Dev-C++ executable in devcpp.exe.BACKUP^M^J* Big speed up in function parameters listing while editing^M^J* Bug fixes^M^J^M^JVersion 4.9.8.3^M^J* On Dev-C++ first time configuration dialog, a code completion cache of all the standard ^M^J include files can now be generated.^M^J* Improved WebUpdate module^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.2^M^J* New debug feature for DLLs: attach to a running process^M^J* New project option: Use custom Makefile. ^M^J* New WebUpdater module.^M^J* Allow user to specify an alternate configuration file in Environment Options ^M^J (still can be overriden by using "-c" command line parameter).^M^J* Lots of bug fixes.^M^J^M^JVersion 4.9.8.1^M^J* When creating a DLL, the created static lib respects now the project-defined output directory^M^J^M^JVersion 4.9.8.0^M^J* Changed position of compiler/linker parameters in Project Options.^M^J* Improved help file^M^J* Bug fixes^M^J^M^JVersion 4.9.7.9^M^J* Resource errors are now reported in the Resource sheet^M^J* Many bug fixes^M^J^M^JVersion 4.9.7.8^M^J* Made whole bottom report control floating instead of only debug output.^M^J* Many bug fixes^M^J^M^JVersion 4.9.7.7^M^J* Printing settings are now saved^M^J* New environment options : "watch variable under mouse" and "Report watch errors"^M^J* Bug fixes^M^J^M^JVersion 4.9.7.6^M^J* Debug variable browser^M^J* Added possibility to include in a Template the Project's directories (include, libs and ressources)^M^J* Changed tint of Class browser pictures colors to match the New Look style^M^J* Bug fixes^M^J^M^JVersion 4.9.7.5^M^J* Bug fixes^M^J^M^JVersion 4.9.7.4^M^J* When compiling with debugging symbols, an extra definition is passed to the^M^J compiler: -D__DEBUG__^M^J* Each project creates a _private.h file containing version^M^J information definitions^M^J* When compiling the current file only, no dependency checks are performed^M^J* ~300% Speed-up in class parser^M^J* Added "External programs" in Tools/Environment Options (for units "Open with")^M^J* Added "Open with" in project units context menu^M^J* Added "Classes" toolbar^M^J* Fixed pre-compilation dependency checks to work correctly^M^J* Added new file menu entry: Save Project As^M^J* Bug-fix for double quotes in devcpp.cfg file read by vUpdate^M^J* Other bug fixes^M^J^M^JVersion 4.9.7.3^M^J* When adding debugging symbols on request, remove "-s" option from linker^M^J* Compiling progress window^M^J* Environment options : "Show progress window" and "Auto-close progress , info=[ts=1254722770.692804, fuid=FL9Y0d45OI4LpS6fmh, tx_hosts={^J^I74.53.140.153^J}, rx_hosts={^J^I10.10.1.4^J}, conn_uids={^J^ICjhGID4nQcgTWjvg4c^J}, source=SMTP, depth=5, analyzers={^J^J}, mime_type=, filename=NEWS.txt, duration=0 secs, local_orig=, is_orig=F, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=] + [1] mime_type: string = text/plain + 1254722771.858334 mime_end_entity [0] c: connection = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=14699, state=4, num_pkts=23, num_bytes_ip=21438, flow_label=0], resp=[size=462, state=4, num_pkts=15, num_bytes_ip=1070, flow_label=0], start_time=1254722767.529046, duration=4.329288, service={^J^ISMTP^J}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^J}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^J}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=[filename=NEWS.txt], fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9, FL9Y0d45OI4LpS6fmh]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=] @@ -425,7 +425,7 @@ [2] is_orig: bool = T 1254722771.858334 file_state_remove - [0] f: fa_file = [id=FL9Y0d45OI4LpS6fmh, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=14699, state=4, num_pkts=23, num_bytes_ip=21438, flow_label=0], resp=[size=462, state=4, num_pkts=15, num_bytes_ip=1070, flow_label=0], start_time=1254722767.529046, duration=4.329288, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=, fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9, FL9Y0d45OI4LpS6fmh]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=]^J}, last_active=1254722771.858316, seen_bytes=10809, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=Version 4.9.9.1^M^J* Many bug fixes^M^J* Improved editor^M^J^M^JVersion 4.9.9.0^M^J* Support for latest Mingw compiler system builds^M^J* Bug fixes^M^J^M^JVersion 4.9.8.9^M^J* New code tooltip display^M^J* Improved Indent/Unindent and Remove Comment^M^J* Improved automatic indent^M^J* Added support for the "interface" keyword^M^J* WebUpdate should now report installation problems from PackMan^M^J* New splash screen and association icons^M^J* Improved installer^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.7^M^J* Added support for GCC > 3.2^M^J* Debug variables are now resent during next debug session^M^J* Watched Variables not in correct context are now kept and updated when it is needed^M^J* Added new compiler/linker options: ^M^J - Strip executable^M^J - Generate instructions for a specific machine (i386, i486, i586, i686, pentium, pentium-mmx, pentiumpro, pentium2, pentium3, pentium4, ^M^J k6, k6-2, k6-3, athlon, athlon-tbird, athlon-4, athlon-xp, athlon-mp, winchip-c6, winchip2, k8, c3 and c3-2)^M^J - Enable use of processor specific built-in functions (mmmx, sse, , info=[ts=1254722770.692804, fuid=FL9Y0d45OI4LpS6fmh, tx_hosts={^J^I74.53.140.153^J}, rx_hosts={^J^I10.10.1.4^J}, conn_uids={^J^ICjhGID4nQcgTWjvg4c^J}, source=SMTP, depth=5, analyzers={^J^J}, mime_type=text/plain, filename=NEWS.txt, duration=18.0 usecs, local_orig=, is_orig=F, seen_bytes=966, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=] + [0] f: fa_file = [id=FL9Y0d45OI4LpS6fmh, parent_id=, source=SMTP, is_orig=F, conns={^J^I[[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp]] = [id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], orig=[size=14699, state=4, num_pkts=23, num_bytes_ip=21438, flow_label=0], resp=[size=462, state=4, num_pkts=15, num_bytes_ip=1070, flow_label=0], start_time=1254722767.529046, duration=4.329288, service={^J^I^ISMTP^J^I}, addl=, hot=0, history=ShAdDa, uid=CjhGID4nQcgTWjvg4c, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=[ts=1254722768.219663, uid=CjhGID4nQcgTWjvg4c, id=[orig_h=10.10.1.4, orig_p=1470/tcp, resp_h=74.53.140.153, resp_p=25/tcp], trans_depth=1, helo=GP, mailfrom=, rcptto={^J^I^I^J^I}, date=Mon, 5 Oct 2009 11:36:07 +0530, from="Gurpartap Singh" , to={^J^I^I^J^I}, reply_to=, msg_id=<000301ca4581$ef9e57f0$cedb07d0$@in>, in_reply_to=, subject=SMTP, x_originating_ip=, first_received=, second_received=, last_reply=354 Enter message, ending with "." on a line by itself, path=[74.53.140.153, 10.10.1.4], user_agent=Microsoft Office Outlook 12.0, tls=F, process_received_from=T, has_client_activity=T, entity=, fuids=[Fel9gs4OtNEV6gUJZ5, Ft4M3f2yMvLlmwtbq9, FL9Y0d45OI4LpS6fmh]], smtp_state=[helo=GP, messages_transferred=0, pending_messages=, mime_depth=5], socks=, ssh=, syslog=]^J}, last_active=1254722771.858316, seen_bytes=10809, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=Version 4.9.9.1^M^J* Many bug fixes^M^J* Improved editor^M^J^M^JVersion 4.9.9.0^M^J* Support for latest Mingw compiler system builds^M^J* Bug fixes^M^J^M^JVersion 4.9.8.9^M^J* New code tooltip display^M^J* Improved Indent/Unindent and Remove Comment^M^J* Improved automatic indent^M^J* Added support for the "interface" keyword^M^J* WebUpdate should now report installation problems from PackMan^M^J* New splash screen and association icons^M^J* Improved installer^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.7^M^J* Added support for GCC > 3.2^M^J* Debug variables are now resent during next debug session^M^J* Watched Variables not in correct context are now kept and updated when it is needed^M^J* Added new compiler/linker options: ^M^J - Strip executable^M^J - Generate instructions for a specific machine (i386, i486, i586, i686, pentium, pentium-mmx, pentiumpro, pentium2, pentium3, pentium4, ^M^J k6, k6-2, k6-3, athlon, athlon-tbird, athlon-4, athlon-xp, athlon-mp, winchip-c6, winchip2, k8, c3 and c3-2)^M^J - Enable use of processor specific built-in functions (mmmx, sse, sse2, pni, 3dnow)^M^J* "Default" button in Compiler Options is back^M^J* Error messages parsing improved^M^J* Bug fixes^M^J^M^JVersion 4.9.8.5^M^J* Added the possibility to modify the value of a variable during debugging (right click on a watch variable and select "Modify value")^M^J* During Dev-C++ First Time COnfiguration window, users can now choose between using or not class browser and code completion features.^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.4^M^J* Added the possibility to specify an include directory for the code completion cache to be created at Dev-C++ first startup^M^J* Improved code completion cache^M^J* WebUpdate will now backup downloaded DevPaks in Dev-C++\Packages directory, and Dev-C++ executable in devcpp.exe.BACKUP^M^J* Big speed up in function parameters listing while editing^M^J* Bug fixes^M^J^M^JVersion 4.9.8.3^M^J* On Dev-C++ first time configuration dialog, a code completion cache of all the standard ^M^J include files can now be generated.^M^J* Improved WebUpdate module^M^J* Many bug fixes^M^J^M^JVersion 4.9.8.2^M^J* New debug feature for DLLs: attach to a running process^M^J* New project option: Use custom Makefile. ^M^J* New WebUpdater module.^M^J* Allow user to specify an alternate configuration file in Environment Options ^M^J (still can be overriden by using "-c" command line parameter).^M^J* Lots of bug fixes.^M^J^M^JVersion 4.9.8.1^M^J* When creating a DLL, the created static lib respects now the project-defined output directory^M^J^M^JVersion 4.9.8.0^M^J* Changed position of compiler/linker parameters in Project Options.^M^J* Improved help file^M^J* Bug fixes^M^J^M^JVersion 4.9.7.9^M^J* Resource errors are now reported in the Resource sheet^M^J* Many bug fixes^M^J^M^JVersion 4.9.7.8^M^J* Made whole bottom report control floating instead of only debug output.^M^J* Many bug fixes^M^J^M^JVersion 4.9.7.7^M^J* Printing settings are now saved^M^J* New environment options : "watch variable under mouse" and "Report watch errors"^M^J* Bug fixes^M^J^M^JVersion 4.9.7.6^M^J* Debug variable browser^M^J* Added possibility to include in a Template the Project's directories (include, libs and ressources)^M^J* Changed tint of Class browser pictures colors to match the New Look style^M^J* Bug fixes^M^J^M^JVersion 4.9.7.5^M^J* Bug fixes^M^J^M^JVersion 4.9.7.4^M^J* When compiling with debugging symbols, an extra definition is passed to the^M^J compiler: -D__DEBUG__^M^J* Each project creates a _private.h file containing version^M^J information definitions^M^J* When compiling the current file only, no dependency checks are performed^M^J* ~300% Speed-up in class parser^M^J* Added "External programs" in Tools/Environment Options (for units "Open with")^M^J* Added "Open with" in project units context menu^M^J* Added "Classes" toolbar^M^J* Fixed pre-compilation dependency checks to work correctly^M^J* Added new file menu entry: Save Project As^M^J* Bug-fix for double quotes in devcpp.cfg file read by vUpdate^M^J* Other bug fixes^M^J^M^JVersion 4.9.7.3^M^J* When adding debugging symbols on request, remove "-s" option from linker^M^J* Compiling progress window^M^J* Environment options : "Show progress window" and "Auto-close progress , info=[ts=1254722770.692804, fuid=FL9Y0d45OI4LpS6fmh, tx_hosts={^J^I74.53.140.153^J}, rx_hosts={^J^I10.10.1.4^J}, conn_uids={^J^ICjhGID4nQcgTWjvg4c^J}, source=SMTP, depth=5, analyzers={^J^J}, mime_type=text/plain, filename=NEWS.txt, duration=801.0 msecs 376.0 usecs, local_orig=, is_orig=F, seen_bytes=4027, total_bytes=, missing_bytes=0, overflow_bytes=0, timedout=F, parent_fuid=, md5=, sha1=, sha256=, x509=, extracted=], ftp=, http=, irc=, u2_events=] 1254722771.858334 get_file_handle [0] tag: enum = Analyzer::ANALYZER_SMTP From f6257618e579400150787bbf8c52f240be86b8bb Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 16 Dec 2014 20:56:15 -0600 Subject: [PATCH 057/299] Change file extraction to explicitly NUL-fill gaps Instead of expecting pwrite to do it. --- src/file_analysis/analyzer/extract/Extract.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/file_analysis/analyzer/extract/Extract.cc b/src/file_analysis/analyzer/extract/Extract.cc index eeec8ef464..c758414a6e 100644 --- a/src/file_analysis/analyzer/extract/Extract.cc +++ b/src/file_analysis/analyzer/extract/Extract.cc @@ -103,7 +103,7 @@ bool Extract::DeliverStream(const u_char* data, uint64 len) if ( towrite > 0 ) { - safe_pwrite(fd, (const u_char *) data, towrite, depth); + safe_write(fd, reinterpret_cast(data), towrite); depth += towrite; } @@ -112,6 +112,13 @@ bool Extract::DeliverStream(const u_char* data, uint64 len) bool Extract::Undelivered(uint64 offset, uint64 len) { - depth += len; + if ( depth == offset ) + { + char* tmp = new char[len](); + safe_write(fd, tmp, len); + delete [] tmp; + depth += len; + } + return true; } From 6941538f8168fc7c25607e9d4019bac37006e277 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 16 Dec 2014 20:58:27 -0600 Subject: [PATCH 058/299] Fix reference counting bug in refactored file reassembly code. --- src/file_analysis/File.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/file_analysis/File.cc b/src/file_analysis/File.cc index d893d7a088..50617f27b6 100644 --- a/src/file_analysis/File.cc +++ b/src/file_analysis/File.cc @@ -147,7 +147,7 @@ void File::RaiseFileOverNewConnection(Connection* conn, bool is_orig) { val_list* vl = new val_list(); vl->append(val->Ref()); - vl->append(conn->BuildConnVal()->Ref()); + vl->append(conn->BuildConnVal()); vl->append(new Val(is_orig, TYPE_BOOL)); FileEvent(file_over_new_connection, vl); } From 1a03a95f355bcc8e68aa096b074714a879fac902 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 17 Dec 2014 09:57:06 -0600 Subject: [PATCH 059/299] Workaround race condition in unified2 file module. This makes the unit test pass consistently, but need to see about fixing it in the unified2 file module directly. --- .../scripts/base/files/unified2/alert.bro | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/testing/btest/scripts/base/files/unified2/alert.bro b/testing/btest/scripts/base/files/unified2/alert.bro index eca1ca036c..189e35bd8e 100644 --- a/testing/btest/scripts/base/files/unified2/alert.bro +++ b/testing/btest/scripts/base/files/unified2/alert.bro @@ -1,4 +1,4 @@ -# @TEST-EXEC: bro -b %INPUT Unified2::watch_file=$FILES/unified2.u2 +# @TEST-EXEC: bro -b %INPUT test_watch_file=$FILES/unified2.u2 # @TEST-EXEC: btest-diff unified2.log @TEST-START-FILE sid_msg.map @@ -68,9 +68,39 @@ redef Unified2::gen_msg = @DIR+"/gen_msg.map"; redef Unified2::classification_config = @DIR+"/classification.config"; global i = 0; +# TODO: can't currently use Unified2::watch_file directly for the test as +# there's a race between reading that file and the map/classification +# config files, which leads to not all fields of the unified2.log being +# populated on occassion. +const test_watch_file: string = "" &redef; + +event start_test() + { + Input::add_analysis([$source=test_watch_file, + $reader=Input::READER_BINARY, + $mode=Input::STREAM, + $name=test_watch_file]); + } + +# TODO: this should be handled by unified2 module, but it's here for +# working around the issue mentioned in comment above. +event file_new(f: fa_file) + { + if ( f$source == test_watch_file ) + { + Files::add_analyzer(f, Files::ANALYZER_UNIFIED2); + f$u2_events = table(); + } + } + +event bro_init() + { + schedule 2sec { start_test() }; + } + event Unified2::alert(f: fa_file, ev: Unified2::IDSEvent, pkt: Unified2::Packet) { ++i; if ( i == 2 ) terminate(); - } \ No newline at end of file + } From 15ec117da678d8df54d018b93f9460dea782700b Mon Sep 17 00:00:00 2001 From: Daniel Thayer Date: Thu, 18 Dec 2014 11:57:32 -0600 Subject: [PATCH 060/299] Correct a typo in the Notice framework doc --- doc/frameworks/notice.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/frameworks/notice.rst b/doc/frameworks/notice.rst index 2c20149ce5..d8197c13af 100644 --- a/doc/frameworks/notice.rst +++ b/doc/frameworks/notice.rst @@ -271,7 +271,7 @@ script that is generating the notice has indicated to the notice framework how to identify notices that are intrinsically the same. Identification of these "intrinsically duplicate" notices is implemented with an optional field in :bro:see:`Notice::Info` records named ``$identifier`` which is a simple string. -If the ``$identifier`` and ``$type`` fields are the same for two notices, the +If the ``$identifier`` and ``$note`` fields are the same for two notices, the notice framework actually considers them to be the same thing and can use that information to suppress duplicates for a configurable period of time. From 3ed6dd558518c2de64c83c3936f8df88b6583392 Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Sat, 27 Dec 2014 17:19:43 -0600 Subject: [PATCH 061/299] A bit of code cleanup. --- src/analyzer/protocol/ssh/ssh-analyzer.pac | 55 ++++---- src/analyzer/protocol/ssh/ssh-protocol.pac | 157 +++++++++------------ 2 files changed, 99 insertions(+), 113 deletions(-) diff --git a/src/analyzer/protocol/ssh/ssh-analyzer.pac b/src/analyzer/protocol/ssh/ssh-analyzer.pac index e4dd71bc39..da940fffed 100644 --- a/src/analyzer/protocol/ssh/ssh-analyzer.pac +++ b/src/analyzer/protocol/ssh/ssh-analyzer.pac @@ -1,36 +1,49 @@ -# Generated by binpac_quickstart - refine flow SSH_Flow += { function proc_ssh_version(msg: SSH_Version): bool %{ if ( ssh_client_version && ${msg.is_orig } ) - BifEvent::generate_ssh_client_version(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), bytestring_to_val(${msg.version})); - else if ( ssh_server_version ) - BifEvent::generate_ssh_server_version(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), bytestring_to_val(${msg.version})); + { + BifEvent::generate_ssh_client_version(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${msg.version})); + } + else if ( ssh_server_version ) + { + BifEvent::generate_ssh_server_version(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${msg.version})); + } return true; %} function proc_ssh_kexinit(msg: SSH_KEXINIT): bool %{ if ( ssh_server_capabilities ) - BifEvent::generate_ssh_server_capabilities(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), - bytestring_to_val(${msg.kex_algorithms}), bytestring_to_val(${msg.server_host_key_algorithms}), - bytestring_to_val(${msg.encryption_algorithms_client_to_server}), - bytestring_to_val(${msg.encryption_algorithms_server_to_client}), - bytestring_to_val(${msg.mac_algorithms_client_to_server}), - bytestring_to_val(${msg.mac_algorithms_server_to_client}), - bytestring_to_val(${msg.compression_algorithms_client_to_server}), - bytestring_to_val(${msg.compression_algorithms_server_to_client}), - bytestring_to_val(${msg.languages_client_to_server}), - bytestring_to_val(${msg.languages_server_to_client})); + { + BifEvent::generate_ssh_server_capabilities(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${msg.kex_algorithms.val}), + bytestring_to_val(${msg.server_host_key_algorithms.val}), + bytestring_to_val(${msg.encryption_algorithms_client_to_server.val}), + bytestring_to_val(${msg.encryption_algorithms_server_to_client.val}), + bytestring_to_val(${msg.mac_algorithms_client_to_server.val}), + bytestring_to_val(${msg.mac_algorithms_server_to_client.val}), + bytestring_to_val(${msg.compression_algorithms_client_to_server.val}), + bytestring_to_val(${msg.compression_algorithms_server_to_client.val}), + bytestring_to_val(${msg.languages_client_to_server.val}), + bytestring_to_val(${msg.languages_server_to_client.val})); + } return true; %} function proc_ssh_server_host_key(key: bytestring): bool %{ if ( ssh_server_host_key ) - BifEvent::generate_ssh_server_host_key(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), - bytestring_to_val(${key})); + { + BifEvent::generate_ssh_server_host_key(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${key})); + } return true; %} @@ -40,12 +53,6 @@ refine flow SSH_Flow += { return true; %} - function debug(loc: uint8): bool - %{ - printf("DEBUG: %d", loc); - return true; - %} - }; refine typeattr SSH_Version += &let { @@ -61,5 +68,5 @@ refine typeattr SSH_DH_GEX_REPLY += &let { }; refine typeattr SSH_Message += &let { - proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == SSH_MSG_NEWKEYS); + proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == SSH2_MSG_NEWKEYS); }; \ No newline at end of file diff --git a/src/analyzer/protocol/ssh/ssh-protocol.pac b/src/analyzer/protocol/ssh/ssh-protocol.pac index 03f47fd67a..d14af0a663 100644 --- a/src/analyzer/protocol/ssh/ssh-protocol.pac +++ b/src/analyzer/protocol/ssh/ssh-protocol.pac @@ -5,43 +5,43 @@ enum state { }; enum message_id { - SSH_MSG_DISCONNECT = 1, - SSH_MSG_IGNORE = 2, - SSH_MSG_UNIMPLEMENTED = 3, - SSH_MSG_DEBUG = 4, - SSH_MSG_SERVICE_REQUEST = 5, - SSH_MSG_SERVICE_ACCEPT = 6, - SSH_MSG_KEXINIT = 20, - SSH_MSG_NEWKEYS = 21, - SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30, - SSH_MSG_KEX_DH_GEX_GROUP = 31, - SSH_MSG_KEX_DH_GEX_INIT = 32, - SSH_MSG_KEX_DH_GEX_REPLY = 33, - SSH_MSG_KEX_DH_GEX_REQUEST = 34, - SSH_MSG_USERAUTH_REQUEST = 50, - SSH_MSG_USERAUTH_FAILURE = 51, - SSH_MSG_USERAUTH_SUCCESS = 52, - SSH_MSG_USERAUTH_BANNER = 53, - SSH_MSG_GLOBAL_REQUEST = 80, - SSH_MSG_REQUEST_SUCCESS = 81, - SSH_MSG_REQUEST_FAILURE = 82, - SSH_MSG_CHANNEL_OPEN = 90, - SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91, - SSH_MSG_CHANNEL_OPEN_FAILURE = 92, - SSH_MSG_CHANNEL_WINDOW_ADJUST = 93, - SSH_MSG_CHANNEL_DATA = 94, - SSH_MSG_CHANNEL_EXTENDED_DATA = 95, - SSH_MSG_CHANNEL_EOF = 96, - SSH_MSG_CHANNEL_CLOSE = 97, - SSH_MSG_CHANNEL_REQUEST = 98, - SSH_MSG_CHANNEL_SUCCESS = 99, - SSH_MSG_CHANNEL_FAILURE = 100, + SSH2_MSG_DISCONNECT = 1, + SSH2_MSG_IGNORE = 2, + SSH2_MSG_UNIMPLEMENTED = 3, + SSH2_MSG_DEBUG = 4, + SSH2_MSG_SERVICE_REQUEST = 5, + SSH2_MSG_SERVICE_ACCEPT = 6, + SSH2_MSG_KEXINIT = 20, + SSH2_MSG_NEWKEYS = 21, + SSH2_MSG_KEX_DH_GEX_REQUEST_OLD = 30, + SSH2_MSG_KEX_DH_GEX_GROUP = 31, + SSH2_MSG_KEX_DH_GEX_INIT = 32, + SSH2_MSG_KEX_DH_GEX_REPLY = 33, + SSH2_MSG_KEX_DH_GEX_REQUEST = 34, + SSH2_MSG_USERAUTH_REQUEST = 50, + SSH2_MSG_USERAUTH_FAILURE = 51, + SSH2_MSG_USERAUTH_SUCCESS = 52, + SSH2_MSG_USERAUTH_BANNER = 53, + SSH2_MSG_GLOBAL_REQUEST = 80, + SSH2_MSG_REQUEST_SUCCESS = 81, + SSH2_MSG_REQUEST_FAILURE = 82, + SSH2_MSG_CHANNEL_OPEN = 90, + SSH2_MSG_CHANNEL_OPEN_CONFIRMATION = 91, + SSH2_MSG_CHANNEL_OPEN_FAILURE = 92, + SSH2_MSG_CHANNEL_WINDOW_ADJUST = 93, + SSH2_MSG_CHANNEL_DATA = 94, + SSH2_MSG_CHANNEL_EXTENDED_DATA = 95, + SSH2_MSG_CHANNEL_EOF = 96, + SSH2_MSG_CHANNEL_CLOSE = 97, + SSH2_MSG_CHANNEL_REQUEST = 98, + SSH2_MSG_CHANNEL_SUCCESS = 99, + SSH2_MSG_CHANNEL_FAILURE = 100, }; type SSH_PDU(is_orig: bool) = case $context.connection.get_state(is_orig) of { - VERSION_EXCHANGE -> version: SSH_Version(is_orig); - KEY_EXCHANGE_CLEARTEXT -> kex: SSH_Key_Exchange(is_orig); - ENCRYPTED -> ciphertext: bytestring &length=1 &transient; + VERSION_EXCHANGE -> version: SSH_Version(is_orig); + KEY_EXCHANGE_CLEARTEXT -> kex: SSH_Key_Exchange(is_orig); + ENCRYPTED -> ciphertext: bytestring &length=1 &transient; } &byteorder=bigendian; type SSH_Version(is_orig: bool) = record { @@ -51,7 +51,7 @@ type SSH_Version(is_orig: bool) = record { }; type SSH_Key_Exchange_Header(is_orig: bool) = record { - packet_length: uint32; + packet_length : uint32; padding_length: uint8; } &length=5; @@ -61,89 +61,68 @@ type SSH_Key_Exchange(is_orig: bool) = record { pad : bytestring &length=header.padding_length; }; -type SSH_Payload_Header(length: uint32) = record { +type SSH_Payload_Header = record { message_type: uint8; } &length=1; type SSH_Payload(is_orig: bool, packet_length: uint32) = record { - header: SSH_Payload_Header(packet_length); + header: SSH_Payload_Header; message: SSH_Message(is_orig, header.message_type, packet_length); }; type SSH_Message(is_orig: bool, msg_type: uint8, packet_length: uint32) = case msg_type of { - SSH_MSG_KEXINIT -> kexinit: SSH_KEXINIT(is_orig, packet_length); - SSH_MSG_KEX_DH_GEX_REQUEST -> dh_gex_request: SSH_DH_GEX_REQUEST(is_orig, packet_length); - SSH_MSG_KEX_DH_GEX_REQUEST_OLD -> dh_gex_request_old: SSH_DH_GEX_REQUEST_OLD(is_orig, packet_length); - SSH_MSG_KEX_DH_GEX_GROUP -> dh_gex_group: SSH_DH_GEX_GROUP(is_orig, packet_length); - SSH_MSG_KEX_DH_GEX_INIT -> dh_gex_init: SSH_DH_GEX_INIT(is_orig, packet_length); - SSH_MSG_KEX_DH_GEX_REPLY -> dh_gex_reply: SSH_DH_GEX_REPLY(is_orig, packet_length); - SSH_MSG_NEWKEYS -> new_keys: bytestring &length=packet_length; + SSH2_MSG_KEXINIT -> kexinit: SSH_KEXINIT(packet_length); + SSH2_MSG_KEX_DH_GEX_REQUEST -> dh_gex_request: SSH_DH_GEX_REQUEST(packet_length); + SSH2_MSG_KEX_DH_GEX_REQUEST_OLD -> dh_gex_request_old: SSH_DH_GEX_REQUEST_OLD(packet_length); + SSH2_MSG_KEX_DH_GEX_GROUP -> dh_gex_group: SSH_DH_GEX_GROUP(packet_length); + SSH2_MSG_KEX_DH_GEX_INIT -> dh_gex_init: SSH_DH_GEX_INIT(packet_length); + SSH2_MSG_KEX_DH_GEX_REPLY -> dh_gex_reply: SSH_DH_GEX_REPLY(packet_length); + SSH2_MSG_NEWKEYS -> new_keys: bytestring &length=packet_length; } &let { - detach: bool = $context.connection.update_state(ENCRYPTED, is_orig) &if(msg_type == SSH_MSG_NEWKEYS); + detach: bool = $context.connection.update_state(ENCRYPTED, is_orig) &if(msg_type == SSH2_MSG_NEWKEYS); }; -type SSH_KEXINIT(is_orig: bool, length: uint32) = record { - cookie : bytestring &length=16; - kex_algorithms_len : uint32; - kex_algorithms : bytestring &length=kex_algorithms_len; - server_host_key_algorithms_len : uint32; - server_host_key_algorithms : bytestring &length=server_host_key_algorithms_len; - encryption_algorithms_client_to_server_len : uint32; - encryption_algorithms_client_to_server : bytestring &length=encryption_algorithms_client_to_server_len; - encryption_algorithms_server_to_client_len : uint32; - encryption_algorithms_server_to_client : bytestring &length=encryption_algorithms_server_to_client_len; - mac_algorithms_client_to_server_len : uint32; - mac_algorithms_client_to_server : bytestring &length=mac_algorithms_client_to_server_len; - mac_algorithms_server_to_client_len : uint32; - mac_algorithms_server_to_client : bytestring &length=mac_algorithms_server_to_client_len; - compression_algorithms_client_to_server_len : uint32; - compression_algorithms_client_to_server : bytestring &length=compression_algorithms_client_to_server_len; - compression_algorithms_server_to_client_len : uint32; - compression_algorithms_server_to_client : bytestring &length=compression_algorithms_server_to_client_len; - languages_client_to_server_len : uint32; - languages_client_to_server : bytestring &length=languages_client_to_server_len; - languages_server_to_client_len : uint32; - languages_server_to_client : bytestring &length=languages_server_to_client_len; - first_kex_packet_follows : uint8; - reserved : uint32; +type SSH_KEXINIT(length: uint32) = record { + cookie : bytestring &length=16; + kex_algorithms : ssh_string; + server_host_key_algorithms : ssh_string; + encryption_algorithms_client_to_server : ssh_string; + encryption_algorithms_server_to_client : ssh_string; + mac_algorithms_client_to_server : ssh_string; + mac_algorithms_server_to_client : ssh_string; + compression_algorithms_client_to_server : ssh_string; + compression_algorithms_server_to_client : ssh_string; + languages_client_to_server : ssh_string; + languages_server_to_client : ssh_string; + first_kex_packet_follows : uint8; + reserved : uint32; } &length=length; -type SSH_DH_GEX_REQUEST(is_orig: bool, length: uint32) = record { +type SSH_DH_GEX_REQUEST(length: uint32) = record { min: uint32; n : uint32; max: uint32; } &length=12; -type SSH_DH_GEX_REQUEST_OLD(is_orig: bool, length: uint32) = record { +type SSH_DH_GEX_REQUEST_OLD(length: uint32) = record { payload: bytestring &length=length; } &length=length; -type SSH_DH_GEX_GROUP(is_orig: bool, length: uint32) = record { - p: mpint; - g: mpint; +type SSH_DH_GEX_GROUP(length: uint32) = record { + p: ssh_string; + g: ssh_string; } &length=length; -type SSH_DH_GEX_INIT(is_orig: bool, length: uint32) = record { - e: mpint; +type SSH_DH_GEX_INIT(length: uint32) = record { + e: ssh_string; } &length=length; -type SSH_DH_GEX_REPLY(is_orig: bool, length: uint32) = record { +type SSH_DH_GEX_REPLY(length: uint32) = record { k_s : ssh_string; - f : mpint; + f : ssh_string; signature: ssh_string; } &length=length; -#type SSH_NEWKEYS(is_orig: bool, length: uint32) = record { -# blah: ; -#} &let { -# detach: bool = $context.connection.detach(); -#} &length=0; - -type mpint = record { - len: uint32; - val: bytestring &length=len; -}; - type ssh_string = record { len: uint32; val: bytestring &length=len; From 727eada9ac6e92679d5d2bfd3a5ded6c97633051 Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Sat, 27 Dec 2014 17:46:42 -0600 Subject: [PATCH 062/299] Move SSH analyzer to new plugin architecture. --- src/analyzer/protocol/ssh/Plugin.cc | 7 ++++++- src/analyzer/protocol/ssh/SSH.cc | 4 ++-- src/analyzer/protocol/ssh/SSH.h | 21 +++++++-------------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/analyzer/protocol/ssh/Plugin.cc b/src/analyzer/protocol/ssh/Plugin.cc index 5887b7a2c6..d2e0116b09 100644 --- a/src/analyzer/protocol/ssh/Plugin.cc +++ b/src/analyzer/protocol/ssh/Plugin.cc @@ -1,5 +1,10 @@ // See the file in the main distribution directory for copyright. + +#include "plugin/Plugin.h" + +#include "SSH.h" + namespace plugin { namespace Bro_SSH { @@ -7,7 +12,7 @@ class Plugin : public plugin::Plugin { public: plugin::Configuration Configure() { - AddComponent(new ::analyzer::Component("SSH", ::analyzer::ssh::SSH_Analyzer::Instantiate)); + AddComponent(new ::analyzer::Component("SSH", ::analyzer::SSH::SSH_Analyzer::Instantiate)); plugin::Configuration config; config.name = "Bro::SSH"; diff --git a/src/analyzer/protocol/ssh/SSH.cc b/src/analyzer/protocol/ssh/SSH.cc index c6ac74f5dc..184dbba914 100644 --- a/src/analyzer/protocol/ssh/SSH.cc +++ b/src/analyzer/protocol/ssh/SSH.cc @@ -1,4 +1,4 @@ -// Generated by binpac_quickstart +// See the file "COPYING" in the main distribution directory for copyright. #include "SSH.h" @@ -72,7 +72,7 @@ void SSH_Analyzer::DeliverStream(int len, const u_char* data, bool orig) } } -void SSH_Analyzer::Undelivered(int seq, int len, bool orig) +void SSH_Analyzer::Undelivered(uint64 seq, int len, bool orig) { tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig); had_gap = true; diff --git a/src/analyzer/protocol/ssh/SSH.h b/src/analyzer/protocol/ssh/SSH.h index 4d587b4426..73a5420166 100644 --- a/src/analyzer/protocol/ssh/SSH.h +++ b/src/analyzer/protocol/ssh/SSH.h @@ -1,18 +1,16 @@ +// See the file "COPYING" in the main distribution directory for copyright. + #ifndef ANALYZER_PROTOCOL_SSH_SSH_H #define ANALYZER_PROTOCOL_SSH_SSH_H #include "events.bif.h" - #include "analyzer/protocol/tcp/TCP.h" - #include "ssh_pac.h" namespace analyzer { namespace SSH { -class SSH_Analyzer - -: public tcp::TCP_ApplicationAnalyzer { +class SSH_Analyzer : public tcp::TCP_ApplicationAnalyzer { public: SSH_Analyzer(Connection* conn); @@ -20,20 +18,15 @@ public: // Overriden from Analyzer. virtual void Done(); - virtual void DeliverStream(int len, const u_char* data, bool orig); - virtual void Undelivered(int seq, int len, bool orig); + virtual void Undelivered(uint64 seq, int len, bool orig); + + // Overriden from tcp::TCP_ApplicationAnalyzer. + virtual void EndpointEOF(bool is_orig); static analyzer::Analyzer* Instantiate(Connection* conn) { return new SSH_Analyzer(conn); } - static bool Available() - { - return ( ssh_server_version || ssh_client_version || - ssh_auth_successful || ssh_auth_failed || - ssh_server_capabilities || ssh_server_host_key ); - } - protected: binpac::SSH::SSH_Conn* interp; From 9af5fb1302239a5b88e0ce35857f7005f4e48fb0 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Wed, 31 Dec 2014 09:14:55 -0800 Subject: [PATCH 063/299] Updating submodule(s). [nomail] --- aux/broctl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aux/broctl b/aux/broctl index 90f9ca0ffa..eb1d029e51 160000 --- a/aux/broctl +++ b/aux/broctl @@ -1 +1 @@ -Subproject commit 90f9ca0ffa2306f0d1d2ac208cdbb7787199f890 +Subproject commit eb1d029e5161c4dfff00fd190d8da22c0bf8ba50 From bd8893f0d0d4ce48423c696f6706a985267e6398 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Wed, 31 Dec 2014 09:19:09 -0800 Subject: [PATCH 064/299] Changing Makefile's test-all to run test-all for broctl. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9feaecd656..207ce72780 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ test: @( cd testing && make ) test-all: test - test -d aux/broctl && ( cd aux/broctl && make test ) + test -d aux/broctl && ( cd aux/broctl && make test-all ) test -d aux/btest && ( cd aux/btest && make test ) test -d aux/bro-aux && ( cd aux/bro-aux && make test ) test -d aux/plugins && ( cd aux/plugins && make test-all ) From 494545f1ebfd569e39a41863ccfd38b884b50127 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Wed, 31 Dec 2014 09:19:34 -0800 Subject: [PATCH 065/299] Updating submodule(s). [nomail] --- CHANGES | 5 +++++ VERSION | 2 +- aux/broctl | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 2d41f9ad1b..4c546131db 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,9 @@ +2.3-348 | 2014-12-31 09:19:34 -0800 + + * Changing Makefile's test-all to run test-all for broctl, which now + executes trace-summary tests as well. (Robin Sommer) + 2.3-345 | 2014-12-31 09:06:15 -0800 * Correct a typo in the Notice framework doc. (Daniel Thayer) diff --git a/VERSION b/VERSION index 196c840941..378e5b5ce8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3-345 +2.3-348 diff --git a/aux/broctl b/aux/broctl index eb1d029e51..8c9b87bc73 160000 --- a/aux/broctl +++ b/aux/broctl @@ -1 +1 @@ -Subproject commit eb1d029e5161c4dfff00fd190d8da22c0bf8ba50 +Subproject commit 8c9b87bc73e1ddaa304e3d89028c1e7b95d37a91 From a3d78cc830c7e6f200f617560908e84cd6a8a9f5 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Mon, 5 Jan 2015 14:51:58 -0600 Subject: [PATCH 066/299] Revert "Workaround race condition in unified2 file module." This reverts commit 1a03a95f355bcc8e68aa096b074714a879fac902. --- .../scripts/base/files/unified2/alert.bro | 34 ++----------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/testing/btest/scripts/base/files/unified2/alert.bro b/testing/btest/scripts/base/files/unified2/alert.bro index 189e35bd8e..eca1ca036c 100644 --- a/testing/btest/scripts/base/files/unified2/alert.bro +++ b/testing/btest/scripts/base/files/unified2/alert.bro @@ -1,4 +1,4 @@ -# @TEST-EXEC: bro -b %INPUT test_watch_file=$FILES/unified2.u2 +# @TEST-EXEC: bro -b %INPUT Unified2::watch_file=$FILES/unified2.u2 # @TEST-EXEC: btest-diff unified2.log @TEST-START-FILE sid_msg.map @@ -68,39 +68,9 @@ redef Unified2::gen_msg = @DIR+"/gen_msg.map"; redef Unified2::classification_config = @DIR+"/classification.config"; global i = 0; -# TODO: can't currently use Unified2::watch_file directly for the test as -# there's a race between reading that file and the map/classification -# config files, which leads to not all fields of the unified2.log being -# populated on occassion. -const test_watch_file: string = "" &redef; - -event start_test() - { - Input::add_analysis([$source=test_watch_file, - $reader=Input::READER_BINARY, - $mode=Input::STREAM, - $name=test_watch_file]); - } - -# TODO: this should be handled by unified2 module, but it's here for -# working around the issue mentioned in comment above. -event file_new(f: fa_file) - { - if ( f$source == test_watch_file ) - { - Files::add_analyzer(f, Files::ANALYZER_UNIFIED2); - f$u2_events = table(); - } - } - -event bro_init() - { - schedule 2sec { start_test() }; - } - event Unified2::alert(f: fa_file, ev: Unified2::IDSEvent, pkt: Unified2::Packet) { ++i; if ( i == 2 ) terminate(); - } + } \ No newline at end of file From 1971d25a5cb895f039ac809d02d986c9dc118b18 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Mon, 5 Jan 2015 15:21:13 -0600 Subject: [PATCH 067/299] Fix race condition in unified2 file analyzer startup. Retrieval of extended alert information from sid-msg.map, gen-msg.map, and classification.config files uses Bro's input framework, but since the unified2 file analyzer also relies on the input framework, coordination is needed to start analysis only after extended info has been read at least once. --- CHANGES | 4 + VERSION | 2 +- scripts/base/files/unified2/main.bro | 95 ++++++++++++++++----- testing/btest/Baseline/plugins.hooks/output | 24 ++++-- 4 files changed, 94 insertions(+), 31 deletions(-) diff --git a/CHANGES b/CHANGES index 4c546131db..60c40cbce6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,8 @@ +2.3-349 | 2015-01-05 15:21:13 -0600 + + * Fix race condition in unified2 file analyzer startup. (Jon siwek) + 2.3-348 | 2014-12-31 09:19:34 -0800 * Changing Makefile's test-all to run test-all for broctl, which now diff --git a/VERSION b/VERSION index 378e5b5ce8..f2b1636819 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3-348 +2.3-349 diff --git a/scripts/base/files/unified2/main.bro b/scripts/base/files/unified2/main.bro index 2f6ae79f4f..627bcc9fee 100644 --- a/scripts/base/files/unified2/main.bro +++ b/scripts/base/files/unified2/main.bro @@ -71,11 +71,50 @@ global classification_map: table[count] of string; global sid_map: table[count] of string; global gen_map: table[count] of string; +global num_classification_map_reads = 0; +global num_sid_map_reads = 0; +global num_gen_map_reads = 0; +global watching = F; + # For reading in config files. type OneLine: record { line: string; }; +function mappings_initialized(): bool + { + return num_classification_map_reads > 0 && + num_sid_map_reads > 0 && + num_gen_map_reads > 0; + } + +function start_watching() + { + if ( watching ) + return; + + watching = T; + + if ( watch_dir != "" ) + { + Dir::monitor(watch_dir, function(fname: string) + { + Input::add_analysis([$source=fname, + $reader=Input::READER_BINARY, + $mode=Input::STREAM, + $name=fname]); + }, 10secs); + } + + if ( watch_file != "" ) + { + Input::add_analysis([$source=watch_file, + $reader=Input::READER_BINARY, + $mode=Input::STREAM, + $name=watch_file]); + } + } + function create_info(ev: IDSEvent): Info { local info = Info($ts=ev$ts, @@ -136,11 +175,33 @@ event Unified2::read_classification_line(desc: Input::EventDescription, tpe: Inp } } +event Input::end_of_data(name: string, source: string) + { + if ( name == classification_config ) + ++num_classification_map_reads; + else if ( name == sid_msg ) + ++num_sid_map_reads; + else if ( name == gen_msg ) + ++num_gen_map_reads; + else + return; + + if ( watching ) + return; + + if ( mappings_initialized() ) + start_watching(); + } + event bro_init() &priority=5 { Log::create_stream(Unified2::LOG, [$columns=Info, $ev=log_unified2]); - if ( sid_msg != "" ) + if ( sid_msg == "" ) + { + num_sid_map_reads = 1; + } + else { Input::add_event([$source=sid_msg, $reader=Input::READER_RAW, @@ -151,7 +212,11 @@ event bro_init() &priority=5 $ev=Unified2::read_sid_msg_line]); } - if ( gen_msg != "" ) + if ( gen_msg == "" ) + { + num_gen_map_reads = 1; + } + else { Input::add_event([$source=gen_msg, $name=gen_msg, @@ -162,7 +227,11 @@ event bro_init() &priority=5 $ev=Unified2::read_gen_msg_line]); } - if ( classification_config != "" ) + if ( classification_config == "" ) + { + num_classification_map_reads = 1; + } + else { Input::add_event([$source=classification_config, $name=classification_config, @@ -173,24 +242,8 @@ event bro_init() &priority=5 $ev=Unified2::read_classification_line]); } - if ( watch_dir != "" ) - { - Dir::monitor(watch_dir, function(fname: string) - { - Input::add_analysis([$source=fname, - $reader=Input::READER_BINARY, - $mode=Input::STREAM, - $name=fname]); - }, 10secs); - } - - if ( watch_file != "" ) - { - Input::add_analysis([$source=watch_file, - $reader=Input::READER_BINARY, - $mode=Input::STREAM, - $name=watch_file]); - } + if ( mappings_initialized() ) + start_watching(); } event file_new(f: fa_file) diff --git a/testing/btest/Baseline/plugins.hooks/output b/testing/btest/Baseline/plugins.hooks/output index 31dd415e1b..d18499d5dd 100644 --- a/testing/btest/Baseline/plugins.hooks/output +++ b/testing/btest/Baseline/plugins.hooks/output @@ -189,7 +189,7 @@ 0.000000 MetaHookPost CallFunction(Log::__create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -> -0.000000 MetaHookPost CallFunction(Log::__write, (PacketFilter::LOG, [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::__write, (PacketFilter::LOG, [ts=1420492465.686432, node=bro, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, (Cluster::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, (Communication::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, (Conn::LOG)) -> @@ -283,8 +283,8 @@ 0.000000 MetaHookPost CallFunction(Log::create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -> -0.000000 MetaHookPost CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T])) -> -0.000000 MetaHookPost CallFunction(Log::write, (PacketFilter::LOG, [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1420492465.686432, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::write, (PacketFilter::LOG, [ts=1420492465.686432, node=bro, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(Notice::want_pp, ()) -> 0.000000 MetaHookPost CallFunction(PacketFilter::build, ()) -> 0.000000 MetaHookPost CallFunction(PacketFilter::combine_filters, (ip or not ip, and, )) -> @@ -303,6 +303,8 @@ 0.000000 MetaHookPost CallFunction(SumStats::register_observe_plugin, (SumStats::UNIQUE, anonymous-function{ if (!SumStats::rv?$unique_vals) SumStats::rv$unique_vals = (coerce set() to set[SumStats::Observation])if (SumStats::r?$unique_max) SumStats::rv$unique_max = SumStats::r$unique_maxif (!SumStats::r?$unique_max || flattenSumStats::rv$unique_vals <= SumStats::r$unique_max) add SumStats::rv$unique_vals[SumStats::obs]SumStats::rv$unique = flattenSumStats::rv$unique_vals})) -> 0.000000 MetaHookPost CallFunction(SumStats::register_observe_plugin, (SumStats::VARIANCE, anonymous-function{ if (1 < SumStats::rv$num) SumStats::rv$var_s += ((SumStats::val - SumStats::rv$prev_avg) * (SumStats::val - SumStats::rv$average))SumStats::calc_variance(SumStats::rv)SumStats::rv$prev_avg = SumStats::rv$average})) -> 0.000000 MetaHookPost CallFunction(SumStats::register_observe_plugins, ()) -> +0.000000 MetaHookPost CallFunction(Unified2::mappings_initialized, ()) -> +0.000000 MetaHookPost CallFunction(Unified2::start_watching, ()) -> 0.000000 MetaHookPost CallFunction(bro_init, ()) -> 0.000000 MetaHookPost CallFunction(cat, (Packe, t, _, Filter)) -> 0.000000 MetaHookPost CallFunction(current_time, ()) -> @@ -724,7 +726,7 @@ 0.000000 MetaHookPre CallFunction(Log::__create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -0.000000 MetaHookPre CallFunction(Log::__write, (PacketFilter::LOG, [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::__write, (PacketFilter::LOG, [ts=1420492465.686432, node=bro, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, (Cluster::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, (Communication::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, (Conn::LOG)) @@ -818,8 +820,8 @@ 0.000000 MetaHookPre CallFunction(Log::create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) 0.000000 MetaHookPre CallFunction(Log::create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) 0.000000 MetaHookPre CallFunction(Log::create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -0.000000 MetaHookPre CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T])) -0.000000 MetaHookPre CallFunction(Log::write, (PacketFilter::LOG, [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1420492465.686432, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::write, (PacketFilter::LOG, [ts=1420492465.686432, node=bro, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(Notice::want_pp, ()) 0.000000 MetaHookPre CallFunction(PacketFilter::build, ()) 0.000000 MetaHookPre CallFunction(PacketFilter::combine_filters, (ip or not ip, and, )) @@ -838,6 +840,8 @@ 0.000000 MetaHookPre CallFunction(SumStats::register_observe_plugin, (SumStats::UNIQUE, anonymous-function{ if (!SumStats::rv?$unique_vals) SumStats::rv$unique_vals = (coerce set() to set[SumStats::Observation])if (SumStats::r?$unique_max) SumStats::rv$unique_max = SumStats::r$unique_maxif (!SumStats::r?$unique_max || flattenSumStats::rv$unique_vals <= SumStats::r$unique_max) add SumStats::rv$unique_vals[SumStats::obs]SumStats::rv$unique = flattenSumStats::rv$unique_vals})) 0.000000 MetaHookPre CallFunction(SumStats::register_observe_plugin, (SumStats::VARIANCE, anonymous-function{ if (1 < SumStats::rv$num) SumStats::rv$var_s += ((SumStats::val - SumStats::rv$prev_avg) * (SumStats::val - SumStats::rv$average))SumStats::calc_variance(SumStats::rv)SumStats::rv$prev_avg = SumStats::rv$average})) 0.000000 MetaHookPre CallFunction(SumStats::register_observe_plugins, ()) +0.000000 MetaHookPre CallFunction(Unified2::mappings_initialized, ()) +0.000000 MetaHookPre CallFunction(Unified2::start_watching, ()) 0.000000 MetaHookPre CallFunction(bro_init, ()) 0.000000 MetaHookPre CallFunction(cat, (Packe, t, _, Filter)) 0.000000 MetaHookPre CallFunction(current_time, ()) @@ -1259,7 +1263,7 @@ 0.000000 | HookCallFunction Log::__create_stream(Weird::LOG, [columns=, ev=Weird::log_weird]) 0.000000 | HookCallFunction Log::__create_stream(X509::LOG, [columns=, ev=X509::log_x509]) 0.000000 | HookCallFunction Log::__create_stream(mysql::LOG, [columns=, ev=MySQL::log_mysql]) -0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1420492465.686432, node=bro, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction Log::add_default_filter(Cluster::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Communication::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Conn::LOG) @@ -1353,8 +1357,8 @@ 0.000000 | HookCallFunction Log::create_stream(Weird::LOG, [columns=, ev=Weird::log_weird]) 0.000000 | HookCallFunction Log::create_stream(X509::LOG, [columns=, ev=X509::log_x509]) 0.000000 | HookCallFunction Log::create_stream(mysql::LOG, [columns=, ev=MySQL::log_mysql]) -0.000000 | HookCallFunction Log::default_path_func(PacketFilter::LOG, , [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T]) -0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1414788015.369883, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::default_path_func(PacketFilter::LOG, , [ts=1420492465.686432, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1420492465.686432, node=bro, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction Notice::want_pp() 0.000000 | HookCallFunction PacketFilter::build() 0.000000 | HookCallFunction PacketFilter::combine_filters(ip or not ip, and, ) @@ -1373,6 +1377,8 @@ 0.000000 | HookCallFunction SumStats::register_observe_plugin(SumStats::UNIQUE, anonymous-function{ if (!SumStats::rv?$unique_vals) SumStats::rv$unique_vals = (coerce set() to set[SumStats::Observation])if (SumStats::r?$unique_max) SumStats::rv$unique_max = SumStats::r$unique_maxif (!SumStats::r?$unique_max || flattenSumStats::rv$unique_vals <= SumStats::r$unique_max) add SumStats::rv$unique_vals[SumStats::obs]SumStats::rv$unique = flattenSumStats::rv$unique_vals}) 0.000000 | HookCallFunction SumStats::register_observe_plugin(SumStats::VARIANCE, anonymous-function{ if (1 < SumStats::rv$num) SumStats::rv$var_s += ((SumStats::val - SumStats::rv$prev_avg) * (SumStats::val - SumStats::rv$average))SumStats::calc_variance(SumStats::rv)SumStats::rv$prev_avg = SumStats::rv$average}) 0.000000 | HookCallFunction SumStats::register_observe_plugins() +0.000000 | HookCallFunction Unified2::mappings_initialized() +0.000000 | HookCallFunction Unified2::start_watching() 0.000000 | HookCallFunction bro_init() 0.000000 | HookCallFunction cat(Packe, t, _, Filter) 0.000000 | HookCallFunction current_time() From 58a9162ce71c18e68942af97449a2346c6fcea12 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Mon, 5 Jan 2015 16:57:24 -0600 Subject: [PATCH 068/299] Add NEWS items related to file analysis changes. --- NEWS | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/NEWS b/NEWS index 6de9bd8f3e..5e2ef52ca1 100644 --- a/NEWS +++ b/NEWS @@ -28,11 +28,31 @@ New Functionality - Bro now has supoprt for the MySQL wire protocol. Activity gets logged into mysql.log. +- Bro's file analysis now supports reassembly of files that are not + transferred/seen sequentially. + Changed Functionality --------------------- - bro-cut has been rewritten in C, and is hence much faster. +- File analysis + + * Removed ``fa_file`` record's ``mime_type`` and ``mime_types`` + fields. The events ``file_mime_type`` and ``file_mime_types`` + have been added which contain the same information. The + ``mime_type`` field of ``Files::Info`` also still has this info. + + * Removed ``Files::add_analyzers_for_mime_type`` function. + + * Removed ``offset`` parameter of the ``file_extraction_limit`` + event. Since file extraction now internally depends on file + reassembly for non-sequential files, "offset" can be obtained + with other information already available -- adding together + ``seen_bytes`` and ``missed_bytes`` fields of the ``fa_file`` + record gives the how many bytes have been written so far (i.e. + the "offset"). + Bro 2.3 ======= From 593e74d4b7adb55e6cffd9f6cd4a616c44d0a90c Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 6 Jan 2015 15:12:28 -0600 Subject: [PATCH 069/299] Updating submodule(s). [nomail] --- aux/broccoli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aux/broccoli b/aux/broccoli index acb8fbe8e7..d43cc790e5 160000 --- a/aux/broccoli +++ b/aux/broccoli @@ -1 +1 @@ -Subproject commit acb8fbe8e7bc6ace5135fb73dca8e29432cdc1ca +Subproject commit d43cc790e5b8709b5e032e52ad0e00936494739b From 5e206ed108e13a31b0829c0c029880007fd8a884 Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Tue, 6 Jan 2015 20:27:20 -0600 Subject: [PATCH 070/299] Add support for SSH1 --- scripts/base/protocols/ssh/dpd.sig | 11 +- scripts/base/protocols/ssh/main.bro | 55 +++-- src/analyzer/protocol/ssh/SSH.cc | 65 ++++-- src/analyzer/protocol/ssh/SSH.h | 4 +- src/analyzer/protocol/ssh/events.bif | 4 +- src/analyzer/protocol/ssh/ssh-analyzer.pac | 8 +- src/analyzer/protocol/ssh/ssh-protocol.pac | 240 +++++++++++++++------ 7 files changed, 275 insertions(+), 112 deletions(-) diff --git a/scripts/base/protocols/ssh/dpd.sig b/scripts/base/protocols/ssh/dpd.sig index 95e22908ab..e56878275c 100644 --- a/scripts/base/protocols/ssh/dpd.sig +++ b/scripts/base/protocols/ssh/dpd.sig @@ -1,13 +1,6 @@ -signature dpd_ssh_client { +signature dpd_ssh { ip-proto == tcp - payload /^[sS][sS][hH]-/ - requires-reverse-signature dpd_ssh_server + payload /^[sS][sS][hH]-[12]./ enable "ssh" - tcp-state originator } -signature dpd_ssh_server { - ip-proto == tcp - payload /^[sS][sS][hH]-/ - tcp-state responder -} diff --git a/scripts/base/protocols/ssh/main.bro b/scripts/base/protocols/ssh/main.bro index bba3f8ac88..00f387d432 100644 --- a/scripts/base/protocols/ssh/main.bro +++ b/scripts/base/protocols/ssh/main.bro @@ -12,6 +12,8 @@ export { uid: string &log; ## The connection's 4-tuple of endpoint addresses/ports. id: conn_id &log; + ## SSH major version (1 or 2) + version: count &log; ## Auth result result: string &log &optional; ## Auth method (password, pubkey, etc.) @@ -54,23 +56,36 @@ event bro_init() &priority=5 Analyzer::register_for_ports(Analyzer::ANALYZER_SSH, ports); } -function determine_auth_method(middle_pkt_len: int, first_pkt_len: int): string +function determine_auth_method(version: int, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int): string { # This is still being tested. # Based on "Analysis for Identifying User Authentication Methods on SSH Connections" # by Satoh, Nakamura, Ikenaga. - if ( middle_pkt_len == 96 ) - return "password"; - if ( middle_pkt_len == 16 ) - return "gssapi"; - if ( ( middle_pkt_len == 32 ) && ( first_pkt_len == 0 || first_pkt_len == 48 ) ) - return "challenge-response"; - if ( middle_pkt_len < 256 ) - return fmt("unknown (mid=%d, first=%d)", middle_pkt_len, first_pkt_len); - if ( first_pkt_len == 16 ) - return "host-based"; - return fmt("pubkey (~%d bits)", (first_pkt_len - 16)*8); + if ( version == 2 ) + { + if ( first_pkt_len == 0 ) + return "none"; + if ( middle_pkt_len == 96 ) + return "password"; + if ( middle_pkt_len == 16 ) + return "gssapi"; + if ( ( middle_pkt_len == 32 ) && ( first_pkt_len == 0 || first_pkt_len == 48 ) ) + return "challenge-response"; + if ( middle_pkt_len < 256 ) + return fmt("unknown (mid=%d, first=%d)", middle_pkt_len, first_pkt_len); + if ( first_pkt_len == 16 ) + return "host-based"; + return fmt("pubkey (~%d bits)", (first_pkt_len - 16)*8); + } + else if ( version == 1 ) + { + if ( first_pkt_len == 0 ) + return "password"; + if ( first_pkt_len >= 96 && first_pkt_len <= 256 ) + return fmt("pubkey (~%d bits)", first_pkt_len * 8); + return fmt("%d %d %d", first_pkt_len, middle_pkt_len, last_pkt_len); + } } event ssh_server_version(c: connection, version: string) @@ -97,33 +112,37 @@ event ssh_client_version(c: connection, version: string) c$ssh = s; } c$ssh$client = version; + if ( version[4] == "1" ) + c$ssh$version = 1; + if ( version[4] == "2" ) + c$ssh$version = 2; } -event ssh_auth_successful(c: connection, middle_pkt_len: int, first_pkt_len: int) +event ssh_auth_successful(c: connection, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int) { print "ssh_auth_successful"; if ( !c?$ssh || ( c$ssh?$result && c$ssh$result == "success" ) ) return; c$ssh$result = "success"; - c$ssh$method = determine_auth_method(middle_pkt_len, first_pkt_len); + c$ssh$method = determine_auth_method(c$ssh$version, last_pkt_len, middle_pkt_len, first_pkt_len); } -event ssh_auth_successful(c: connection, middle_pkt_len: int, first_pkt_len: int) &priority=-5 +event ssh_auth_successful(c: connection, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int) &priority=-5 { c$ssh$logged = T; Log::write(SSH::LOG, c$ssh); } -event ssh_auth_failed(c: connection, middle_pkt_len: int, first_pkt_len: int) +event ssh_auth_failed(c: connection, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int) { print "ssh_auth_failed"; if ( !c?$ssh || ( c$ssh?$result && c$ssh$result == "success" ) ) return; c$ssh$result = "failure"; - c$ssh$method = determine_auth_method(middle_pkt_len, first_pkt_len); + c$ssh$method = determine_auth_method(c$ssh$version, last_pkt_len, middle_pkt_len, first_pkt_len); } -event ssh_auth_failed(c: connection, middle_pkt_len: int, first_pkt_len: int) &priority=-5 +event ssh_auth_failed(c: connection, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int) &priority=-5 { c$ssh$logged = T; Log::write(SSH::LOG, c$ssh); diff --git a/src/analyzer/protocol/ssh/SSH.cc b/src/analyzer/protocol/ssh/SSH.cc index 184dbba914..19ca99417c 100644 --- a/src/analyzer/protocol/ssh/SSH.cc +++ b/src/analyzer/protocol/ssh/SSH.cc @@ -17,6 +17,7 @@ SSH_Analyzer::SSH_Analyzer(Connection* c) { interp = new binpac::SSH::SSH_Conn(this); had_gap = false; + auth_decision_made = false; num_encrypted_packets_seen = 0; initial_client_packet_size = 0; initial_server_packet_size = 0; @@ -68,6 +69,7 @@ void SSH_Analyzer::DeliverStream(int len, const u_char* data, bool orig) } catch ( const binpac::Exception& e ) { + printf("Binpac exception: %s\n", e.c_msg()); ProtocolViolation(fmt("Binpac exception: %s", e.c_msg())); } } @@ -92,17 +94,24 @@ void SSH_Analyzer::ProcessEncrypted(int len, bool orig) else relative_len = len - initial_server_packet_size; - if ( num_encrypted_packets_seen >= 6 ) + if ( !auth_decision_made && ( num_encrypted_packets_seen > 3 ) ) { - int auth_result = AuthResult(relative_len, orig); + int auth_result = AuthResult(relative_len, orig, interp->get_version()); if ( auth_result > 0 ) { + auth_decision_made = true; if ( auth_result == 1 ) - BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), - packet_n_1_size, packet_n_2_size); + BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), + interp->bro_analyzer()->Conn(), + len, + packet_n_1_size, + packet_n_2_size); if ( auth_result == 2 ) - BifEvent::generate_ssh_auth_failed(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), - packet_n_1_size, packet_n_2_size); + BifEvent::generate_ssh_auth_failed(interp->bro_analyzer(), + interp->bro_analyzer()->Conn(), + len, + packet_n_1_size, + packet_n_2_size); } } if ( ( num_encrypted_packets_seen >= 2 ) && @@ -111,29 +120,47 @@ void SSH_Analyzer::ProcessEncrypted(int len, bool orig) packet_n_2_is_orig = packet_n_1_is_orig; packet_n_2_size = packet_n_1_size; } - - if ( orig == packet_n_1_is_orig ) + if ( num_encrypted_packets_seen == 0 ) + num_encrypted_packets_seen = 1; + else if ( orig == packet_n_1_is_orig ) packet_n_1_size += len; else { packet_n_1_is_orig = orig; packet_n_1_size = relative_len; - num_encrypted_packets_seen++; + if ( ! ( ( interp->get_version() == binpac::SSH::SSH1 ) && len > 90 ) ) + num_encrypted_packets_seen++; } } -int SSH_Analyzer::AuthResult(int len, bool orig) +int SSH_Analyzer::AuthResult(int len, bool orig, int version) { - if ( !orig && packet_n_1_is_orig && !packet_n_2_is_orig ) - { - printf("Auth result = %d\n", len); - if ( len == -16 ) - return 1; - else if ( len >= 16 && len <= 32 ) - return 2; - return 0; - } + if ( version == binpac::SSH::SSH2 ) + { + if ( !orig && packet_n_1_is_orig && !packet_n_2_is_orig ) + { + if ( len == -16 ) + return 1; + else if ( len >= 16 && len <= 32 ) + return 2; + return 0; + } + } + else if ( version == binpac::SSH::SSH1 ) + { + // On a successful login, the server sends a longer message + if ( !orig && len > 0 ) + { + // To verify a public key, the server sends back a message of the same size + // as the previous one. Ignore that occurrence here. + if ( ! ( packet_n_1_is_orig && ( len == packet_n_1_size ) ) ) + return 1; + } + // If we've seen too many messages without a longer message, treat it as a failure + if ( num_encrypted_packets_seen > 7 ) + return 2; + } return -1; } diff --git a/src/analyzer/protocol/ssh/SSH.h b/src/analyzer/protocol/ssh/SSH.h index 73a5420166..185e83d4de 100644 --- a/src/analyzer/protocol/ssh/SSH.h +++ b/src/analyzer/protocol/ssh/SSH.h @@ -31,11 +31,13 @@ protected: binpac::SSH::SSH_Conn* interp; void ProcessEncrypted(int len, bool orig); - int AuthResult(int len, bool orig); + int AuthResult(int len, bool orig, int version); bool had_gap; // Packet analysis stuff + bool auth_decision_made; + int initial_client_packet_size; int initial_server_packet_size; int num_encrypted_packets_seen; diff --git a/src/analyzer/protocol/ssh/events.bif b/src/analyzer/protocol/ssh/events.bif index 1389b2e0da..1b6b80f8df 100644 --- a/src/analyzer/protocol/ssh/events.bif +++ b/src/analyzer/protocol/ssh/events.bif @@ -2,9 +2,9 @@ event ssh_server_version%(c: connection, version: string%); event ssh_client_version%(c: connection, version: string%); -event ssh_auth_successful%(c: connection, middle_packet_len: int, first_packet_len: int%); +event ssh_auth_successful%(c: connection, last_packet_len: int, middle_packet_len: int, first_packet_len: int%); -event ssh_auth_failed%(c: connection, middle_packet_len: int, first_packet_len: int%); +event ssh_auth_failed%(c: connection, last_packet_len: int, middle_packet_len: int, first_packet_len: int%); event ssh_server_capabilities%(c: connection, kex_algorithms: string, server_host_key_algorithms: string, encryption_algorithms_client_to_server: string, encryption_algorithms_server_to_client: string, mac_algorithms_client_to_server: string, mac_algorithms_server_to_client: string, compression_algorithms_client_to_server: string, compression_algorithms_server_to_client: string, languages_client_to_server: string, languages_server_to_client: string%); diff --git a/src/analyzer/protocol/ssh/ssh-analyzer.pac b/src/analyzer/protocol/ssh/ssh-analyzer.pac index da940fffed..0e627481a4 100644 --- a/src/analyzer/protocol/ssh/ssh-analyzer.pac +++ b/src/analyzer/protocol/ssh/ssh-analyzer.pac @@ -67,6 +67,10 @@ refine typeattr SSH_DH_GEX_REPLY += &let { proc: bool = $context.flow.proc_ssh_server_host_key(k_s.val); }; -refine typeattr SSH_Message += &let { - proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == SSH2_MSG_NEWKEYS); +refine typeattr SSH1_Message += &let { + proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == SSH_CMSG_SESSION_KEY); +}; + +refine typeattr SSH2_Message += &let { + proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == MSG_NEWKEYS); }; \ No newline at end of file diff --git a/src/analyzer/protocol/ssh/ssh-protocol.pac b/src/analyzer/protocol/ssh/ssh-protocol.pac index d14af0a663..3e1ec95ecc 100644 --- a/src/analyzer/protocol/ssh/ssh-protocol.pac +++ b/src/analyzer/protocol/ssh/ssh-protocol.pac @@ -1,41 +1,95 @@ +enum version { + SSH1 = 1, + SSH2 = 2, + UNK = 3, +}; + enum state { VERSION_EXCHANGE = 0, KEY_EXCHANGE_CLEARTEXT = 1, ENCRYPTED = 2, }; -enum message_id { - SSH2_MSG_DISCONNECT = 1, - SSH2_MSG_IGNORE = 2, - SSH2_MSG_UNIMPLEMENTED = 3, - SSH2_MSG_DEBUG = 4, - SSH2_MSG_SERVICE_REQUEST = 5, - SSH2_MSG_SERVICE_ACCEPT = 6, - SSH2_MSG_KEXINIT = 20, - SSH2_MSG_NEWKEYS = 21, - SSH2_MSG_KEX_DH_GEX_REQUEST_OLD = 30, - SSH2_MSG_KEX_DH_GEX_GROUP = 31, - SSH2_MSG_KEX_DH_GEX_INIT = 32, - SSH2_MSG_KEX_DH_GEX_REPLY = 33, - SSH2_MSG_KEX_DH_GEX_REQUEST = 34, - SSH2_MSG_USERAUTH_REQUEST = 50, - SSH2_MSG_USERAUTH_FAILURE = 51, - SSH2_MSG_USERAUTH_SUCCESS = 52, - SSH2_MSG_USERAUTH_BANNER = 53, - SSH2_MSG_GLOBAL_REQUEST = 80, - SSH2_MSG_REQUEST_SUCCESS = 81, - SSH2_MSG_REQUEST_FAILURE = 82, - SSH2_MSG_CHANNEL_OPEN = 90, - SSH2_MSG_CHANNEL_OPEN_CONFIRMATION = 91, - SSH2_MSG_CHANNEL_OPEN_FAILURE = 92, - SSH2_MSG_CHANNEL_WINDOW_ADJUST = 93, - SSH2_MSG_CHANNEL_DATA = 94, - SSH2_MSG_CHANNEL_EXTENDED_DATA = 95, - SSH2_MSG_CHANNEL_EOF = 96, - SSH2_MSG_CHANNEL_CLOSE = 97, - SSH2_MSG_CHANNEL_REQUEST = 98, - SSH2_MSG_CHANNEL_SUCCESS = 99, - SSH2_MSG_CHANNEL_FAILURE = 100, +enum ssh1_message_id { + SSH_MSG_NONE = 0, + SSH_MSG_DISCONNECT = 1, + SSH_SMSG_PUBLIC_KEY = 2, + SSH_CMSG_SESSION_KEY = 3, + SSH_CMSG_USER = 4, + SSH_CMSG_AUTH_RHOSTS = 5, + SSH_CMSG_AUTH_RSA = 6, + SSH_SMSG_AUTH_RSA_CHALLENGE = 7, + SSH_CMSG_AUTH_RSA_RESPONSE = 8, + SSH_CMSG_AUTH_PASSWORD = 9, + SSH_CMSG_REQUEST_PTY = 10, + SSH_CMSG_WINDOW_SIZE = 11, + SSH_CMSG_EXEC_SHELL = 12, + SSH_CMSG_EXEC_CMD = 13, + SSH_SMSG_SUCCESS = 14, + SSH_SMSG_FAILURE = 15, + SSH_CMSG_STDIN_DATA = 16, + SSH_SMSG_STDOUT_DATA = 17, + SSH_SMSG_STDERR_DATA = 18, + SSH_CMSG_EOF = 19, + SSH_SMSG_EXITSTATUS = 20, + SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 21, + SSH_MSG_CHANNEL_OPEN_FAILURE = 22, + SSH_MSG_CHANNEL_DATA = 23, + SSH_MSG_CHANNEL_CLOSE = 24, + SSH_MSG_CHANNEL_CLOSE_CONFIRMATION = 25, + SSH_CMSG_X11_REQUEST_FORWARDING_OLD = 26, + SSH_SMSG_X11_OPEN = 27, + SSH_CMSG_PORT_FORWARD_REQUEST = 28, + SSH_MSG_PORT_OPEN = 29, + SSH_CMSG_AGENT_REQUEST_FORWARDING = 30, + SSH_SMSG_AGENT_OPEN = 31, + SSH_MSG_IGNORE = 32, + SSH_CMSG_EXIT_CONFIRMATION = 33, + SSH_CMSG_X11_REQUEST_FORWARDING = 34, + SSH_CMSG_AUTH_RHOSTS_RSA = 35, + SSH_MSG_DEBUG = 36, + SSH_CMSG_REQUEST_COMPRESSION = 37, + SSH_CMSG_MAX_PACKET_SIZE = 38, + SSH_CMSG_AUTH_TIS = 39, + SSH_SMSG_AUTH_TIS_CHALLENGE = 40, + SSH_CMSG_AUTH_TIS_RESPONSE = 41, + SSH_CMSG_AUTH_KERBEROS = 42, + SSH_SMSG_AUTH_KERBEROS_RESPONSE = 43, + SSH_CMSG_HAVE_KERBEROS_TGT = 44, +}; + +enum ssh2_message_id { + MSG_DISCONNECT = 1, + MSG_IGNORE = 2, + MSG_UNIMPLEMENTED = 3, + MSG_DEBUG = 4, + MSG_SERVICE_REQUEST = 5, + MSG_SERVICE_ACCEPT = 6, + MSG_KEXINIT = 20, + MSG_NEWKEYS = 21, + MSG_KEX_DH_GEX_REQUEST_OLD = 30, + MSG_KEX_DH_GEX_GROUP = 31, + MSG_KEX_DH_GEX_INIT = 32, + MSG_KEX_DH_GEX_REPLY = 33, + MSG_KEX_DH_GEX_REQUEST = 34, + MSG_USERAUTH_REQUEST = 50, + MSG_USERAUTH_FAILURE = 51, + MSG_USERAUTH_SUCCESS = 52, + MSG_USERAUTH_BANNER = 53, + MSG_GLOBAL_REQUEST = 80, + MSG_REQUEST_SUCCESS = 81, + MSG_REQUEST_FAILURE = 82, + MSG_CHANNEL_OPEN = 90, + MSG_CHANNEL_OPEN_CONFIRMATION = 91, + MSG_CHANNEL_OPEN_FAILURE = 92, + MSG_CHANNEL_WINDOW_ADJUST = 93, + MSG_CHANNEL_DATA = 94, + MSG_CHANNEL_EXTENDED_DATA = 95, + MSG_CHANNEL_EOF = 96, + MSG_CHANNEL_CLOSE = 97, + MSG_CHANNEL_REQUEST = 98, + MSG_CHANNEL_SUCCESS = 99, + MSG_CHANNEL_FAILURE = 100, }; type SSH_PDU(is_orig: bool) = case $context.connection.get_state(is_orig) of { @@ -46,40 +100,76 @@ type SSH_PDU(is_orig: bool) = case $context.connection.get_state(is_orig) of { type SSH_Version(is_orig: bool) = record { version: bytestring &oneline; + pad: bytestring &length=0 &transient; } &let { - update_state: bool = $context.connection.update_state(KEY_EXCHANGE_CLEARTEXT, is_orig); + update_state : bool = $context.connection.update_state(KEY_EXCHANGE_CLEARTEXT, is_orig); + update_version: bool = $context.connection.update_version(version, is_orig); }; -type SSH_Key_Exchange_Header(is_orig: bool) = record { - packet_length : uint32; +type SSH_Key_Exchange(is_orig: bool) = case $context.connection.get_version() of { + SSH1 -> ssh1_msg: SSH1_Key_Exchange(is_orig); + SSH2 -> ssh2_msg: SSH2_Key_Exchange(is_orig); +}; + +type SSH1_Key_Exchange(is_orig: bool) = record { + packet_length: uint32; + pad_fill : bytestring &length = 8 - (packet_length % 8); + msg_type : uint8; + message : SSH1_Message(is_orig, msg_type, packet_length-5); + crc : uint32; +} &length = packet_length + 4 + 8 - (packet_length % 8); + +type SSH2_Key_Exchange_Header = record { + packet_length : uint32; padding_length: uint8; -} &length=5; - -type SSH_Key_Exchange(is_orig: bool) = record { - header : SSH_Key_Exchange_Header(is_orig); - payload: SSH_Payload(is_orig, header.packet_length - header.padding_length - 2); - pad : bytestring &length=header.padding_length; -}; - -type SSH_Payload_Header = record { - message_type: uint8; -} &length=1; - -type SSH_Payload(is_orig: bool, packet_length: uint32) = record { - header: SSH_Payload_Header; - message: SSH_Message(is_orig, header.message_type, packet_length); -}; - -type SSH_Message(is_orig: bool, msg_type: uint8, packet_length: uint32) = case msg_type of { - SSH2_MSG_KEXINIT -> kexinit: SSH_KEXINIT(packet_length); - SSH2_MSG_KEX_DH_GEX_REQUEST -> dh_gex_request: SSH_DH_GEX_REQUEST(packet_length); - SSH2_MSG_KEX_DH_GEX_REQUEST_OLD -> dh_gex_request_old: SSH_DH_GEX_REQUEST_OLD(packet_length); - SSH2_MSG_KEX_DH_GEX_GROUP -> dh_gex_group: SSH_DH_GEX_GROUP(packet_length); - SSH2_MSG_KEX_DH_GEX_INIT -> dh_gex_init: SSH_DH_GEX_INIT(packet_length); - SSH2_MSG_KEX_DH_GEX_REPLY -> dh_gex_reply: SSH_DH_GEX_REPLY(packet_length); - SSH2_MSG_NEWKEYS -> new_keys: bytestring &length=packet_length; + msg_type : uint8; } &let { - detach: bool = $context.connection.update_state(ENCRYPTED, is_orig) &if(msg_type == SSH2_MSG_NEWKEYS); + payload_length: uint32 = packet_length - padding_length - 2; +} &length=6; + +type SSH2_Key_Exchange(is_orig: bool) = record { + header : SSH2_Key_Exchange_Header; + payload : SSH2_Message(is_orig, header.msg_type, header.payload_length); + pad : bytestring &length=header.padding_length; +} &length=header.packet_length + 4; + +type SSH1_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { + SSH_SMSG_PUBLIC_KEY -> public_key: SSH1_PUBLIC_KEY(length); + SSH_CMSG_SESSION_KEY -> session_key: SSH1_SESSION_KEY(length); +} &let { + detach: bool = $context.connection.update_state(ENCRYPTED, is_orig); +}; + +type SSH1_PUBLIC_KEY(length: uint32) = record { + cookie : bytestring &length=8; + server_key : uint32; + server_key_p : ssh1_mp_int; + server_key_e : ssh1_mp_int; + host_key : uint32; + host_key_p : ssh1_mp_int; + host_key_e : ssh1_mp_int; + flags : uint32; + supported_ciphers : uint32; + supported_auths : uint32; +} &length=length; + +type SSH1_SESSION_KEY(length: uint32) = record { + cipher : uint8; + cookie : bytestring &length=8; + session_key : ssh1_mp_int; + flags : uint32; +} &length=length; + +type SSH2_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { + MSG_KEXINIT -> kexinit: SSH_KEXINIT(length); + MSG_KEX_DH_GEX_REQUEST -> dh_gex_request: SSH_DH_GEX_REQUEST(length); + MSG_KEX_DH_GEX_REQUEST_OLD -> dh_gex_request_old: SSH_DH_GEX_REQUEST_OLD(length); + MSG_KEX_DH_GEX_GROUP -> dh_gex_group: SSH_DH_GEX_GROUP(length); + MSG_KEX_DH_GEX_INIT -> dh_gex_init: SSH_DH_GEX_INIT(length); + MSG_KEX_DH_GEX_REPLY -> dh_gex_reply: SSH_DH_GEX_REPLY(length); + MSG_NEWKEYS -> new_keys: bytestring &length=length; +} &let { + detach: bool = $context.connection.update_state(ENCRYPTED, is_orig) &if(msg_type == MSG_NEWKEYS); }; type SSH_KEXINIT(length: uint32) = record { @@ -123,6 +213,11 @@ type SSH_DH_GEX_REPLY(length: uint32) = record { signature: ssh_string; } &length=length; +type ssh1_mp_int = record { + len: uint16; + val: bytestring &length=(len+7)/8; +}; + type ssh_string = record { len: uint32; val: bytestring &length=len; @@ -132,19 +227,25 @@ refine connection SSH_Conn += { %member{ int state_up_; int state_down_; + int version_; %} %init{ state_up_ = VERSION_EXCHANGE; state_down_ = VERSION_EXCHANGE; + version_ = UNK; %} function get_state(is_orig: bool): int %{ if ( is_orig ) + { return state_up_; + } else + { return state_down_; + } %} function update_state(s: state, is_orig: bool): bool @@ -156,4 +257,21 @@ refine connection SSH_Conn += { return true; %} + function get_version(): int + %{ + return version_; + %} + + function update_version(v: bytestring, is_orig: bool): bool + %{ + if ( is_orig && ( v.length() >= 4 ) ) + { + if ( v[4] == '2' ) + version_ = SSH2; + if ( v[4] == '1' ) + version_ = SSH1; + } + return true; + %} + }; \ No newline at end of file From 245bd07af7d58af2fc3772a7a3363a573db7a6fa Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Tue, 6 Jan 2015 21:23:18 -0600 Subject: [PATCH 071/299] Add host key support for SSH1. --- scripts/base/protocols/ssh/main.bro | 21 +++++++++++++++++---- src/analyzer/protocol/ssh/events.bif | 4 +++- src/analyzer/protocol/ssh/ssh-analyzer.pac | 16 ++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/scripts/base/protocols/ssh/main.bro b/scripts/base/protocols/ssh/main.bro index 00f387d432..24e11779b7 100644 --- a/scripts/base/protocols/ssh/main.bro +++ b/scripts/base/protocols/ssh/main.bro @@ -186,11 +186,24 @@ event connection_state_remove(c: connection) &priority=-5 Log::write(SSH::LOG, c$ssh); } +function generate_fingerprint(c: connection, key: string) + { + local lx = str_split(md5_hash(key), vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30)); + lx[0] = ""; + c$ssh$host_key = sub(join_string_vec(lx, ":"), /:/, ""); + } + +event ssh1_server_host_key(c: connection, p: string, e: string) + { + if ( !c?$ssh ) + return; + generate_fingerprint(c, e + p); + } + event ssh_server_host_key(c: connection, key: string) { if ( !c?$ssh ) return; - local lx = str_split(md5_hash(key), vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30)); - lx[0] = ""; - c$ssh$host_key = sub(join_string_vec(lx, ":"), /:/, ""); - } \ No newline at end of file + generate_fingerprint(c, key); + } + diff --git a/src/analyzer/protocol/ssh/events.bif b/src/analyzer/protocol/ssh/events.bif index 1b6b80f8df..7505dfd6db 100644 --- a/src/analyzer/protocol/ssh/events.bif +++ b/src/analyzer/protocol/ssh/events.bif @@ -8,4 +8,6 @@ event ssh_auth_failed%(c: connection, last_packet_len: int, middle_packet_len: i event ssh_server_capabilities%(c: connection, kex_algorithms: string, server_host_key_algorithms: string, encryption_algorithms_client_to_server: string, encryption_algorithms_server_to_client: string, mac_algorithms_client_to_server: string, mac_algorithms_server_to_client: string, compression_algorithms_client_to_server: string, compression_algorithms_server_to_client: string, languages_client_to_server: string, languages_server_to_client: string%); -event ssh_server_host_key%(c: connection, key: string%); \ No newline at end of file +event ssh_server_host_key%(c: connection, key: string%); + +event ssh1_server_host_key%(c: connection, p: string, e: string%); \ No newline at end of file diff --git a/src/analyzer/protocol/ssh/ssh-analyzer.pac b/src/analyzer/protocol/ssh/ssh-analyzer.pac index 0e627481a4..1af96a4c5c 100644 --- a/src/analyzer/protocol/ssh/ssh-analyzer.pac +++ b/src/analyzer/protocol/ssh/ssh-analyzer.pac @@ -47,6 +47,18 @@ refine flow SSH_Flow += { return true; %} + function proc_ssh1_server_host_key(p: bytestring, e: bytestring): bool + %{ + if ( ssh_server_host_key ) + { + BifEvent::generate_ssh1_server_host_key(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${p}), + bytestring_to_val(${e})); + } + return true; + %} + function proc_newkeys(): bool %{ connection()->bro_analyzer()->ProtocolConfirmation(); @@ -73,4 +85,8 @@ refine typeattr SSH1_Message += &let { refine typeattr SSH2_Message += &let { proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == MSG_NEWKEYS); +}; + +refine typeattr SSH1_PUBLIC_KEY += &let { + proc: bool = $context.flow.proc_ssh1_server_host_key(host_key_p.val, host_key_e.val); }; \ No newline at end of file From b5e9433b043bd354024b9945d00de22f50c26cad Mon Sep 17 00:00:00 2001 From: Daniel Thayer Date: Wed, 7 Jan 2015 00:01:35 -0600 Subject: [PATCH 072/299] Improve documentation of the Intelligence Framework Added some missing information and rearranged a few sentences so the order makes more sense. --- doc/frameworks/intel.rst | 72 +++++++++++++++++--------- scripts/base/frameworks/intel/main.bro | 3 +- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/doc/frameworks/intel.rst b/doc/frameworks/intel.rst index f90092aac3..965fba4c14 100644 --- a/doc/frameworks/intel.rst +++ b/doc/frameworks/intel.rst @@ -14,32 +14,35 @@ consume that data, make it available for matching, and provide infrastructure around improving performance, memory utilization, and generally making all of this easier. -Data in the Intelligence Framework is the atomic piece of intelligence +Data in the Intelligence Framework is an atomic piece of intelligence such as an IP address or an e-mail address along with a suite of metadata about it such as a freeform source field, a freeform descriptive field and a URL which might lead to more information about the specific item. The metadata in the default scripts has been deliberately kept minimal so that the community can find the -appropriate fields that need added by writing scripts which extend the +appropriate fields that need to be added by writing scripts which extend the base record using the normal record extension mechanism. Quick Start ----------- -Load the package of scripts that sends data into the Intelligence -Framework to be checked by loading this script in local.bro:: - - @load policy/frameworks/intel/seen - Refer to the "Loading Intelligence" section below to see the format for Intelligence Framework text files, then load those text files with this line in local.bro:: redef Intel::read_files += { "/somewhere/yourdata.txt" }; -The data itself only needs to reside on the manager if running in a +The text files need to reside only on the manager if running in a cluster. +Add the following line to local.bro in order to load the scripts +that send "seen" data into the Intelligence Framework to be checked against +the loaded intelligence data:: + + @load policy/frameworks/intel/seen + +Intelligence data matches will be logged to the intel.log file. + Architecture ------------ @@ -58,8 +61,10 @@ manager is the only node that needs the intelligence data. The intelligence framework has distribution mechanisms which will push data out to all of the nodes that need it. -Here is an example of the intelligence data format. Note that all -whitespace field separators are literal tabs and fields containing only a +Here is an example of the intelligence data format (note that there will be +additional fields if you are using CIF intelligence data or if you are +using the policy/frameworks/intel/do_notice script). Note that all fields +must be separated by a single tab character and fields containing only a hyphen are considered to be null values. :: #fields indicator indicator_type meta.source meta.desc meta.url @@ -69,8 +74,21 @@ hyphen are considered to be null values. :: For a list of all built-in `indicator_type` values, please refer to the documentation of :bro:see:`Intel::Type`. -To load the data once files are created, use the following example -code to define files to load with your own file names of course:: +Note that if you are using data from the Collective Intelligence Framework, +then you will need to add the following line to your local.bro in order +to support additional metadata fields used by CIF:: + + @load policy/integration/collective-intel + +There is a simple mechanism to raise a Bro notice (of type Intel::Notice) +for user-specified intelligence matches. To use this feature, add the +following line to local.bro in order to support additional metadata fields +(documented in the :bro:see:`Intel::MetaData` record):: + + @load policy/frameworks/intel/do_notice + +To load the data once the files are created, use the following example +to specify which files to load (with your own file names of course):: redef Intel::read_files += { "/somewhere/feed1.txt", @@ -85,24 +103,23 @@ Seen Data When some bit of data is extracted (such as an email address in the "From" header in a message over SMTP), the Intelligence Framework -needs to be informed that this data was discovered and it's presence -should be checked within the intelligence data set. This is -accomplished through the :bro:see:`Intel::seen` function. +needs to be informed that this data was discovered so that its presence +will be checked within the loaded intelligence data. This is +accomplished through the :bro:see:`Intel::seen` function, however +typically users won't need to work with this function due to the +scripts included with Bro that will call this function. -Typically users won't need to work with this function due to built in -hook scripts that Bro ships with that will "see" data and send it into -the intelligence framework. A user may only need to load the entire -package of hook scripts as a module or pick and choose specific -scripts to load. Keep in mind that as more data is sent into the +To load all of the scripts included with Bro for sending "seen" data to +the intelligence framework, just add this line to local.bro:: + + @load policy/frameworks/intel/seen + +Alternatively, specific scripts in that directory can be loaded. +Keep in mind that as more data is sent into the intelligence framework, the CPU load consumed by Bro will increase depending on how many times the :bro:see:`Intel::seen` function is being called which is heavily traffic dependent. -The full package of hook scripts that Bro ships with for sending this -"seen" data into the intelligence framework can be loading by adding -this line to local.bro:: - - @load policy/frameworks/intel/seen Intelligence Matches ******************** @@ -111,6 +128,7 @@ Against all hopes, most networks will eventually have a hit on intelligence data which could indicate a possible compromise or other unwanted activity. The Intelligence Framework provides an event that is generated whenever a match is discovered named :bro:see:`Intel::match`. + Due to design restrictions placed upon the intelligence framework, there is no assurance as to where this event will be generated. It could be generated on the worker where @@ -119,3 +137,7 @@ handled, only the data given as event arguments to the event can be assured since the host where the data was seen may not be where ``Intel::match`` is handled. +Intelligence matches are logged to the intel.log file. For a description of +each field in that file, see the documentation for the :bro:see:`Intel::Info` +record. + diff --git a/scripts/base/frameworks/intel/main.bro b/scripts/base/frameworks/intel/main.bro index b5f305a7fc..4866766df4 100644 --- a/scripts/base/frameworks/intel/main.bro +++ b/scripts/base/frameworks/intel/main.bro @@ -67,6 +67,7 @@ export { IN_ANYWHERE, }; + ## Information about a piece of "seen" data. type Seen: record { ## The string if the data is about a string. indicator: string &log &optional; @@ -124,7 +125,7 @@ export { sources: set[string] &log &default=string_set(); }; - ## Intelligence data manipulation functions. + ## Intelligence data manipulation function. global insert: function(item: Item); ## Function to declare discovery of a piece of data in order to check From 794273913fc1c9ef0a8b7c2c250e680d5bb52e23 Mon Sep 17 00:00:00 2001 From: Hui Lin Date: Wed, 7 Jan 2015 15:04:22 -0600 Subject: [PATCH 073/299] add test trace in which DNP3 packets are over UDP; update test scripts and baseline results --- aux/binpac | 2 +- aux/bro-aux | 2 +- aux/broccoli | 2 +- aux/broctl | 2 +- aux/plugins | 2 +- cmake | 2 +- scripts/base/protocols/dnp3/main.bro | 2 +- .../coverage | 1 + .../dnp3.log | 10 + .../output | 7 + .../coverage | 1 + .../dnp3.log | 11 + .../output | 2995 +++++++++++++++++ .../coverage | 1 + .../dnp3.log | 12 + .../output | 1055 ++++++ .../coverage | 1 + .../dnp3.log | 10 + .../output | 6 + .../btest/Traces/dnp3/dnp3_udp_en_spon.pcap | Bin 0 -> 326 bytes testing/btest/Traces/dnp3/dnp3_udp_read.pcap | Bin 0 -> 5800 bytes .../Traces/dnp3/dnp3_udp_select_operate.pcap | Bin 0 -> 1427 bytes testing/btest/Traces/dnp3/dnp3_udp_write.pcap | Bin 0 -> 179 bytes .../base/protocols/dnp3/dnp3_udp_en_spon.bro | 9 + .../base/protocols/dnp3/dnp3_udp_read.bro | 9 + .../dnp3/dnp3_udp_select_operate.bro | 9 + .../base/protocols/dnp3/dnp3_udp_write.bro | 9 + 27 files changed, 4153 insertions(+), 7 deletions(-) create mode 100644 testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_en_spon/coverage create mode 100644 testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_en_spon/dnp3.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_en_spon/output create mode 100644 testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_read/coverage create mode 100644 testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_read/dnp3.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_read/output create mode 100644 testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_select_operate/coverage create mode 100644 testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_select_operate/dnp3.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_select_operate/output create mode 100644 testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_write/coverage create mode 100644 testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_write/dnp3.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_write/output create mode 100755 testing/btest/Traces/dnp3/dnp3_udp_en_spon.pcap create mode 100755 testing/btest/Traces/dnp3/dnp3_udp_read.pcap create mode 100755 testing/btest/Traces/dnp3/dnp3_udp_select_operate.pcap create mode 100755 testing/btest/Traces/dnp3/dnp3_udp_write.pcap create mode 100644 testing/btest/scripts/base/protocols/dnp3/dnp3_udp_en_spon.bro create mode 100644 testing/btest/scripts/base/protocols/dnp3/dnp3_udp_read.bro create mode 100644 testing/btest/scripts/base/protocols/dnp3/dnp3_udp_select_operate.bro create mode 100644 testing/btest/scripts/base/protocols/dnp3/dnp3_udp_write.bro diff --git a/aux/binpac b/aux/binpac index 4e5969f5a4..77a86591dc 160000 --- a/aux/binpac +++ b/aux/binpac @@ -1 +1 @@ -Subproject commit 4e5969f5a40f5cc192a751375cb61131d32c0fc1 +Subproject commit 77a86591dcf89d7252d3676d3f1199d6c927d073 diff --git a/aux/bro-aux b/aux/bro-aux index 181f084432..977654dc51 160000 --- a/aux/bro-aux +++ b/aux/bro-aux @@ -1 +1 @@ -Subproject commit 181f084432e277f899140647d9b788059b3cccb1 +Subproject commit 977654dc51ab08a2afde32241f108cdb4a581d8f diff --git a/aux/broccoli b/aux/broccoli index 6be54279bb..acb8fbe8e7 160000 --- a/aux/broccoli +++ b/aux/broccoli @@ -1 +1 @@ -Subproject commit 6be54279bb7ecb5e03d8bcdc7660d323dc4de1bc +Subproject commit acb8fbe8e7bc6ace5135fb73dca8e29432cdc1ca diff --git a/aux/broctl b/aux/broctl index f0e0efda05..39e865dec9 160000 --- a/aux/broctl +++ b/aux/broctl @@ -1 +1 @@ -Subproject commit f0e0efda05e4b20924efc1b826ad5d85c8b65f83 +Subproject commit 39e865dec9611b9b53b609cbc8df519cebae0a1e diff --git a/aux/plugins b/aux/plugins index 6de518922e..ad600b5bdc 160000 --- a/aux/plugins +++ b/aux/plugins @@ -1 +1 @@ -Subproject commit 6de518922e5f89d52d831ea6fb6adb7fff94437e +Subproject commit ad600b5bdcd56a2723e323c0f2c8e1708956ca4f diff --git a/cmake b/cmake index aa15263ae3..1316c07f70 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit aa15263ae39667e5e9bd73690b05aa4af9147ca3 +Subproject commit 1316c07f7059647b6c4a496ea36e4b83bb5d8f0f diff --git a/scripts/base/protocols/dnp3/main.bro b/scripts/base/protocols/dnp3/main.bro index 60fcab0c26..c00934a65b 100644 --- a/scripts/base/protocols/dnp3/main.bro +++ b/scripts/base/protocols/dnp3/main.bro @@ -31,7 +31,7 @@ redef record connection += { dnp3: Info &optional; }; -const ports = { 20000/tcp }; +const ports = { 20000/tcp , 20000/udp }; redef likely_server_ports += { ports }; event bro_init() &priority=5 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_en_spon/coverage b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_en_spon/coverage new file mode 100644 index 0000000000..8369d1fd9b --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_en_spon/coverage @@ -0,0 +1 @@ +4 of 51 events triggered by trace diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_en_spon/dnp3.log b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_en_spon/dnp3.log new file mode 100644 index 0000000000..1d5c0e56c6 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_en_spon/dnp3.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path dnp3 +#open 2015-01-07-21-02-21 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p fc_request fc_reply iin +#types time string addr port addr port string string count +1420058797.673799 CXWv6p3arKYeMETxOg 192.168.80.160 1128 192.168.80.12 20000 ENABLE_UNSOLICITED RESPONSE 1 +#close 2015-01-07-21-02-21 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_en_spon/output b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_en_spon/output new file mode 100644 index 0000000000..ea77e744fb --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_en_spon/output @@ -0,0 +1,7 @@ +dnp3_header_block, T, 25605, 17, 196, 1, 100 +dnp3_application_request_header, T, 207, 20 +dnp3_object_header, T, 15362, 6, 0, 65535, 65535 +dnp3_object_header, T, 15363, 6, 0, 65535, 65535 +dnp3_object_header, T, 15364, 6, 0, 65535, 65535 +dnp3_header_block, F, 25605, 10, 68, 100, 1 +dnp3_application_response_header, F, 207, 129, 1 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_read/coverage b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_read/coverage new file mode 100644 index 0000000000..e49a3133a9 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_read/coverage @@ -0,0 +1 @@ +7 of 51 events triggered by trace diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_read/dnp3.log b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_read/dnp3.log new file mode 100644 index 0000000000..dcd3facf87 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_read/dnp3.log @@ -0,0 +1,11 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path dnp3 +#open 2015-01-07-21-02-12 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p fc_request fc_reply iin +#types time string addr port addr port string string count +1420058427.969342 CXWv6p3arKYeMETxOg 192.168.80.160 1128 192.168.80.12 20000 READ RESPONSE 36864 +1420058427.972303 CXWv6p3arKYeMETxOg 192.168.80.160 1128 192.168.80.12 20000 - RESPONSE 36864 +#close 2015-01-07-21-02-12 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_read/output b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_read/output new file mode 100644 index 0000000000..c875efced0 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_read/output @@ -0,0 +1,2995 @@ +dnp3_header_block, T, 25605, 20, 196, 1, 100 +dnp3_application_request_header, T, 199, 1 +dnp3_object_header, T, 15362, 6, 0, 65535, 65535 +dnp3_object_header, T, 15363, 6, 0, 65535, 65535 +dnp3_object_header, T, 15364, 6, 0, 65535, 65535 +dnp3_object_header, T, 15361, 6, 0, 65535, 65535 +dnp3_header_block, F, 25605, 255, 68, 100, 1 +dnp3_application_response_header, F, 135, 129, 36864 +dnp3_object_header, F, 257, 1, 1024, 0, 1023 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 3 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 0 +dnp3_object_header, F, 2562, 1, 512, 0, 511 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_header, F, 7685, 1, 276, 0, 275 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 1124382111 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 3242862076 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_header_block, F, 25605, 255, 68, 100, 1 +dnp3_application_response_header, F, 72, 129, 36864 +dnp3_object_header, F, 7685, 1, 224, 276, 499 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_analog_input_SPwFlag, F, 1, 0 +dnp3_response_data_object, F, 255 +dnp3_object_header, F, 10243, 0, 100, 0, 99 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 255 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_select_operate/coverage b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_select_operate/coverage new file mode 100644 index 0000000000..e49a3133a9 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_select_operate/coverage @@ -0,0 +1 @@ +7 of 51 events triggered by trace diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_select_operate/dnp3.log b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_select_operate/dnp3.log new file mode 100644 index 0000000000..c826e9bcd8 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_select_operate/dnp3.log @@ -0,0 +1,12 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path dnp3 +#open 2015-01-07-21-02-26 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p fc_request fc_reply iin +#types time string addr port addr port string string count +1420058517.353161 CXWv6p3arKYeMETxOg 192.168.80.160 1128 192.168.80.12 20000 SELECT RESPONSE 36864 +1420058517.467502 CXWv6p3arKYeMETxOg 192.168.80.160 1128 192.168.80.12 20000 OPERATE RESPONSE 36864 +1420058517.574061 CXWv6p3arKYeMETxOg 192.168.80.160 1128 192.168.80.12 20000 READ RESPONSE 36864 +#close 2015-01-07-21-02-26 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_select_operate/output b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_select_operate/output new file mode 100644 index 0000000000..394bdb1fe9 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_select_operate/output @@ -0,0 +1,1055 @@ +dnp3_header_block, T, 25605, 26, 196, 1, 100 +dnp3_application_request_header, T, 201, 3 +dnp3_object_header, T, 3073, 40, 1, 256, 0 +dnp3_object_prefix, T, 1 +dnp3_crob, T, 3, 1, 100, 100, 0 +dnp3_header_block, F, 25605, 28, 68, 100, 1 +dnp3_application_response_header, F, 201, 129, 36864 +dnp3_object_header, F, 3073, 40, 1, 256, 0 +dnp3_object_prefix, F, 1 +dnp3_crob, F, 3, 1, 100, 100, 0 +dnp3_response_data_object, F, 255 +dnp3_header_block, T, 25605, 26, 196, 1, 100 +dnp3_application_request_header, T, 202, 4 +dnp3_object_header, T, 3073, 40, 1, 256, 0 +dnp3_object_prefix, T, 1 +dnp3_crob, T, 3, 1, 100, 100, 0 +dnp3_header_block, F, 25605, 28, 68, 100, 1 +dnp3_application_response_header, F, 202, 129, 36864 +dnp3_object_header, F, 3073, 40, 1, 256, 0 +dnp3_object_prefix, F, 1 +dnp3_crob, F, 3, 1, 100, 100, 0 +dnp3_response_data_object, F, 255 +dnp3_header_block, T, 25605, 20, 196, 1, 100 +dnp3_application_request_header, T, 203, 1 +dnp3_object_header, T, 15362, 6, 0, 65535, 65535 +dnp3_object_header, T, 15363, 6, 0, 65535, 65535 +dnp3_object_header, T, 15364, 6, 0, 65535, 65535 +dnp3_object_header, T, 2560, 6, 0, 65535, 65535 +dnp3_header_block, F, 25605, 255, 68, 100, 1 +dnp3_application_response_header, F, 203, 129, 36864 +dnp3_object_header, F, 2562, 1, 512, 0, 511 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 129 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 +dnp3_object_prefix, F, 0 +dnp3_response_data_object, F, 1 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_write/coverage b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_write/coverage new file mode 100644 index 0000000000..3f1d5d583a --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_write/coverage @@ -0,0 +1 @@ +5 of 51 events triggered by trace diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_write/dnp3.log b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_write/dnp3.log new file mode 100644 index 0000000000..bab97b0ee7 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_write/dnp3.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path dnp3 +#open 2015-01-07-21-02-34 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p fc_request fc_reply iin +#types time string addr port addr port string string count +1420058753.490949 CXWv6p3arKYeMETxOg 192.168.80.160 1128 192.168.80.12 20000 WRITE RESPONSE 0 +#close 2015-01-07-21-02-34 diff --git a/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_write/output b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_write/output new file mode 100644 index 0000000000..f8ee638f76 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dnp3.dnp3_udp_write/output @@ -0,0 +1,6 @@ +dnp3_header_block, T, 25605, 14, 196, 1, 100 +dnp3_application_request_header, T, 206, 2 +dnp3_object_header, T, 20481, 0, 1, 7, 7 +dnp3_object_prefix, T, 0 +dnp3_header_block, F, 25605, 10, 68, 100, 1 +dnp3_application_response_header, F, 206, 129, 0 diff --git a/testing/btest/Traces/dnp3/dnp3_udp_en_spon.pcap b/testing/btest/Traces/dnp3/dnp3_udp_en_spon.pcap new file mode 100755 index 0000000000000000000000000000000000000000..79881f62e734c6714ea1a20d36ce60e3cd7f3941 GIT binary patch literal 326 zcmca|c+)~A1{MY+z{m*XtW8)Fa>|*D!3oF)VTOQ1{!>yI#LAAJyUoGi%D`Z9N1TD7 zLC|K&ffWG@fS89R!%u-hp{k5EMeqnCLkhz?y|d>hhX1FgOT$_kc_Tnd+y&lEEOk^%iRimrDu*Bg5&3Kh8HYFfvN5+5~c) z3m&u7crCzYO+qnCXs$VH3acl`th)GBPyjM($zCo76QGko7{jM(cXGf!jRm<0WFFY3 PLREW#W_5#nx?nm0LhfDq literal 0 HcmV?d00001 diff --git a/testing/btest/Traces/dnp3/dnp3_udp_read.pcap b/testing/btest/Traces/dnp3/dnp3_udp_read.pcap new file mode 100755 index 0000000000000000000000000000000000000000..7bc9d4add2dd73d0eb883d0dd37cbc26e07b9791 GIT binary patch literal 5800 zcmd^@Ye-Z<6vxl3dzYwX!!qkb*rvHk*#n9UT*#i5mI;|)eX#OE`lYfUdJ#rMA9_$^ z6h&lEBo*1iK15Holr-1(_R`YqO+*iff@nK4SDia!PDN$QdSRBi=iWW{e||IP&biZ2 zdj6;dtYA6_!IOy@$5O+VYT(8`Z4kPq-187pO6KM1eP9NF%YGLiJ~-*)je-9FukA|;GH0K7--ADaUf7YyICk$AB6@6_d zphN!tIP`Jtky6h+rsu=Y^-y1D(`_m}JDDE0TF0iEZ@_D$H5zfMgs2+GOY?-hQ73nRwS6B#Bw%)O6o;3f(t$ z@wUyofzUDZ1qGptTpRufhtDUR;z>_g`dSDiECe6QGadOMrW4k^T>AGdJ2z0e<4cru zA27OJk#6i|PItH@Y^_H!kbj}wc!ocFHt2?yD(OCCbQ?vwr=vODa0%v{A(Fu#>4s^T zYXb>6a?v`Ce2q*!lUPhs7n8C5=Tjr^8@(+#ofq3LO^@ohyW`#oFSw8Tesu0e-BQwR zVsslsx|dv>ZUlG8dYSaSE(lp(C>g-jEZza|yV{Ls__ODk8!6qvWlFlujBdS1H?NJ; z9f5(GSxp@(8NfWE{!hhF|L2+y4Wo1|B`CSzT(%jhi6^j&2OV7 zOs`YYea7fkigdq)aJn%@nsg$qc`X@!l0n8y54;-Os0k-EDCs_DbSp%?6mNO`Hf1t_;rcBAUR;;0t7fFhjs0|0yX9Vr9qA-R59$Wni#Bf0BWr zLD1v*ffWG@fS89R!%u-h+ixXniqsKCh7^V*_2Va*c^EYq85kLu8B>5pBuauv28L*m z;ho8>4E{h42t$m8SOGTNk>?i!gM*;=R*=CU3;YyVG8l9V4OvrUTvC7rUjFpqWa9(| zB;%DgG5{?F0kH9pWZCh#{S?^k&rscdy#nF(6Q@{^3};}t=7D6mo)SAgxBmvaeG97F zc_t&={_zy5+uQw6++MX490;IL#te7!^JllWLFdYDsTq9@z literal 0 HcmV?d00001 diff --git a/testing/btest/Traces/dnp3/dnp3_udp_write.pcap b/testing/btest/Traces/dnp3/dnp3_udp_write.pcap new file mode 100755 index 0000000000000000000000000000000000000000..a2dd31b3b721409e32b617bb2b43ef8609ec5ca3 GIT binary patch literal 179 zcmca|c+)~A1{MY+z{m*XG$t$wQK({Pum`e1m?7Yh|CAI4v9jamZgViWGB6n4Y-V6+ z5VW^Gup(dq5c9BP_$e^RT1;h4;XA^}kiu|Q=*&5$07eFOb_NSmka0(A*%@qrCV?=- yAc&!0<8*oT7#JJ`y{CeV0vYM2z>>irS@(f8h07&{fsx_#!|&%B85rIKtpWgLGchy( literal 0 HcmV?d00001 diff --git a/testing/btest/scripts/base/protocols/dnp3/dnp3_udp_en_spon.bro b/testing/btest/scripts/base/protocols/dnp3/dnp3_udp_en_spon.bro new file mode 100644 index 0000000000..a5f1f895cc --- /dev/null +++ b/testing/btest/scripts/base/protocols/dnp3/dnp3_udp_en_spon.bro @@ -0,0 +1,9 @@ +# +# @TEST-EXEC: bro -r $TRACES/dnp3/dnp3_udp_en_spon.pcap %DIR/events.bro >output +# @TEST-EXEC: btest-diff output +# @TEST-EXEC: cat output | awk '{print $1}' | sort | uniq | wc -l >covered +# @TEST-EXEC: cat ${DIST}/src/analyzer/protocol/dnp3/events.bif | grep "^event dnp3_" | wc -l >total +# @TEST-EXEC: echo `cat covered` of `cat total` events triggered by trace >coverage +# @TEST-EXEC: btest-diff coverage +# @TEST-EXEC: btest-diff dnp3.log +# diff --git a/testing/btest/scripts/base/protocols/dnp3/dnp3_udp_read.bro b/testing/btest/scripts/base/protocols/dnp3/dnp3_udp_read.bro new file mode 100644 index 0000000000..073e758df4 --- /dev/null +++ b/testing/btest/scripts/base/protocols/dnp3/dnp3_udp_read.bro @@ -0,0 +1,9 @@ +# +# @TEST-EXEC: bro -r $TRACES/dnp3/dnp3_udp_read.pcap %DIR/events.bro >output +# @TEST-EXEC: btest-diff output +# @TEST-EXEC: cat output | awk '{print $1}' | sort | uniq | wc -l >covered +# @TEST-EXEC: cat ${DIST}/src/analyzer/protocol/dnp3/events.bif | grep "^event dnp3_" | wc -l >total +# @TEST-EXEC: echo `cat covered` of `cat total` events triggered by trace >coverage +# @TEST-EXEC: btest-diff coverage +# @TEST-EXEC: btest-diff dnp3.log +# diff --git a/testing/btest/scripts/base/protocols/dnp3/dnp3_udp_select_operate.bro b/testing/btest/scripts/base/protocols/dnp3/dnp3_udp_select_operate.bro new file mode 100644 index 0000000000..c8708b10cd --- /dev/null +++ b/testing/btest/scripts/base/protocols/dnp3/dnp3_udp_select_operate.bro @@ -0,0 +1,9 @@ +# +# @TEST-EXEC: bro -r $TRACES/dnp3/dnp3_udp_select_operate.pcap %DIR/events.bro >output +# @TEST-EXEC: btest-diff output +# @TEST-EXEC: cat output | awk '{print $1}' | sort | uniq | wc -l >covered +# @TEST-EXEC: cat ${DIST}/src/analyzer/protocol/dnp3/events.bif | grep "^event dnp3_" | wc -l >total +# @TEST-EXEC: echo `cat covered` of `cat total` events triggered by trace >coverage +# @TEST-EXEC: btest-diff coverage +# @TEST-EXEC: btest-diff dnp3.log +# diff --git a/testing/btest/scripts/base/protocols/dnp3/dnp3_udp_write.bro b/testing/btest/scripts/base/protocols/dnp3/dnp3_udp_write.bro new file mode 100644 index 0000000000..d832d937a7 --- /dev/null +++ b/testing/btest/scripts/base/protocols/dnp3/dnp3_udp_write.bro @@ -0,0 +1,9 @@ +# +# @TEST-EXEC: bro -r $TRACES/dnp3/dnp3_udp_write.pcap %DIR/events.bro >output +# @TEST-EXEC: btest-diff output +# @TEST-EXEC: cat output | awk '{print $1}' | sort | uniq | wc -l >covered +# @TEST-EXEC: cat ${DIST}/src/analyzer/protocol/dnp3/events.bif | grep "^event dnp3_" | wc -l >total +# @TEST-EXEC: echo `cat covered` of `cat total` events triggered by trace >coverage +# @TEST-EXEC: btest-diff coverage +# @TEST-EXEC: btest-diff dnp3.log +# From d8890ea009fdb94ecffcf826bbfd23577396365e Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Thu, 8 Jan 2015 13:10:09 -0600 Subject: [PATCH 074/299] Increase minimum required CMake version to 2.8. --- CHANGES | 4 ++++ CMakeLists.txt | 2 +- VERSION | 2 +- aux/bro-aux | 2 +- doc/install/install.rst | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index e7006e0ab0..cf1b682f81 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,8 @@ +2.3-375 | 2015-01-08 13:10:09 -0600 + + * Increase minimum required CMake version to 2.8. (Jon Siwek) + 2.3-374 | 2015-01-07 10:03:17 -0600 * Improve documentation of the Intelligence Framework. (Daniel Thayer) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a287ef5b4..c0ff6c09d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ project(Bro C CXX) # When changing the minimum version here, also adapt # aux/bro-aux/plugin-support/skeleton/CMakeLists.txt -cmake_minimum_required(VERSION 2.6.3 FATAL_ERROR) +cmake_minimum_required(VERSION 2.8 FATAL_ERROR) include(cmake/CommonCMakeConfig.cmake) diff --git a/VERSION b/VERSION index feb900b5ab..41ff87b741 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3-374 +2.3-375 diff --git a/aux/bro-aux b/aux/bro-aux index 43a9f360c9..0b713c027d 160000 --- a/aux/bro-aux +++ b/aux/bro-aux @@ -1 +1 @@ -Subproject commit 43a9f360c9bf6b35fcb25d61ebff80c7feb1812b +Subproject commit 0b713c027d3efaaca50e5df995c02656175573cd diff --git a/doc/install/install.rst b/doc/install/install.rst index 0052acafb0..a3531f70c3 100644 --- a/doc/install/install.rst +++ b/doc/install/install.rst @@ -35,7 +35,7 @@ before you begin: To build Bro from source, the following additional dependencies are required: - * CMake 2.6.3 or greater (http://www.cmake.org) + * CMake 2.8 or greater (http://www.cmake.org) * Make * C/C++ compiler * SWIG (http://www.swig.org) From 7120098ca2eef0449c383dbf57cc0a26b6303d11 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Thu, 8 Jan 2015 16:43:07 -0600 Subject: [PATCH 075/299] Add support for building/linking broker within bro The new --enable-broker flag can be used to toggle the use of Broker, which also implies building with -std=c++11, though nothing makes use of these features at the moment. --- .gitmodules | 3 +++ CMakeLists.txt | 7 +++++++ aux/broker | 1 + cmake | 2 +- configure | 15 +++++++++++++++ 5 files changed, 27 insertions(+), 1 deletion(-) create mode 160000 aux/broker diff --git a/.gitmodules b/.gitmodules index 24375ce23d..91f39e3d04 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "aux/plugins"] path = aux/plugins url = git://git.bro.org/bro-plugins +[submodule "aux/broker"] + path = aux/broker + url = git://git.bro.org/broker diff --git a/CMakeLists.txt b/CMakeLists.txt index c0ff6c09d4..1f0fcf8d07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,6 +177,12 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) ######################################################################## ## Recurse on sub-directories +if ( ENABLE_BROKER ) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + add_subdirectory(aux/broker) + set(brodeps ${brodeps} broker) +endif () + add_subdirectory(src) add_subdirectory(scripts) add_subdirectory(doc) @@ -224,6 +230,7 @@ message( "\nCXXFLAGS: ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BuildType}}" "\nCPP: ${CMAKE_CXX_COMPILER}" "\n" + "\nBroker: ${ENABLE_BROKER}" "\nBroccoli: ${INSTALL_BROCCOLI}" "\nBroctl: ${INSTALL_BROCTL}" "\nAux. Tools: ${INSTALL_AUX_TOOLS}" diff --git a/aux/broker b/aux/broker new file mode 160000 index 0000000000..a1b51def07 --- /dev/null +++ b/aux/broker @@ -0,0 +1 @@ +Subproject commit a1b51def07cfb191d0a83a78c7102560740dbcb3 diff --git a/cmake b/cmake index 1316c07f70..c2057b7f15 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 1316c07f7059647b6c4a496ea36e4b83bb5d8f0f +Subproject commit c2057b7f15dedc27641a50312384505ce4f2112c diff --git a/configure b/configure index 2b1c568b26..6235aba7dd 100755 --- a/configure +++ b/configure @@ -41,6 +41,8 @@ Usage: $0 [OPTION]... [VAR=VALUE]... --enable-perftools-debug use Google's perftools for debugging --enable-jemalloc link against jemalloc --enable-ruby build ruby bindings for broccoli (deprecated) + --enable-broker enable use of the Broker communication library + (requires C++ Actor Framework and C++11) --disable-broccoli don't build or install the Broccoli library --disable-broctl don't install Broctl --disable-auxtools don't build or install auxiliary tools @@ -55,6 +57,8 @@ Usage: $0 [OPTION]... [VAR=VALUE]... --with-flex=PATH path to flex executable --with-bison=PATH path to bison executable --with-perl=PATH path to perl executable + --with-libcaf=PATH path to C++ Actor Framework installation + (a required Broker dependency) Optional Packages in Non-Standard Locations: --with-geoip=PATH path to the libGeoIP install root @@ -67,6 +71,8 @@ Usage: $0 [OPTION]... [VAR=VALUE]... --with-ruby-lib=PATH path to ruby library --with-ruby-inc=PATH path to ruby headers --with-swig=PATH path to SWIG executable + --with-rocksdb=PATH path to RocksDB installation + (an optional Broker dependency) Packaging Options (for developers): --binary-package toggle special logic for binary packaging @@ -176,6 +182,9 @@ while [ $# -ne 0 ]; do --enable-jemalloc) append_cache_entry ENABLE_JEMALLOC BOOL true ;; + --enable-broker) + append_cache_entry ENABLE_BROKER BOOL true + ;; --disable-broccoli) append_cache_entry INSTALL_BROCCOLI BOOL false ;; @@ -248,6 +257,12 @@ while [ $# -ne 0 ]; do --with-swig=*) append_cache_entry SWIG_EXECUTABLE PATH $optarg ;; + --with-libcaf=*) + append_cache_entry LIBCAF_ROOT_DIR PATH $optarg + ;; + --with-rocksdb=*) + append_cache_entry ROCKSDB_ROOT_DIR PATH $optarg + ;; --binary-package) append_cache_entry BINARY_PACKAGING_MODE BOOL true ;; From 39d51ca99c1e994fd48bfccc89c070522ca7deca Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Mon, 12 Jan 2015 09:38:10 -0600 Subject: [PATCH 076/299] Improve documentation for connection_established event. --- CHANGES | 4 ++++ VERSION | 2 +- src/analyzer/protocol/tcp/events.bif | 6 ++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index cf1b682f81..5fd76c8b06 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,8 @@ +2.3-376 | 2015-01-12 09:38:10 -0600 + + * Improve documentation for connection_established event. (Jon Siwek) + 2.3-375 | 2015-01-08 13:10:09 -0600 * Increase minimum required CMake version to 2.8. (Jon Siwek) diff --git a/VERSION b/VERSION index 41ff87b741..05511b04c1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3-375 +2.3-376 diff --git a/src/analyzer/protocol/tcp/events.bif b/src/analyzer/protocol/tcp/events.bif index f52fadaebb..5cf2710804 100644 --- a/src/analyzer/protocol/tcp/events.bif +++ b/src/analyzer/protocol/tcp/events.bif @@ -29,8 +29,10 @@ event new_connection_contents%(c: connection%); ## new_connection new_connection_contents partial_connection event connection_attempt%(c: connection%); -## Generated when a SYN-ACK packet is seen in response to a SYN packet during -## a TCP handshake. The final ACK of the handshake in response to SYN-ACK may +## Generated when seeing a SYN-ACK packet from the responder in a TCP +## handshake. An associated SYN packet was not seen from the originator +## side if its state is not set to :bro:see:`TCP_ESTABLISHED`. +## The final ACK of the handshake in response to SYN-ACK may ## or may not occur later, one way to tell is to check the *history* field of ## :bro:type:`connection` to see if the originator sent an ACK, indicated by ## 'A' in the history string. From 0480f0d81160e19f17f4107608c0f2fafdb15ef9 Mon Sep 17 00:00:00 2001 From: Johanna Amann Date: Tue, 13 Jan 2015 08:38:18 -0800 Subject: [PATCH 077/299] small changes to ec curve names in a newer draft --- scripts/base/protocols/ssl/consts.bro | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/base/protocols/ssl/consts.bro b/scripts/base/protocols/ssl/consts.bro index 278a2a37ae..3d115419d4 100644 --- a/scripts/base/protocols/ssl/consts.bro +++ b/scripts/base/protocols/ssl/consts.bro @@ -158,12 +158,11 @@ export { [26] = "brainpoolP256r1", [27] = "brainpoolP384r1", [28] = "brainpoolP512r1", - # draft-ietf-tls-negotiated-ff-dhe-02 - [256] = "ffdhe2432", + # draft-ietf-tls-negotiated-ff-dhe-05 + [256] = "ffdhe2048", [257] = "ffdhe3072", [258] = "ffdhe4096", - [259] = "ffdhe6144", - [260] = "ffdhe8192", + [259] = "ffdhe8192", [0xFF01] = "arbitrary_explicit_prime_curves", [0xFF02] = "arbitrary_explicit_char2_curves" } &default=function(i: count):string { return fmt("unknown-%d", i); }; From 05ecac24971cba58f58c28704cdb2b77bee2dbf1 Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Tue, 13 Jan 2015 12:02:31 -0500 Subject: [PATCH 078/299] Refactored the SSH analyzer. Added supported for algorithm detection and more key exchange message types. --- scripts/base/init-bare.bro | 37 ++ scripts/base/protocols/ssh/dpd.sig | 11 +- scripts/base/protocols/ssh/main.bro | 251 ++++++----- src/analyzer/protocol/ssh/CMakeLists.txt | 1 + src/analyzer/protocol/ssh/SSH.cc | 147 +++--- src/analyzer/protocol/ssh/SSH.h | 12 +- src/analyzer/protocol/ssh/events.bif | 8 +- src/analyzer/protocol/ssh/ssh-analyzer.pac | 98 +++- src/analyzer/protocol/ssh/ssh-protocol.pac | 502 ++++++++++++++++----- src/analyzer/protocol/ssh/ssh.pac | 1 + src/analyzer/protocol/ssh/types.bif | 5 + 11 files changed, 745 insertions(+), 328 deletions(-) create mode 100644 src/analyzer/protocol/ssh/types.bif diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index efce524fc5..f71ebe7718 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -2222,6 +2222,43 @@ export { const heartbeat_interval = 1.0 secs &redef; } +module SSH; + +export { + ## SSH Capability record + type Capabilities: record { + ## Key exchange algorithms + kex_algorithms : string_vec; + ## The algorithms supported for the server host key + server_host_key_algorithms : string_vec; + ## Acceptable symmetric encryption algorithms for c->s, + ## in order of preference + encryption_algorithms_client_to_server : string_vec; + ## Acceptable symmetric encryption algorithms for s->c, + ## in order of preference + encryption_algorithms_server_to_client : string_vec; + ## Acceptable MAC algorithms for c->s, + ## in order of preference + mac_algorithms_client_to_server : string_vec; + + ## Acceptable MAC algorithms for s->c, + ## in order of preference + mac_algorithms_server_to_client : string_vec; + ## Acceptable compression algorithms for c->s, + ## in order of preference + compression_algorithms_client_to_server : string_vec; + ## Acceptable compression algorithms for c->s, + ## in order of preference + compression_algorithms_server_to_client : string_vec; + ## Language tags in order of preference for c->s + languages_client_to_server : string_vec &optional; + ## Language tags in order of preference for s->c + languages_server_to_client : string_vec &optional; + ## Are these the capabilities of the server? + is_server : bool; + }; +} + module GLOBAL; ## An NTP message. diff --git a/scripts/base/protocols/ssh/dpd.sig b/scripts/base/protocols/ssh/dpd.sig index e56878275c..816e7929b3 100644 --- a/scripts/base/protocols/ssh/dpd.sig +++ b/scripts/base/protocols/ssh/dpd.sig @@ -1,6 +1,13 @@ -signature dpd_ssh { +signature dpd_ssh_client { ip-proto == tcp - payload /^[sS][sS][hH]-[12]./ + payload /^[sS][sS][hH]-[12]\./ + requires-reverse-signature dpd_ssh_server enable "ssh" + tcp-state originator } +signature dpd_ssh_server { + ip-proto == tcp + payload /^[sS][sS][hH]-[12]\./ + tcp-state responder +} \ No newline at end of file diff --git a/scripts/base/protocols/ssh/main.bro b/scripts/base/protocols/ssh/main.bro index 24e11779b7..8db2e4d023 100644 --- a/scripts/base/protocols/ssh/main.bro +++ b/scripts/base/protocols/ssh/main.bro @@ -15,22 +15,40 @@ export { ## SSH major version (1 or 2) version: count &log; ## Auth result - result: string &log &optional; - ## Auth method (password, pubkey, etc.) - method: string &log &optional; + auth_success: bool &log &optional; + + ## Auth details + auth_details: string &log &optional; ## Direction of the connection. If the client was a local host ## logging into an external host, this would be OUTBOUND. INBOUND ## would be set for the opposite situation. ## TODO: handle local-local and remote-remote better. direction: Direction &log &optional; + ## The encryption algorithm in use + cipher_alg: string &log &optional; + ## The signing (MAC) algorithm in use + mac_alg: string &log &optional; + ## The compression algorithm in use + compression_alg: string &log &optional; + ## The key exchange algorithm in use + kex_alg: string &log &optional; + + ## The server host key's algorithm + host_key_alg: string &log &optional; + ## The server's key fingerprint + host_key: string &log &optional; ## The client's version string client: string &log &optional; ## The server's version string server: string &log &optional; - ## The server's key fingerprint - host_key: string &log &optional; + ## This connection has been logged (internal use) logged: bool &default=F; + ## Number of failures seen (internal use) + num_failures: count &default=0; + ## Store capabilities from the first host for + ## comparison with the second (internal use) + capabilities: Capabilities &optional; }; ## If true, we tell the event engine to not look at further data @@ -48,146 +66,167 @@ redef record connection += { ssh: Info &optional; }; -const ports = { 22/tcp }; - event bro_init() &priority=5 { Log::create_stream(SSH::LOG, [$columns=Info, $ev=log_ssh]); - Analyzer::register_for_ports(Analyzer::ANALYZER_SSH, ports); } -function determine_auth_method(version: int, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int): string - { - # This is still being tested. - # Based on "Analysis for Identifying User Authentication Methods on SSH Connections" - # by Satoh, Nakamura, Ikenaga. - if ( version == 2 ) - { - if ( first_pkt_len == 0 ) - return "none"; - if ( middle_pkt_len == 96 ) - return "password"; - if ( middle_pkt_len == 16 ) - return "gssapi"; - if ( ( middle_pkt_len == 32 ) && ( first_pkt_len == 0 || first_pkt_len == 48 ) ) - return "challenge-response"; - if ( middle_pkt_len < 256 ) - return fmt("unknown (mid=%d, first=%d)", middle_pkt_len, first_pkt_len); - if ( first_pkt_len == 16 ) - return "host-based"; - return fmt("pubkey (~%d bits)", (first_pkt_len - 16)*8); - } - else if ( version == 1 ) - { - if ( first_pkt_len == 0 ) - return "password"; - if ( first_pkt_len >= 96 && first_pkt_len <= 256 ) - return fmt("pubkey (~%d bits)", first_pkt_len * 8); - return fmt("%d %d %d", first_pkt_len, middle_pkt_len, last_pkt_len); - } - } +function init_record(c: connection) + { + local s: SSH::Info; + s$ts = network_time(); + s$uid = c$uid; + s$id = c$id; + c$ssh = s; + } + event ssh_server_version(c: connection, version: string) { if ( !c?$ssh ) - { - local s: SSH::Info; - s$ts = network_time(); - s$uid = c$uid; - s$id = c$id; - c$ssh = s; - } + init_record(c); + c$ssh$server = version; } event ssh_client_version(c: connection, version: string) { if ( !c?$ssh ) - { - local s: SSH::Info; - s$ts = network_time(); - s$uid = c$uid; - s$id = c$id; - c$ssh = s; - } + init_record(c); + c$ssh$client = version; + if ( version[4] == "1" ) c$ssh$version = 1; if ( version[4] == "2" ) c$ssh$version = 2; } -event ssh_auth_successful(c: connection, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int) +event ssh_auth_successful(c: connection, auth_method_none: bool) { - print "ssh_auth_successful"; - if ( !c?$ssh || ( c$ssh?$result && c$ssh$result == "success" ) ) + if ( !c?$ssh || ( c$ssh?$auth_success && c$ssh$auth_success ) ) return; - c$ssh$result = "success"; - c$ssh$method = determine_auth_method(c$ssh$version, last_pkt_len, middle_pkt_len, first_pkt_len); - } -event ssh_auth_successful(c: connection, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int) &priority=-5 - { - c$ssh$logged = T; - Log::write(SSH::LOG, c$ssh); - } - -event ssh_auth_failed(c: connection, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int) - { - print "ssh_auth_failed"; - if ( !c?$ssh || ( c$ssh?$result && c$ssh$result == "success" ) ) + # We can't accurately tell for compressed streams + if ( c$ssh?$compression_alg && ( c$ssh$compression_alg == "zlib@openssh.com" || + c$ssh$compression_alg == "zlib" ) ) return; - c$ssh$result = "failure"; - c$ssh$method = determine_auth_method(c$ssh$version, last_pkt_len, middle_pkt_len, first_pkt_len); - } - -event ssh_auth_failed(c: connection, last_pkt_len: int, middle_pkt_len: int, first_pkt_len: int) &priority=-5 - { - c$ssh$logged = T; - Log::write(SSH::LOG, c$ssh); - } + + c$ssh$auth_success = T; -event connection_state_remove(c: connection) - { - if ( c?$ssh && !c$ssh?$result ) + if ( auth_method_none ) + c$ssh$auth_details = "method: none"; + + if ( skip_processing_after_detection) { - c$ssh$result = "unknown"; + skip_further_processing(c$id); + set_record_packets(c$id, F); + } + } + +event ssh_auth_successful(c: connection, auth_method_none: bool) &priority=-5 + { + if ( c?$ssh && !c$ssh$logged ) + { + c$ssh$logged = T; + Log::write(SSH::LOG, c$ssh); } } - -event ssh_server_capabilities(c: connection, kex_algorithms: string, server_host_key_algorithms: string, encryption_algorithms_client_to_server: string, encryption_algorithms_server_to_client: string, mac_algorithms_client_to_server: string, mac_algorithms_server_to_client: string, compression_algorithms_client_to_server: string, compression_algorithms_server_to_client: string, languages_client_to_server: string, languages_server_to_client: string) + +event ssh_auth_failed(c: connection) { - # print "kex_algorithms", kex_algorithms; - # print ""; - # print "server_host_key_algorithms", server_host_key_algorithms; - # print ""; - # print "encryption_algorithms_client_to_server", encryption_algorithms_client_to_server; - # print ""; - # print "encryption_algorithms_server_to_client", encryption_algorithms_server_to_client; - # print ""; - # print "mac_algorithms_client_to_server", mac_algorithms_client_to_server; - # print ""; - # print "mac_algorithms_server_to_client", mac_algorithms_server_to_client; - # print ""; - # print "compression_algorithms_client_to_server", compression_algorithms_client_to_server; - # print ""; - # print "compression_algorithms_server_to_client", compression_algorithms_server_to_client; - # print ""; - # print "languages_client_to_server", languages_client_to_server; - # print ""; - # print "languages_server_to_client", languages_server_to_client; - # print ""; + if ( !c?$ssh || ( c$ssh?$auth_success && !c$ssh$auth_success ) ) + return; + + # We can't accurately tell for compressed streams + if ( c$ssh?$compression_alg && ( c$ssh$compression_alg == "zlib@openssh.com" || + c$ssh$compression_alg == "zlib" ) ) + return; + + c$ssh$auth_success = F; + c$ssh$num_failures += 1; + } + +function array_to_vec(s: string_array): vector of string + { + local r: vector of string; + + for (i in s) + r[i] = s[i]; + return r; + } + +function find_client_preferred_algorithm(client_algorithms: vector of string, server_algorithms: vector of string): string + { + for ( i in client_algorithms ) + for ( j in server_algorithms ) + if ( client_algorithms[i] == server_algorithms[j] ) + return client_algorithms[i]; + } + +function find_client_preferred_algorithm_bidirectional(client_algorithms_c_to_s: vector of string, + server_algorithms_c_to_s: vector of string, + client_algorithms_s_to_c: vector of string, + server_algorithms_s_to_c: vector of string): string + { + local c_to_s = find_client_preferred_algorithm(client_algorithms_c_to_s, server_algorithms_c_to_s); + local s_to_c = find_client_preferred_algorithm(client_algorithms_s_to_c, server_algorithms_s_to_c); + + return c_to_s == s_to_c ? c_to_s : fmt("To server: %s, to client: %s", c_to_s, s_to_c); + } + +event ssh_capabilities(c: connection, cookie: string, capabilities: Capabilities) + { + if ( !c?$ssh || ( c$ssh?$capabilities && c$ssh$capabilities$is_server == capabilities$is_server ) ) + return; + + if ( !c$ssh?$capabilities ) + { + c$ssh$capabilities = capabilities; + return; + } + + local client_caps = capabilities$is_server ? c$ssh$capabilities : capabilities; + local server_caps = capabilities$is_server ? capabilities : c$ssh$capabilities; + + c$ssh$cipher_alg = find_client_preferred_algorithm_bidirectional(client_caps$encryption_algorithms_client_to_server, + server_caps$encryption_algorithms_client_to_server, + client_caps$encryption_algorithms_server_to_client, + server_caps$encryption_algorithms_server_to_client); + + c$ssh$mac_alg = find_client_preferred_algorithm_bidirectional(client_caps$mac_algorithms_client_to_server, + server_caps$mac_algorithms_client_to_server, + client_caps$mac_algorithms_server_to_client, + server_caps$mac_algorithms_server_to_client); + + c$ssh$compression_alg = find_client_preferred_algorithm_bidirectional(client_caps$compression_algorithms_client_to_server, + server_caps$compression_algorithms_client_to_server, + client_caps$compression_algorithms_server_to_client, + server_caps$compression_algorithms_server_to_client); + + c$ssh$kex_alg = find_client_preferred_algorithm(client_caps$kex_algorithms, server_caps$kex_algorithms); + c$ssh$host_key_alg = find_client_preferred_algorithm(client_caps$server_host_key_algorithms, + server_caps$server_host_key_algorithms); } event connection_state_remove(c: connection) &priority=-5 { - if ( c?$ssh && !c$ssh$logged ) + if ( c?$ssh && !c$ssh$logged && c$ssh?$client && c$ssh?$server ) + { + if ( c$ssh?$auth_success && !c$ssh$auth_success ) + c$ssh$auth_details = fmt("%d failure%s", c$ssh$num_failures, c$ssh$num_failures == 1 ? "" : "s"); + + c$ssh$logged = T; Log::write(SSH::LOG, c$ssh); + } } function generate_fingerprint(c: connection, key: string) { + if ( !c?$ssh ) + return; + local lx = str_split(md5_hash(key), vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30)); lx[0] = ""; c$ssh$host_key = sub(join_string_vec(lx, ":"), /:/, ""); @@ -195,15 +234,11 @@ function generate_fingerprint(c: connection, key: string) event ssh1_server_host_key(c: connection, p: string, e: string) { - if ( !c?$ssh ) - return; generate_fingerprint(c, e + p); } event ssh_server_host_key(c: connection, key: string) { - if ( !c?$ssh ) - return; generate_fingerprint(c, key); } diff --git a/src/analyzer/protocol/ssh/CMakeLists.txt b/src/analyzer/protocol/ssh/CMakeLists.txt index 1266e4f496..de9abeb31f 100644 --- a/src/analyzer/protocol/ssh/CMakeLists.txt +++ b/src/analyzer/protocol/ssh/CMakeLists.txt @@ -6,6 +6,7 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DI bro_plugin_begin(Bro SSH) bro_plugin_cc(SSH.cc Plugin.cc) + bro_plugin_bif(types.bif) bro_plugin_bif(events.bif) bro_plugin_pac(ssh.pac ssh-analyzer.pac ssh-protocol.pac) bro_plugin_end() \ No newline at end of file diff --git a/src/analyzer/protocol/ssh/SSH.cc b/src/analyzer/protocol/ssh/SSH.cc index 19ca99417c..4cf05c8c54 100644 --- a/src/analyzer/protocol/ssh/SSH.cc +++ b/src/analyzer/protocol/ssh/SSH.cc @@ -6,6 +6,7 @@ #include "Reporter.h" +#include "types.bif.h" #include "events.bif.h" using namespace analyzer::SSH; @@ -18,10 +19,10 @@ SSH_Analyzer::SSH_Analyzer(Connection* c) interp = new binpac::SSH::SSH_Conn(this); had_gap = false; auth_decision_made = false; - num_encrypted_packets_seen = 0; - initial_client_packet_size = 0; - initial_server_packet_size = 0; - } + skipped_banner = false; + service_accept_size = 0; + userauth_failure_size = 0; + } SSH_Analyzer::~SSH_Analyzer() { @@ -59,7 +60,13 @@ void SSH_Analyzer::DeliverStream(int len, const u_char* data, bool orig) if ( interp->get_state(orig) == binpac::SSH::ENCRYPTED ) { - ProcessEncrypted(len, orig); + if ( ssh_encrypted_packet ) + BifEvent::generate_ssh_encrypted_packet(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), + orig, len); + + if ( !auth_decision_made ) + ProcessEncrypted(len, orig); + return; } @@ -69,7 +76,6 @@ void SSH_Analyzer::DeliverStream(int len, const u_char* data, bool orig) } catch ( const binpac::Exception& e ) { - printf("Binpac exception: %s\n", e.c_msg()); ProtocolViolation(fmt("Binpac exception: %s", e.c_msg())); } } @@ -83,84 +89,65 @@ void SSH_Analyzer::Undelivered(uint64 seq, int len, bool orig) void SSH_Analyzer::ProcessEncrypted(int len, bool orig) { - if (orig && !initial_client_packet_size) - initial_client_packet_size = len; - if (!orig && !initial_server_packet_size) - initial_server_packet_size = len; - - int relative_len; - if (orig) - relative_len = len - initial_client_packet_size; - else - relative_len = len - initial_server_packet_size; - - if ( !auth_decision_made && ( num_encrypted_packets_seen > 3 ) ) + // We're interested in messages from the server for SSH2 + if (!orig && (interp->get_version() == binpac::SSH::SSH2)) { - int auth_result = AuthResult(relative_len, orig, interp->get_version()); - if ( auth_result > 0 ) + // The first thing we see and want to know is the length of + // SSH_MSG_SERVICE_REQUEST, which has a fixed (decrypted) size + // of 24 bytes (17 for content pad-aligned to 8-byte + // boundaries) + if (!service_accept_size) + { + service_accept_size = len; + return; + } + + // If our user can authenticate via the "none" method, this + // packet will be a SSH_MSG_USERAUTH_SUCCESS, which has a + // fixed (decrypted) size of 8 bytes (1 for content + // pad-aligned to 8-byte boundaries). relative_len would be + // -16. + if (!userauth_failure_size && (len + 16 == service_accept_size)) { auth_decision_made = true; - if ( auth_result == 1 ) - BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), - interp->bro_analyzer()->Conn(), - len, - packet_n_1_size, - packet_n_2_size); - if ( auth_result == 2 ) - BifEvent::generate_ssh_auth_failed(interp->bro_analyzer(), - interp->bro_analyzer()->Conn(), - len, - packet_n_1_size, - packet_n_2_size); + if ( ssh_auth_successful ) + BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), true); + return; + } + + // Normally, this packet would be a SSH_MSG_USERAUTH_FAILURE + // message, with a variable length, depending on the + // authentication methods the server supports. If it's too + // big, it might contain a pre-auth MOTD/banner, so we'll just + // skip it. + if (!userauth_failure_size) + { + if ( !skipped_banner && (len - service_accept_size) > 256 ) + { + skipped_banner = true; + return; + } + userauth_failure_size = len; + return; + } + + // If we've already seen a failure, let's see if this is + // another packet of the same size. + if (len == userauth_failure_size) + { + if ( ssh_auth_failed ) + BifEvent::generate_ssh_auth_failed(interp->bro_analyzer(), interp->bro_analyzer()->Conn()); + return; + } + + // ...or a success packet. + if (len - service_accept_size == -16) + { + auth_decision_made = true; + if ( ssh_auth_successful ) + BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), false); + return; } } - if ( ( num_encrypted_packets_seen >= 2 ) && - ( orig != packet_n_1_is_orig ) ) - { - packet_n_2_is_orig = packet_n_1_is_orig; - packet_n_2_size = packet_n_1_size; - } - if ( num_encrypted_packets_seen == 0 ) - num_encrypted_packets_seen = 1; - else if ( orig == packet_n_1_is_orig ) - packet_n_1_size += len; - else - { - packet_n_1_is_orig = orig; - packet_n_1_size = relative_len; - if ( ! ( ( interp->get_version() == binpac::SSH::SSH1 ) && len > 90 ) ) - num_encrypted_packets_seen++; - } - } - - -int SSH_Analyzer::AuthResult(int len, bool orig, int version) - { - if ( version == binpac::SSH::SSH2 ) - { - if ( !orig && packet_n_1_is_orig && !packet_n_2_is_orig ) - { - if ( len == -16 ) - return 1; - else if ( len >= 16 && len <= 32 ) - return 2; - return 0; - } - } - else if ( version == binpac::SSH::SSH1 ) - { - // On a successful login, the server sends a longer message - if ( !orig && len > 0 ) - { - // To verify a public key, the server sends back a message of the same size - // as the previous one. Ignore that occurrence here. - if ( ! ( packet_n_1_is_orig && ( len == packet_n_1_size ) ) ) - return 1; - } - // If we've seen too many messages without a longer message, treat it as a failure - if ( num_encrypted_packets_seen > 7 ) - return 2; - } - return -1; } diff --git a/src/analyzer/protocol/ssh/SSH.h b/src/analyzer/protocol/ssh/SSH.h index 185e83d4de..af6dcbf090 100644 --- a/src/analyzer/protocol/ssh/SSH.h +++ b/src/analyzer/protocol/ssh/SSH.h @@ -31,22 +31,16 @@ protected: binpac::SSH::SSH_Conn* interp; void ProcessEncrypted(int len, bool orig); - int AuthResult(int len, bool orig, int version); bool had_gap; // Packet analysis stuff bool auth_decision_made; + bool skipped_banner; - int initial_client_packet_size; - int initial_server_packet_size; - int num_encrypted_packets_seen; + int service_accept_size; + int userauth_failure_size; - bool packet_n_1_is_orig; - int packet_n_1_size; - bool packet_n_2_is_orig; - int packet_n_2_size; - }; } } // namespace analyzer::* diff --git a/src/analyzer/protocol/ssh/events.bif b/src/analyzer/protocol/ssh/events.bif index 7505dfd6db..4a8d959de4 100644 --- a/src/analyzer/protocol/ssh/events.bif +++ b/src/analyzer/protocol/ssh/events.bif @@ -2,11 +2,13 @@ event ssh_server_version%(c: connection, version: string%); event ssh_client_version%(c: connection, version: string%); -event ssh_auth_successful%(c: connection, last_packet_len: int, middle_packet_len: int, first_packet_len: int%); +event ssh_auth_successful%(c: connection, auth_method_none: bool%); -event ssh_auth_failed%(c: connection, last_packet_len: int, middle_packet_len: int, first_packet_len: int%); +event ssh_auth_failed%(c: connection%); -event ssh_server_capabilities%(c: connection, kex_algorithms: string, server_host_key_algorithms: string, encryption_algorithms_client_to_server: string, encryption_algorithms_server_to_client: string, mac_algorithms_client_to_server: string, mac_algorithms_server_to_client: string, compression_algorithms_client_to_server: string, compression_algorithms_server_to_client: string, languages_client_to_server: string, languages_server_to_client: string%); +event ssh_encrypted_packet%(c: connection, orig: bool, len: count%); + +event ssh_capabilities%(c: connection, cookie: string, capabilities: SSH::Capabilities%); event ssh_server_host_key%(c: connection, key: string%); diff --git a/src/analyzer/protocol/ssh/ssh-analyzer.pac b/src/analyzer/protocol/ssh/ssh-analyzer.pac index 1af96a4c5c..0a76b7c8f4 100644 --- a/src/analyzer/protocol/ssh/ssh-analyzer.pac +++ b/src/analyzer/protocol/ssh/ssh-analyzer.pac @@ -1,3 +1,53 @@ +%extern{ +#include +#include +#include +%} + +%header{ +VectorVal* name_list_to_vector(const bytestring nl); +%} + +%code{ +// Copied from IRC_Analyzer::SplitWords +VectorVal* name_list_to_vector(const bytestring nl) + { + VectorVal* vv = new VectorVal(internal_type("string_vec")->AsVectorType()); + + string name_list = std_str(nl); + if ( name_list.size() < 1 ) + return vv; + + unsigned int start = 0; + unsigned int split_pos = 0; + + while ( name_list[start] == ',' ) + { + ++start; + ++split_pos; + } + + string word; + while ( (split_pos = name_list.find(',', start)) < name_list.size() ) + { + word = name_list.substr(start, split_pos - start); + if ( word.size() > 0 && word[0] != ',' ) + vv->Assign(vv->Size(), new StringVal(word)); + + start = split_pos + 1; + } + + // Add line end if needed. + if ( start < name_list.size() ) + { + word = name_list.substr(start, name_list.size() - start); + vv->Assign(vv->Size(), new StringVal(word)); + } + + return vv; + } +%} + refine flow SSH_Flow += { function proc_ssh_version(msg: SSH_Version): bool %{ @@ -18,20 +68,26 @@ refine flow SSH_Flow += { function proc_ssh_kexinit(msg: SSH_KEXINIT): bool %{ - if ( ssh_server_capabilities ) + if ( ssh_capabilities ) { - BifEvent::generate_ssh_server_capabilities(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - bytestring_to_val(${msg.kex_algorithms.val}), - bytestring_to_val(${msg.server_host_key_algorithms.val}), - bytestring_to_val(${msg.encryption_algorithms_client_to_server.val}), - bytestring_to_val(${msg.encryption_algorithms_server_to_client.val}), - bytestring_to_val(${msg.mac_algorithms_client_to_server.val}), - bytestring_to_val(${msg.mac_algorithms_server_to_client.val}), - bytestring_to_val(${msg.compression_algorithms_client_to_server.val}), - bytestring_to_val(${msg.compression_algorithms_server_to_client.val}), - bytestring_to_val(${msg.languages_client_to_server.val}), - bytestring_to_val(${msg.languages_server_to_client.val})); + RecordVal* result = new RecordVal(BifType::Record::SSH::Capabilities); + result->Assign(0, name_list_to_vector(${msg.kex_algorithms.val})); + result->Assign(1, name_list_to_vector(${msg.server_host_key_algorithms.val})); + result->Assign(2, name_list_to_vector(${msg.encryption_algorithms_client_to_server.val})); + result->Assign(3, name_list_to_vector(${msg.encryption_algorithms_server_to_client.val})); + result->Assign(4, name_list_to_vector(${msg.mac_algorithms_client_to_server.val})); + result->Assign(5, name_list_to_vector(${msg.mac_algorithms_server_to_client.val})); + result->Assign(6, name_list_to_vector(${msg.compression_algorithms_client_to_server.val})); + result->Assign(7, name_list_to_vector(${msg.compression_algorithms_server_to_client.val})); + if ( ${msg.languages_client_to_server.len} ) + result->Assign(8, name_list_to_vector(${msg.languages_client_to_server.val})); + if ( ${msg.languages_server_to_client.len} ) + result->Assign(9, name_list_to_vector(${msg.languages_server_to_client.val})); + result->Assign(10, new Val(${msg.is_orig}, TYPE_BOOL)); + + BifEvent::generate_ssh_capabilities(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), bytestring_to_val(${msg.cookie}), + result); } return true; %} @@ -49,7 +105,7 @@ refine flow SSH_Flow += { function proc_ssh1_server_host_key(p: bytestring, e: bytestring): bool %{ - if ( ssh_server_host_key ) + if ( ssh1_server_host_key ) { BifEvent::generate_ssh1_server_host_key(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), @@ -75,10 +131,6 @@ refine typeattr SSH_KEXINIT += &let { proc: bool = $context.flow.proc_ssh_kexinit(this); }; -refine typeattr SSH_DH_GEX_REPLY += &let { - proc: bool = $context.flow.proc_ssh_server_host_key(k_s.val); -}; - refine typeattr SSH1_Message += &let { proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == SSH_CMSG_SESSION_KEY); }; @@ -87,6 +139,14 @@ refine typeattr SSH2_Message += &let { proc_newkeys: bool = $context.flow.proc_newkeys() &if(msg_type == MSG_NEWKEYS); }; +refine typeattr SSH_DH_GEX_REPLY += &let { + proc: bool = $context.flow.proc_ssh_server_host_key(k_s.val); +}; + +refine typeattr SSH_ECC_REPLY += &let { + proc: bool = $context.flow.proc_ssh_server_host_key(k_s.val); +}; + refine typeattr SSH1_PUBLIC_KEY += &let { - proc: bool = $context.flow.proc_ssh1_server_host_key(host_key_p.val, host_key_e.val); + proc: bool = $context.flow.proc_ssh1_server_host_key(host_key_p.val, host_key_e.val); }; \ No newline at end of file diff --git a/src/analyzer/protocol/ssh/ssh-protocol.pac b/src/analyzer/protocol/ssh/ssh-protocol.pac index 3e1ec95ecc..36bfcaedf3 100644 --- a/src/analyzer/protocol/ssh/ssh-protocol.pac +++ b/src/analyzer/protocol/ssh/ssh-protocol.pac @@ -5,104 +5,143 @@ enum version { }; enum state { - VERSION_EXCHANGE = 0, - KEY_EXCHANGE_CLEARTEXT = 1, - ENCRYPTED = 2, + VERSION_EXCHANGE = 0, + KEX_INIT = 1, + KEX_DH_GEX = 2, + KEX_DH = 3, + KEX_ECC = 4, + KEX_GSS = 5, + KEX_RSA = 6, + ENCRYPTED = 7, +}; + +# diffie-hellman-group1-sha1 [RFC4253] Section 8.1 +# diffie-hellman-group14-sha1 [RFC4253] Section 8.2 +enum KEX_DH_message_id { + SSH_MSG_KEXDH_INIT = 30, + SSH_MSG_KEXDH_REPLY = 31, +}; + +# diffie-hellman-group-exchange-sha1 [RFC4419] Section 4.1 +# diffie-hellman-group-exchange-sha256 [RFC4419] Section 4.2 +enum KEX_DH_GEX_message_id { + SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30, + SSH_MSG_KEX_DH_GEX_GROUP = 31, + SSH_MSG_KEX_DH_GEX_INIT = 32, + SSH_MSG_KEX_DH_GEX_REPLY = 33, + SSH_MSG_KEX_DH_GEX_REQUEST = 34, +}; + +# rsa1024-sha1 [RFC4432] +# rsa2048-sha256 [RFC4432] +enum KEX_RSA_message_id { + SSH_MSG_KEXRSA_PUBKEY = 30, + SSH_MSG_KEXRSA_SECRET = 31, + SSH_MSG_KEXRSA_DONE = 32, +}; + +# gss-group1-sha1-* [RFC4462] Section 2.3 +# gss-group14-sha1-* [RFC4462] Section 2.4 +# gss-gex-sha1-* [RFC4462] Section 2.5 +# gss-* [RFC4462] Section 2.6 +enum KEX_GSS_message_id { + SSH_MSG_KEXGSS_INIT = 30, + SSH_MSG_KEXGSS_CONTINUE = 31, + SSH_MSG_KEXGSS_COMPLETE = 32, + SSH_MSG_KEXGSS_HOSTKEY = 33, + SSH_MSG_KEXGSS_ERROR = 34, + SSH_MSG_KEXGSS_GROUPREQ = 40, + SSH_MSG_KEXGSS_GROUP = 41, +}; + +# ecdh-sha2-* [RFC5656] +enum KEX_ECDH_message_id { + SSH_MSG_KEX_ECDH_INIT = 30, + SSH_MSG_KEX_ECDH_REPLY = 31, +}; + +# ecmqv-sha2 [RFC5656] +enum KEX_ECMQV_message_id { + SSH_MSG_ECMQV_INIT = 30, + SSH_MSG_ECMQV_REPLY = 31, }; enum ssh1_message_id { - SSH_MSG_NONE = 0, - SSH_MSG_DISCONNECT = 1, - SSH_SMSG_PUBLIC_KEY = 2, - SSH_CMSG_SESSION_KEY = 3, - SSH_CMSG_USER = 4, - SSH_CMSG_AUTH_RHOSTS = 5, - SSH_CMSG_AUTH_RSA = 6, - SSH_SMSG_AUTH_RSA_CHALLENGE = 7, - SSH_CMSG_AUTH_RSA_RESPONSE = 8, - SSH_CMSG_AUTH_PASSWORD = 9, - SSH_CMSG_REQUEST_PTY = 10, - SSH_CMSG_WINDOW_SIZE = 11, - SSH_CMSG_EXEC_SHELL = 12, - SSH_CMSG_EXEC_CMD = 13, - SSH_SMSG_SUCCESS = 14, - SSH_SMSG_FAILURE = 15, - SSH_CMSG_STDIN_DATA = 16, - SSH_SMSG_STDOUT_DATA = 17, - SSH_SMSG_STDERR_DATA = 18, - SSH_CMSG_EOF = 19, - SSH_SMSG_EXITSTATUS = 20, + SSH_MSG_NONE = 0, + SSH_MSG_DISCONNECT = 1, + SSH_SMSG_PUBLIC_KEY = 2, + SSH_CMSG_SESSION_KEY = 3, + SSH_CMSG_USER = 4, + SSH_CMSG_AUTH_RHOSTS = 5, + SSH_CMSG_AUTH_RSA = 6, + SSH_SMSG_AUTH_RSA_CHALLENGE = 7, + SSH_CMSG_AUTH_RSA_RESPONSE = 8, + SSH_CMSG_AUTH_PASSWORD = 9, + SSH_CMSG_REQUEST_PTY = 10, + SSH_CMSG_WINDOW_SIZE = 11, + SSH_CMSG_EXEC_SHELL = 12, + SSH_CMSG_EXEC_CMD = 13, + SSH_SMSG_SUCCESS = 14, + SSH_SMSG_FAILURE = 15, + SSH_CMSG_STDIN_DATA = 16, + SSH_SMSG_STDOUT_DATA = 17, + SSH_SMSG_STDERR_DATA = 18, + SSH_CMSG_EOF = 19, + SSH_SMSG_EXITSTATUS = 20, SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 21, SSH_MSG_CHANNEL_OPEN_FAILURE = 22, - SSH_MSG_CHANNEL_DATA = 23, - SSH_MSG_CHANNEL_CLOSE = 24, + SSH_MSG_CHANNEL_DATA = 23, + SSH_MSG_CHANNEL_CLOSE = 24, SSH_MSG_CHANNEL_CLOSE_CONFIRMATION = 25, SSH_CMSG_X11_REQUEST_FORWARDING_OLD = 26, - SSH_SMSG_X11_OPEN = 27, + SSH_SMSG_X11_OPEN = 27, SSH_CMSG_PORT_FORWARD_REQUEST = 28, - SSH_MSG_PORT_OPEN = 29, + SSH_MSG_PORT_OPEN = 29, SSH_CMSG_AGENT_REQUEST_FORWARDING = 30, - SSH_SMSG_AGENT_OPEN = 31, - SSH_MSG_IGNORE = 32, - SSH_CMSG_EXIT_CONFIRMATION = 33, + SSH_SMSG_AGENT_OPEN = 31, + SSH_MSG_IGNORE = 32, + SSH_CMSG_EXIT_CONFIRMATION = 33, SSH_CMSG_X11_REQUEST_FORWARDING = 34, - SSH_CMSG_AUTH_RHOSTS_RSA = 35, - SSH_MSG_DEBUG = 36, + SSH_CMSG_AUTH_RHOSTS_RSA = 35, + SSH_MSG_DEBUG = 36, SSH_CMSG_REQUEST_COMPRESSION = 37, - SSH_CMSG_MAX_PACKET_SIZE = 38, - SSH_CMSG_AUTH_TIS = 39, - SSH_SMSG_AUTH_TIS_CHALLENGE = 40, - SSH_CMSG_AUTH_TIS_RESPONSE = 41, - SSH_CMSG_AUTH_KERBEROS = 42, + SSH_CMSG_MAX_PACKET_SIZE = 38, + SSH_CMSG_AUTH_TIS = 39, + SSH_SMSG_AUTH_TIS_CHALLENGE = 40, + SSH_CMSG_AUTH_TIS_RESPONSE = 41, + SSH_CMSG_AUTH_KERBEROS = 42, SSH_SMSG_AUTH_KERBEROS_RESPONSE = 43, - SSH_CMSG_HAVE_KERBEROS_TGT = 44, + SSH_CMSG_HAVE_KERBEROS_TGT = 44, }; enum ssh2_message_id { - MSG_DISCONNECT = 1, - MSG_IGNORE = 2, - MSG_UNIMPLEMENTED = 3, - MSG_DEBUG = 4, - MSG_SERVICE_REQUEST = 5, - MSG_SERVICE_ACCEPT = 6, - MSG_KEXINIT = 20, - MSG_NEWKEYS = 21, - MSG_KEX_DH_GEX_REQUEST_OLD = 30, - MSG_KEX_DH_GEX_GROUP = 31, - MSG_KEX_DH_GEX_INIT = 32, - MSG_KEX_DH_GEX_REPLY = 33, - MSG_KEX_DH_GEX_REQUEST = 34, - MSG_USERAUTH_REQUEST = 50, - MSG_USERAUTH_FAILURE = 51, - MSG_USERAUTH_SUCCESS = 52, - MSG_USERAUTH_BANNER = 53, - MSG_GLOBAL_REQUEST = 80, - MSG_REQUEST_SUCCESS = 81, - MSG_REQUEST_FAILURE = 82, - MSG_CHANNEL_OPEN = 90, - MSG_CHANNEL_OPEN_CONFIRMATION = 91, - MSG_CHANNEL_OPEN_FAILURE = 92, - MSG_CHANNEL_WINDOW_ADJUST = 93, - MSG_CHANNEL_DATA = 94, - MSG_CHANNEL_EXTENDED_DATA = 95, - MSG_CHANNEL_EOF = 96, - MSG_CHANNEL_CLOSE = 97, - MSG_CHANNEL_REQUEST = 98, - MSG_CHANNEL_SUCCESS = 99, - MSG_CHANNEL_FAILURE = 100, + MSG_DISCONNECT = 1, + MSG_IGNORE = 2, + MSG_UNIMPLEMENTED = 3, + MSG_DEBUG = 4, + MSG_SERVICE_REQUEST = 5, + MSG_SERVICE_ACCEPT = 6, + MSG_KEXINIT = 20, + MSG_NEWKEYS = 21, }; +## SSH Generic + type SSH_PDU(is_orig: bool) = case $context.connection.get_state(is_orig) of { - VERSION_EXCHANGE -> version: SSH_Version(is_orig); - KEY_EXCHANGE_CLEARTEXT -> kex: SSH_Key_Exchange(is_orig); - ENCRYPTED -> ciphertext: bytestring &length=1 &transient; + VERSION_EXCHANGE -> version: SSH_Version(is_orig); + KEX_INIT -> kex: SSH_Key_Exchange(is_orig); + KEX_DH_GEX -> kex_dh_gex: SSH_Key_Exchange_DH_GEX(is_orig); + KEX_DH -> kex_dh: SSH_Key_Exchange_DH(is_orig); + KEX_ECC -> kex_ecc: SSH_Key_Exchange_ECC(is_orig); + KEX_GSS -> kex_gss: SSH_Key_Exchange_GSS(is_orig); + KEX_RSA -> kex_rsa: SSH_Key_Exchange_RSA(is_orig); } &byteorder=bigendian; type SSH_Version(is_orig: bool) = record { version: bytestring &oneline; pad: bytestring &length=0 &transient; } &let { - update_state : bool = $context.connection.update_state(KEY_EXCHANGE_CLEARTEXT, is_orig); + update_state : bool = $context.connection.update_state(KEX_INIT, is_orig); update_version: bool = $context.connection.update_version(version, is_orig); }; @@ -111,6 +150,8 @@ type SSH_Key_Exchange(is_orig: bool) = case $context.connection.get_version() of SSH2 -> ssh2_msg: SSH2_Key_Exchange(is_orig); }; +## SSH1 + type SSH1_Key_Exchange(is_orig: bool) = record { packet_length: uint32; pad_fill : bytestring &length = 8 - (packet_length % 8); @@ -119,20 +160,6 @@ type SSH1_Key_Exchange(is_orig: bool) = record { crc : uint32; } &length = packet_length + 4 + 8 - (packet_length % 8); -type SSH2_Key_Exchange_Header = record { - packet_length : uint32; - padding_length: uint8; - msg_type : uint8; -} &let { - payload_length: uint32 = packet_length - padding_length - 2; -} &length=6; - -type SSH2_Key_Exchange(is_orig: bool) = record { - header : SSH2_Key_Exchange_Header; - payload : SSH2_Message(is_orig, header.msg_type, header.payload_length); - pad : bytestring &length=header.padding_length; -} &length=header.packet_length + 4; - type SSH1_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { SSH_SMSG_PUBLIC_KEY -> public_key: SSH1_PUBLIC_KEY(length); SSH_CMSG_SESSION_KEY -> session_key: SSH1_SESSION_KEY(length); @@ -160,19 +187,35 @@ type SSH1_SESSION_KEY(length: uint32) = record { flags : uint32; } &length=length; +type ssh1_mp_int = record { + len: uint16; + val: bytestring &length=(len+7)/8; +}; + +## SSH2 + +type SSH2_Key_Exchange_Header = record { + packet_length : uint32; + padding_length: uint8; + msg_type : uint8; +} &let { + payload_length: uint32 = packet_length - padding_length - 2; +} &length=6; + +type SSH2_Key_Exchange(is_orig: bool) = record { + header : SSH2_Key_Exchange_Header; + payload : SSH2_Message(is_orig, header.msg_type, header.payload_length); + pad : bytestring &length=header.padding_length; +} &length=header.packet_length + 4; + type SSH2_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { - MSG_KEXINIT -> kexinit: SSH_KEXINIT(length); - MSG_KEX_DH_GEX_REQUEST -> dh_gex_request: SSH_DH_GEX_REQUEST(length); - MSG_KEX_DH_GEX_REQUEST_OLD -> dh_gex_request_old: SSH_DH_GEX_REQUEST_OLD(length); - MSG_KEX_DH_GEX_GROUP -> dh_gex_group: SSH_DH_GEX_GROUP(length); - MSG_KEX_DH_GEX_INIT -> dh_gex_init: SSH_DH_GEX_INIT(length); - MSG_KEX_DH_GEX_REPLY -> dh_gex_reply: SSH_DH_GEX_REPLY(length); - MSG_NEWKEYS -> new_keys: bytestring &length=length; + MSG_KEXINIT -> kexinit: SSH_KEXINIT(length, is_orig); + default -> unknown: bytestring &length=length; } &let { detach: bool = $context.connection.update_state(ENCRYPTED, is_orig) &if(msg_type == MSG_NEWKEYS); }; -type SSH_KEXINIT(length: uint32) = record { +type SSH_KEXINIT(length: uint32, is_orig: bool) = record { cookie : bytestring &length=16; kex_algorithms : ssh_string; server_host_key_algorithms : ssh_string; @@ -182,21 +225,52 @@ type SSH_KEXINIT(length: uint32) = record { mac_algorithms_server_to_client : ssh_string; compression_algorithms_client_to_server : ssh_string; compression_algorithms_server_to_client : ssh_string; - languages_client_to_server : ssh_string; - languages_server_to_client : ssh_string; - first_kex_packet_follows : uint8; - reserved : uint32; + languages_client_to_server : ssh_string; + languages_server_to_client : ssh_string; + first_kex_packet_follows : uint8; + reserved : uint32; +} &let { + proc_kex : bool = $context.connection.update_kex(kex_algorithms.val, is_orig); } &length=length; -type SSH_DH_GEX_REQUEST(length: uint32) = record { +# KEX_DH exchanges + +type SSH_Key_Exchange_DH(is_orig: bool) = record { + header : SSH2_Key_Exchange_Header; + payload : SSH_Key_Exchange_DH_Message(is_orig, header.msg_type, header.payload_length); + pad : bytestring &length=header.padding_length; +} &length=header.packet_length + 4; + +type SSH_Key_Exchange_DH_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { + SSH_MSG_KEXDH_INIT -> init : SSH_DH_GEX_INIT(length); + SSH_MSG_KEXDH_REPLY -> reply : SSH_DH_GEX_REPLY(length); +}; + +# KEX_DH_GEX exchanges + +type SSH_Key_Exchange_DH_GEX(is_orig: bool) = record { + header : SSH2_Key_Exchange_Header; + payload : SSH_Key_Exchange_DH_GEX_Message(is_orig, header.msg_type, header.payload_length); + pad : bytestring &length=header.padding_length; +} &length=header.packet_length + 4; + +type SSH_Key_Exchange_DH_GEX_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { + SSH_MSG_KEX_DH_GEX_REQUEST_OLD -> request_old : SSH_DH_GEX_REQUEST_OLD; + SSH_MSG_KEX_DH_GEX_REQUEST -> request : SSH_DH_GEX_REQUEST; + SSH_MSG_KEX_DH_GEX_GROUP -> group : SSH_DH_GEX_GROUP(length); + SSH_MSG_KEX_DH_GEX_INIT -> init : SSH_DH_GEX_INIT(length); + SSH_MSG_KEX_DH_GEX_REPLY -> reply : SSH_DH_GEX_REPLY(length); +}; + +type SSH_DH_GEX_REQUEST = record { min: uint32; n : uint32; max: uint32; } &length=12; -type SSH_DH_GEX_REQUEST_OLD(length: uint32) = record { - payload: bytestring &length=length; -} &length=length; +type SSH_DH_GEX_REQUEST_OLD = record { + n: uint32; +} &length=4; type SSH_DH_GEX_GROUP(length: uint32) = record { p: ssh_string; @@ -213,9 +287,106 @@ type SSH_DH_GEX_REPLY(length: uint32) = record { signature: ssh_string; } &length=length; -type ssh1_mp_int = record { - len: uint16; - val: bytestring &length=(len+7)/8; +# KEX_RSA exchanges + +type SSH_Key_Exchange_RSA(is_orig: bool) = record { + header : SSH2_Key_Exchange_Header; + payload : SSH_Key_Exchange_RSA_Message(is_orig, header.msg_type, header.payload_length); + pad : bytestring &length=header.padding_length; +} &length=header.packet_length + 4; + +type SSH_Key_Exchange_RSA_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { + SSH_MSG_KEXRSA_PUBKEY -> pubkey : SSH_RSA_PUBKEY(length); + SSH_MSG_KEXRSA_SECRET -> secret : SSH_RSA_SECRET(length); + SSH_MSG_KEXRSA_DONE -> done : SSH_RSA_DONE(length); +}; + +type SSH_RSA_PUBKEY(length: uint32) = record { + k_s: ssh_string; + k_t: ssh_string; +} &length=length; + +type SSH_RSA_SECRET(length: uint32) = record { + encrypted_payload: ssh_string; +} &length=length; + +type SSH_RSA_DONE(length: uint32) = record { + signature: ssh_string; +} &length=length; + +# KEX_GSS exchanges + +type SSH_Key_Exchange_GSS(is_orig: bool) = record { + header : SSH2_Key_Exchange_Header; + payload : SSH_Key_Exchange_GSS_Message(is_orig, header.msg_type, header.payload_length); + pad : bytestring &length=header.padding_length; +} &length=header.packet_length + 4; + +type SSH_Key_Exchange_GSS_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { + SSH_MSG_KEXGSS_INIT -> init : SSH_GSS_INIT(length); + SSH_MSG_KEXGSS_CONTINUE -> cont : SSH_GSS_CONTINUE(length); + SSH_MSG_KEXGSS_COMPLETE -> complete : SSH_GSS_COMPLETE(length); + SSH_MSG_KEXGSS_HOSTKEY -> hostkey : SSH_GSS_HOSTKEY(length); + SSH_MSG_KEXGSS_ERROR -> error : SSH_GSS_ERROR(length); + SSH_MSG_KEXGSS_GROUPREQ -> groupreq : SSH_DH_GEX_REQUEST; + SSH_MSG_KEXGSS_GROUP -> group : SSH_DH_GEX_GROUP(length); +}; + +type SSH_GSS_INIT(length: uint32) = record { + output_token: ssh_string; + e : ssh_string; +} &length=length; + +type SSH_GSS_CONTINUE(length: uint32) = record { + output_token: ssh_string; +} &length=length; + +type SSH_GSS_COMPLETE(length: uint32) = record { + f : ssh_string; + per_msg_token : ssh_string; + have_token : uint8; + parse_token : case have_token of { + 0 -> no_token: empty; + default -> token: ssh_string; + }; +} &length=length; + +type SSH_GSS_HOSTKEY(length: uint32) = record { + k_s: ssh_string; +} &length=length; + +type SSH_GSS_ERROR(length: uint32) = record { + major_status: uint32; + minor_status: uint32; + message : ssh_string; + language : ssh_string; +} &length=length; + +# KEX_ECDH and KEX_ECMQV exchanges + +type SSH_Key_Exchange_ECC(is_orig: bool) = record { + header : SSH2_Key_Exchange_Header; + payload : SSH_Key_Exchange_ECC_Message(is_orig, header.msg_type, header.payload_length); + pad : bytestring &length=header.padding_length; +} &length=header.packet_length + 4; + +type SSH_Key_Exchange_ECC_Message(is_orig: bool, msg_type: uint8, length: uint32) = case msg_type of { + SSH_MSG_KEX_ECDH_INIT -> init : SSH_ECC_INIT(length); + SSH_MSG_KEX_ECDH_REPLY -> reply : SSH_ECC_REPLY(length); +}; + +# This deviates from the RFC. SSH_MSG_KEX_ECDH_INIT and +# SSH_MSG_KEX_ECMQV_INIT can be parsed the same way. +type SSH_ECC_INIT(length: uint32) = record { + q_c: ssh_string; +}; + +# This deviates from the RFC. SSH_MSG_KEX_ECDH_REPLY and +# SSH_MSG_KEX_ECMQV_REPLY can be parsed the same way. +type SSH_ECC_REPLY(length: uint32) = record { + k_s : ssh_string; + q_s : ssh_string; + signature : ssh_string; }; type ssh_string = record { @@ -223,17 +394,35 @@ type ssh_string = record { val: bytestring &length=len; }; +type ssh_host_key = record { + len: uint32; + key_type: ssh_string; + key: ssh_string; +} &length=(len + 4); + +## Done with types + refine connection SSH_Conn += { %member{ int state_up_; int state_down_; int version_; + + bool kex_orig_; + bool kex_seen_; + bytestring kex_algs_cache_; + bytestring kex_algorithm_; %} %init{ state_up_ = VERSION_EXCHANGE; state_down_ = VERSION_EXCHANGE; version_ = UNK; + + kex_seen_ = false; + kex_orig_ = false; + kex_algs_cache_ = bytestring(); + kex_algorithm_ = bytestring(); %} function get_state(is_orig: bool): int @@ -274,4 +463,103 @@ refine connection SSH_Conn += { return true; %} + function update_kex_state_if_equal(s: string, to_state: state): bool + %{ + if ( strcmp(c_str(kex_algorithm_), s.c_str()) == 0 ) + { + update_state(to_state, true); + update_state(to_state, false); + return true; + } + return false; + %} + + function update_kex_state_if_startswith(s: string, to_state: state): bool + %{ + if ( (uint) kex_algorithm_.length() < s.length() ) + return false; + + if ( strcmp(std_str(kex_algorithm_).substr(0, s.length()).c_str(), s.c_str()) == 0 ) + { + update_state(to_state, true); + update_state(to_state, false); + return true; + } + return false; + %} + + function update_kex(algs: bytestring, orig: bool): bool + %{ + if ( !kex_seen_ ) + { + kex_seen_ = true; + kex_orig_ = orig; + kex_algs_cache_.init(${algs}.data(), ${algs}.length()); + + return false; + } + else if ( kex_orig_ == orig ) + return false; + + VectorVal* client_list = name_list_to_vector(orig ? algs : kex_algs_cache_); + VectorVal* server_list = name_list_to_vector(orig ? kex_algs_cache_ : algs); + + for ( unsigned int i = 0; i < client_list->Size(); ++i) + { + for ( unsigned int j = 0; j < server_list->Size(); ++j) + { + if ( strcmp((const char *) client_list->Lookup(i)->AsStringVal()->Bytes(), + (const char *) server_list->Lookup(j)->AsStringVal()->Bytes()) == 0 ) + { + kex_algorithm_.init((const uint8 *) client_list->Lookup(i)->AsStringVal()->Bytes(), + client_list->Lookup(i)->AsStringVal()->Len()); + + // UNTESTED + if ( update_kex_state_if_equal("rsa1024-sha1", KEX_RSA) ) + return true; + // UNTESTED + if ( update_kex_state_if_equal("rsa2048-sha256", KEX_RSA) ) + return true; + + // UNTESTED + if ( update_kex_state_if_equal("diffie-hellman-group1-sha1", KEX_DH) ) + return true; + // UNTESTED + if ( update_kex_state_if_equal("diffie-hellman-group14-sha1", KEX_DH) ) + return true; + + if ( update_kex_state_if_equal("diffie-hellman-group-exchange-sha1", KEX_DH_GEX) ) + return true; + if ( update_kex_state_if_equal("diffie-hellman-group-exchange-sha256", KEX_DH_GEX) ) + return true; + + if ( update_kex_state_if_startswith("gss-group1-sha1-", KEX_GSS) ) + return true; + if ( update_kex_state_if_startswith("gss-group14-sha1-", KEX_GSS) ) + return true; + if ( update_kex_state_if_startswith("gss-gex-sha1-", KEX_GSS) ) + return true; + if ( update_kex_state_if_startswith("gss-", KEX_GSS) ) + return true; + + if ( update_kex_state_if_startswith("ecdh-sha2-", KEX_ECC) ) + return true; + if ( update_kex_state_if_equal("ecmqv-sha2", KEX_ECC) ) + return true; + if ( update_kex_state_if_equal("curve25519-sha256@libssh.org", KEX_ECC) ) + return true; + + + bro_analyzer()->Weird(fmt("ssh_unknown_kex_algorithm=%s", c_str(kex_algorithm_))); + return true; + + } + } + } + + return true; + + %} + + }; \ No newline at end of file diff --git a/src/analyzer/protocol/ssh/ssh.pac b/src/analyzer/protocol/ssh/ssh.pac index b3181c4fa1..2358f056da 100644 --- a/src/analyzer/protocol/ssh/ssh.pac +++ b/src/analyzer/protocol/ssh/ssh.pac @@ -8,6 +8,7 @@ %include bro.pac %extern{ + #include "types.bif.h" #include "events.bif.h" %} diff --git a/src/analyzer/protocol/ssh/types.bif b/src/analyzer/protocol/ssh/types.bif new file mode 100644 index 0000000000..38e51600f3 --- /dev/null +++ b/src/analyzer/protocol/ssh/types.bif @@ -0,0 +1,5 @@ +module SSH; + +type Capabilities: record; + +module GLOBAL; \ No newline at end of file From 272916c18922575add906fde9ec85be9e03d8a19 Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Tue, 13 Jan 2015 14:39:25 -0500 Subject: [PATCH 079/299] Updating MySQL with Robin's suggestions: - Use a boolean success instead of a result string - Change the affected_rows response detail string to a "rows" count - Fix the state tracking to log incomplete commands --- scripts/base/protocols/mysql/main.bro | 42 ++++++++++++------ .../mysql.log | 32 +++++++------- .../mysql.log | 44 +++++++++---------- 3 files changed, 67 insertions(+), 51 deletions(-) diff --git a/scripts/base/protocols/mysql/main.bro b/scripts/base/protocols/mysql/main.bro index d0d3d4b3d6..49bb6e7564 100644 --- a/scripts/base/protocols/mysql/main.bro +++ b/scripts/base/protocols/mysql/main.bro @@ -18,8 +18,10 @@ export { cmd: string &log; ## The argument issued to the command arg: string &log; - ## The result (error, OK, etc.) from the server - result: string &log &optional; + ## Did the server tell us that the command succeeded? + success: bool &log &optional; + ## The number of affected rows, if any + rows: count &log &optional; ## Server message, if any response: string &log &optional; }; @@ -57,16 +59,21 @@ event mysql_handshake(c: connection, username: string) event mysql_command_request(c: connection, command: count, arg: string) &priority=5 { - if ( ! c?$mysql ) + if ( c?$mysql ) { - local info: Info; - info$ts = network_time(); - info$uid = c$uid; - info$id = c$id; - info$cmd = commands[command]; - info$arg = sub(arg, /\0$/, ""); - c$mysql = info; + # We got a request, but we haven't logged our + # previous request yet, so let's do that now. + Log::write(mysql::LOG, c$mysql); + delete c$mysql; } + + local info: Info; + info$ts = network_time(); + info$uid = c$uid; + info$id = c$id; + info$cmd = commands[command]; + info$arg = sub(arg, /\0$/, ""); + c$mysql = info; } event mysql_command_request(c: connection, command: count, arg: string) &priority=-5 @@ -83,7 +90,7 @@ event mysql_error(c: connection, code: count, msg: string) &priority=5 { if ( c?$mysql ) { - c$mysql$result = "error"; + c$mysql$success = F; c$mysql$response = msg; } } @@ -101,8 +108,8 @@ event mysql_ok(c: connection, affected_rows: count) &priority=5 { if ( c?$mysql ) { - c$mysql$result = "ok"; - c$mysql$response = fmt("Affected rows: %d", affected_rows); + c$mysql$success = T; + c$mysql$rows = affected_rows; } } @@ -114,3 +121,12 @@ event mysql_ok(c: connection, affected_rows: count) &priority=-5 delete c$mysql; } } + +event connection_state_remove(c: connection) &priority=-5 + { + if ( c?$mysql ) + { + Log::write(mysql::LOG, c$mysql); + delete c$mysql; + } + } \ No newline at end of file diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.auth/mysql.log b/testing/btest/Baseline/scripts.base.protocols.mysql.auth/mysql.log index 536ac3aaac..df7f3b800b 100644 --- a/testing/btest/Baseline/scripts.base.protocols.mysql.auth/mysql.log +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.auth/mysql.log @@ -3,19 +3,19 @@ #empty_field (empty) #unset_field - #path mysql -#open 2014-09-05-03-02-01 -#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p cmd arg result response -#types time string addr port addr port string string string string -1362452327.618353 CsRx2w45OKnoww6xl4 192.168.1.3 55845 192.168.1.8 3306 login root_nope error Access denied for user 'root_nope'@'lumberjack.home' (using password: NO) -1362452330.947463 CRJuHdVW0XPVINV8a 192.168.1.3 55846 192.168.1.8 3306 login root_nope error Access denied for user 'root_nope'@'lumberjack.home' (using password: YES) -1362452332.571339 CPbrpk1qSsw6ESzHV4 192.168.1.3 55847 192.168.1.8 3306 login root_nope error Access denied for user 'root_nope'@'lumberjack.home' (using password: YES) -1362452334.559420 C6pKV8GSxOnSLghOa 192.168.1.3 55857 192.168.1.8 3306 login root_nope error Access denied for user 'root_nope'@'lumberjack.home' (using password: YES) -1362452336.361958 CIPOse170MGiRM1Qf4 192.168.1.3 55860 192.168.1.8 3306 login root_nope error Access denied for user 'root_nope'@'lumberjack.home' (using password: YES) -1362452357.320858 C7XEbhP654jzLoe3a 192.168.1.3 55861 192.168.1.8 3306 login root error Access denied for user 'root'@'lumberjack.home' (using password: NO) -1362452358.565340 CJ3xTn1c4Zw9TmAE05 192.168.1.3 55862 192.168.1.8 3306 login root error Access denied for user 'root'@'lumberjack.home' (using password: YES) -1362452360.410803 CMXxB5GvmoxJFXdTa 192.168.1.3 55863 192.168.1.8 3306 login root error Access denied for user 'root'@'lumberjack.home' (using password: YES) -1362452361.886123 Caby8b1slFea8xwSmb 192.168.1.3 55864 192.168.1.8 3306 login root error Access denied for user 'root'@'lumberjack.home' (using password: YES) -1362452372.452858 Che1bq3i2rO3KD1Syg 192.168.1.3 55865 192.168.1.8 3306 login root ok Affected rows: 0 -1362452372.454995 Che1bq3i2rO3KD1Syg 192.168.1.3 55865 192.168.1.8 3306 query select @@version_comment limit 1 ok Affected rows: 1 -1362452372.991997 Che1bq3i2rO3KD1Syg 192.168.1.3 55865 192.168.1.8 3306 quit (empty) - - -#close 2014-09-05-03-02-01 +#open 2015-01-13-18-11-40 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p cmd arg success rows response +#types time string addr port addr port string string bool count string +1362452327.618353 CsRx2w45OKnoww6xl4 192.168.1.3 55845 192.168.1.8 3306 login root_nope F - Access denied for user 'root_nope'@'lumberjack.home' (using password: NO) +1362452330.947463 CRJuHdVW0XPVINV8a 192.168.1.3 55846 192.168.1.8 3306 login root_nope F - Access denied for user 'root_nope'@'lumberjack.home' (using password: YES) +1362452332.571339 CPbrpk1qSsw6ESzHV4 192.168.1.3 55847 192.168.1.8 3306 login root_nope F - Access denied for user 'root_nope'@'lumberjack.home' (using password: YES) +1362452334.559420 C6pKV8GSxOnSLghOa 192.168.1.3 55857 192.168.1.8 3306 login root_nope F - Access denied for user 'root_nope'@'lumberjack.home' (using password: YES) +1362452336.361958 CIPOse170MGiRM1Qf4 192.168.1.3 55860 192.168.1.8 3306 login root_nope F - Access denied for user 'root_nope'@'lumberjack.home' (using password: YES) +1362452357.320858 C7XEbhP654jzLoe3a 192.168.1.3 55861 192.168.1.8 3306 login root F - Access denied for user 'root'@'lumberjack.home' (using password: NO) +1362452358.565340 CJ3xTn1c4Zw9TmAE05 192.168.1.3 55862 192.168.1.8 3306 login root F - Access denied for user 'root'@'lumberjack.home' (using password: YES) +1362452360.410803 CMXxB5GvmoxJFXdTa 192.168.1.3 55863 192.168.1.8 3306 login root F - Access denied for user 'root'@'lumberjack.home' (using password: YES) +1362452361.886123 Caby8b1slFea8xwSmb 192.168.1.3 55864 192.168.1.8 3306 login root F - Access denied for user 'root'@'lumberjack.home' (using password: YES) +1362452372.452858 Che1bq3i2rO3KD1Syg 192.168.1.3 55865 192.168.1.8 3306 login root T 0 - +1362452372.454995 Che1bq3i2rO3KD1Syg 192.168.1.3 55865 192.168.1.8 3306 query select @@version_comment limit 1 T 1 - +1362452372.991997 Che1bq3i2rO3KD1Syg 192.168.1.3 55865 192.168.1.8 3306 quit (empty) - - - +#close 2015-01-13-18-11-40 diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.wireshark/mysql.log b/testing/btest/Baseline/scripts.base.protocols.mysql.wireshark/mysql.log index 7baf2954ae..a35f1a136d 100644 --- a/testing/btest/Baseline/scripts.base.protocols.mysql.wireshark/mysql.log +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.wireshark/mysql.log @@ -3,25 +3,25 @@ #empty_field (empty) #unset_field - #path mysql -#open 2014-09-05-03-02-01 -#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p cmd arg result response -#types time string addr port addr port string string string string -1216281025.136728 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 login tfoerste ok Affected rows: 0 -1216281025.137062 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query select @@version_comment limit 1 ok Affected rows: 1 -1216281030.835001 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query SELECT DATABASE() ok Affected rows: 1 -1216281030.835395 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 init_db test ok Affected rows: 0 -1216281030.835742 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query show databases ok Affected rows: 1 -1216281030.836349 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query show tables ok Affected rows: 1 -1216281030.836757 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 field_list agent ok Affected rows: 3 -1216281048.287657 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query create table foo (id BIGINT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, animal VARCHAR(64) NOT NULL, name VARCHAR(64) NULL DEFAULT NULL) ENGINE = MYISAM ok Affected rows: 0 -1216281057.746222 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query insert into foo (animal, name) values ("dog", "Goofy") ok Affected rows: 1 -1216281061.713980 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query insert into foo (animal, name) values ("cat", "Garfield") ok Affected rows: 1 -1216281066.549786 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query select * from foo ok Affected rows: 3 -1216281072.304467 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query delete from foo where name like '%oo%' ok Affected rows: 1 -1216281079.450037 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query delete from foo where id = 1 ok Affected rows: 0 -1216281087.437392 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query select count(*) from foo ok Affected rows: 1 -1216281109.107769 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query select * from foo ok Affected rows: 3 -1216281116.209268 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query delete from foo ok Affected rows: 1 -1216281122.880561 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query drop table foo ok Affected rows: 0 -1216281124.418765 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 quit (empty) - - -#close 2014-09-05-03-02-01 +#open 2015-01-13-18-12-10 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p cmd arg success rows response +#types time string addr port addr port string string bool count string +1216281025.136728 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 login tfoerste T 0 - +1216281025.137062 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query select @@version_comment limit 1 T 1 - +1216281030.835001 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query SELECT DATABASE() T 1 - +1216281030.835395 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 init_db test T 0 - +1216281030.835742 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query show databases T 1 - +1216281030.836349 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query show tables T 1 - +1216281030.836757 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 field_list agent T 3 - +1216281048.287657 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query create table foo (id BIGINT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, animal VARCHAR(64) NOT NULL, name VARCHAR(64) NULL DEFAULT NULL) ENGINE = MYISAM T 0 - +1216281057.746222 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query insert into foo (animal, name) values ("dog", "Goofy") T 1 - +1216281061.713980 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query insert into foo (animal, name) values ("cat", "Garfield") T 1 - +1216281066.549786 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query select * from foo T 3 - +1216281072.304467 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query delete from foo where name like '%oo%' T 1 - +1216281079.450037 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query delete from foo where id = 1 T 0 - +1216281087.437392 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query select count(*) from foo T 1 - +1216281109.107769 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query select * from foo T 3 - +1216281116.209268 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query delete from foo T 1 - +1216281122.880561 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 query drop table foo T 0 - +1216281124.418765 CXWv6p3arKYeMETxOg 192.168.0.254 56162 192.168.0.254 3306 quit (empty) - - - +#close 2015-01-13-18-12-10 From 1e8d6cd917d7d2a6f81c873d9113e6a18fb43df9 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 13 Jan 2015 17:14:21 -0600 Subject: [PATCH 080/299] broker integration: add API for connecting to peers --- CMakeLists.txt | 2 + aux/broker | 2 +- src/CMakeLists.txt | 4 ++ src/comm/CMakeLists.txt | 15 ++++++ src/comm/Manager.cc | 115 ++++++++++++++++++++++++++++++++++++++++ src/comm/Manager.h | 52 ++++++++++++++++++ src/comm/comm.bif | 43 +++++++++++++++ src/main.cc | 22 ++++++++ 8 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 src/comm/CMakeLists.txt create mode 100644 src/comm/Manager.cc create mode 100644 src/comm/Manager.h create mode 100644 src/comm/comm.bif diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f0fcf8d07..b31e60ac01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,6 +181,8 @@ if ( ENABLE_BROKER ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") add_subdirectory(aux/broker) set(brodeps ${brodeps} broker) + add_definitions(-DENABLE_BROKER) + include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/aux/broker) endif () add_subdirectory(src) diff --git a/aux/broker b/aux/broker index a1b51def07..331966d1f3 160000 --- a/aux/broker +++ b/aux/broker @@ -1 +1 @@ -Subproject commit a1b51def07cfb191d0a83a78c7102560740dbcb3 +Subproject commit 331966d1f3d24c63bedbda79e477f759c4d267f9 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 13c6e45006..55ca12c873 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -161,6 +161,10 @@ add_subdirectory(iosource) add_subdirectory(logging) add_subdirectory(probabilistic) +if ( ENABLE_BROKER ) + add_subdirectory(comm) +endif () + set(bro_SUBDIRS # Order is important here. ${bro_PLUGIN_LIBS} diff --git a/src/comm/CMakeLists.txt b/src/comm/CMakeLists.txt new file mode 100644 index 0000000000..c152adc49a --- /dev/null +++ b/src/comm/CMakeLists.txt @@ -0,0 +1,15 @@ +include(BroSubdir) + +include_directories(BEFORE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} +) + +set(comm_SRCS + Manager.cc +) + +bif_target(comm.bif) + +bro_add_subdir_library(comm ${comm_SRCS} ${BIF_OUTPUT_CC}) +add_dependencies(bro_comm generate_outputs) diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc new file mode 100644 index 0000000000..ac67208ca6 --- /dev/null +++ b/src/comm/Manager.cc @@ -0,0 +1,115 @@ +#include "Manager.h" +#include +#include +#include +#include "util.h" +#include "Reporter.h" + +bool comm::Manager::InitPreScript() + { + auto res = broker::init(); + + if ( res ) + { + fprintf(stderr, "broker::init failed: %s\n", broker::strerror(res)); + return false; + } + + char host[256]; + const char* name; + + if ( gethostname(host, sizeof(host)) == 0 ) + name = fmt("bro@%s.%ld", host, static_cast(getpid())); + else + name = fmt("bro@.%ld", static_cast(getpid())); + + endpoint = std::unique_ptr(new broker::endpoint(name)); + return true; + } + +bool comm::Manager::InitPostScript() + { + return true; + } + +bool comm::Manager::Listen(uint16_t port, const char* addr) + { + auto rval = endpoint->listen(port, addr); + + if ( ! rval ) + { + reporter->Error("Failed to listen on %s:%" PRIu16 " : %s", + addr ? addr : "INADDR_ANY", port, + endpoint->last_error().data()); + } + + return rval; + } + +bool comm::Manager::Connect(string addr, uint16_t port, + std::chrono::duration retry_interval) + { + auto& peer = peers[std::make_pair(addr, port)]; + + if ( peer ) + return false; + + peer = endpoint->peer(std::move(addr), port, retry_interval); + return true; + } + +bool comm::Manager::Disconnect(const string& addr, uint16_t port) + { + auto it = peers.find(std::make_pair(addr, port)); + + if ( it == peers.end() ) + return false; + + return endpoint->unpeer(it->second); + } + +void comm::Manager::GetFds(iosource::FD_Set* read, iosource::FD_Set* write, + iosource::FD_Set* except) + { + read->Insert(endpoint->peer_status().fd()); + } + +double comm::Manager::NextTimestamp(double* local_network_time) + { + // TODO: do something better? + return timer_mgr->Time(); + } + +void comm::Manager::Process() + { + bool idle = true; + auto peer_status_updates = endpoint->peer_status().want_pop(); + + if ( ! peer_status_updates.empty() ) + idle = false; + + for ( auto& u : peer_status_updates ) + { + if ( ! u.relation.remote() ) + continue; + + // TODO: generate events + switch ( u.status ) { + case broker::peer_status::tag::established: + printf("established\n"); + break; + case broker::peer_status::tag::disconnected: + printf("disconnected\n"); + break; + case broker::peer_status::tag::incompatible: + printf("incompatible\n"); + break; + default: + reporter->InternalWarning("unknown broker::peer_status::tag : %d", + static_cast(u.status)); + break; + } + } + + SetIdle(idle); + } diff --git a/src/comm/Manager.h b/src/comm/Manager.h new file mode 100644 index 0000000000..412c125d14 --- /dev/null +++ b/src/comm/Manager.h @@ -0,0 +1,52 @@ +#ifndef BRO_COMM_MANAGER_H +#define BRO_COMM_MANAGER_H + +#include +#include +#include +#include +#include "Reporter.h" +#include "iosource/IOSource.h" + +namespace comm { + +// TODO: documentation + +// Manages various forms of communication between peer Bro processes +// or possibly between different parts of a single Bro process. +class Manager : public iosource::IOSource { +public: + + bool InitPreScript(); + + bool InitPostScript(); + + bool Listen(uint16_t port, const char* addr = nullptr); + + bool Connect(std::string addr, uint16_t port, + std::chrono::duration retry_interval); + + bool Disconnect(const std::string& addr, uint16_t port); + +private: + + // IOSource interface overrides: + void GetFds(iosource::FD_Set* read, iosource::FD_Set* write, + iosource::FD_Set* except) override; + + double NextTimestamp(double* local_network_time) override; + + void Process() override; + + const char* Tag() override + { return "Comm::Manager"; } + + std::unique_ptr endpoint; + std::map, broker::peering> peers; +}; + +} // namespace comm + +extern comm::Manager* comm_mgr; + +#endif // BRO_COMM_MANAGER_H diff --git a/src/comm/comm.bif b/src/comm/comm.bif new file mode 100644 index 0000000000..ce54b916ca --- /dev/null +++ b/src/comm/comm.bif @@ -0,0 +1,43 @@ + +module Comm; + +%%{ +#include "comm/Manager.h" +%%} + +function Comm::listen%(p: port, a: string &default=""%): bool + %{ + if ( ! p->IsTCP() ) + { + reporter->Error("listen port must use tcp"); + return new Val(false, TYPE_BOOL); + } + + auto rval = comm_mgr->Listen(p->Port(), a->Len() ? a->CheckString() : 0); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::connect%(a: string, p: port, retry: interval%): bool + %{ + if ( ! p->IsTCP() ) + { + reporter->Error("remote connection port must use tcp"); + return new Val(false, TYPE_BOOL); + } + + auto rval = comm_mgr->Connect(a->CheckString(), p->Port(), + std::chrono::duration(retry)); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::disconnect%(a: string, p: port%): bool + %{ + if ( ! p->IsTCP() ) + { + reporter->Error("remote connection port must use tcp"); + return new Val(false, TYPE_BOOL); + } + + auto rval = comm_mgr->Disconnect(a->CheckString(), p->Port()); + return new Val(rval, TYPE_BOOL); + %} diff --git a/src/main.cc b/src/main.cc index 15aea3d3fe..a7099cb90b 100644 --- a/src/main.cc +++ b/src/main.cc @@ -63,6 +63,10 @@ extern "C" void OPENSSL_add_all_algorithms_conf(void); #include "3rdparty/sqlite3.h" +#ifdef ENABLE_BROKER +#include +#endif + Brofiler brofiler; #ifndef HAVE_STRSEP @@ -94,6 +98,9 @@ analyzer::Manager* analyzer_mgr = 0; file_analysis::Manager* file_mgr = 0; broxygen::Manager* broxygen_mgr = 0; iosource::Manager* iosource_mgr = 0; +#ifdef ENABLE_BROKER +comm::Manager* comm_mgr = 0; +#endif Stmt* stmts; EventHandlerPtr net_done = 0; RuleMatcher* rule_matcher = 0; @@ -851,6 +858,16 @@ int main(int argc, char** argv) input_mgr = new input::Manager(); file_mgr = new file_analysis::Manager(); +#ifdef ENABLE_BROKER + comm_mgr = new comm::Manager(); + + if ( ! comm_mgr->InitPreScript() ) + { + fprintf(stderr, "Failed to initialize communication manager."); + exit(1); + } +#endif + plugin_mgr->InitPreScript(); analyzer_mgr->InitPreScript(); file_mgr->InitPreScript(); @@ -925,6 +942,11 @@ int main(int argc, char** argv) exit(rc); } +#ifdef ENABLE_BROKER + comm_mgr->InitPostScript(); + iosource_mgr->Register(comm_mgr); +#endif + #ifdef USE_PERFTOOLS_DEBUG } #endif From 0daa954ddbff2bdb991b3ade0312a7fcf03b76aa Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 14 Jan 2015 10:40:11 -0600 Subject: [PATCH 081/299] broker integration: add remote connection status events. --- scripts/base/frameworks/comm/__load__.bro | 1 + scripts/base/frameworks/comm/main.bro | 7 +++ scripts/base/init-bare.bro | 1 + src/comm/Manager.cc | 62 ++++++++++++++++++----- src/comm/comm.bif | 8 +++ 5 files changed, 66 insertions(+), 13 deletions(-) create mode 100644 scripts/base/frameworks/comm/__load__.bro create mode 100644 scripts/base/frameworks/comm/main.bro diff --git a/scripts/base/frameworks/comm/__load__.bro b/scripts/base/frameworks/comm/__load__.bro new file mode 100644 index 0000000000..a10fe855df --- /dev/null +++ b/scripts/base/frameworks/comm/__load__.bro @@ -0,0 +1 @@ +@load ./main diff --git a/scripts/base/frameworks/comm/main.bro b/scripts/base/frameworks/comm/main.bro new file mode 100644 index 0000000000..af4225f5dd --- /dev/null +++ b/scripts/base/frameworks/comm/main.bro @@ -0,0 +1,7 @@ + +module Comm; + +export { + + const endpoint_name = "" &redef; +} diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 4a1bcfbe72..9d790e1e09 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -3358,6 +3358,7 @@ const bits_per_uid: count = 96 &redef; # Load these frameworks here because they use fairly deep integration with # BiFs and script-land defined types. +@load base/frameworks/comm @load base/frameworks/logging @load base/frameworks/input @load base/frameworks/analyzer diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index ac67208ca6..29ff71d7e0 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -3,9 +3,16 @@ #include #include #include "util.h" +#include "Var.h" #include "Reporter.h" +#include "comm/comm.bif.h" bool comm::Manager::InitPreScript() + { + return true; + } + +bool comm::Manager::InitPostScript() { auto res = broker::init(); @@ -15,23 +22,25 @@ bool comm::Manager::InitPreScript() return false; } - char host[256]; const char* name; + auto name_from_script = internal_val("Comm::endpoint_name")->AsString(); - if ( gethostname(host, sizeof(host)) == 0 ) - name = fmt("bro@%s.%ld", host, static_cast(getpid())); + if ( name_from_script->Len() ) + name = name_from_script->CheckString(); else - name = fmt("bro@.%ld", static_cast(getpid())); + { + char host[256]; + + if ( gethostname(host, sizeof(host)) == 0 ) + name = fmt("bro@%s.%ld", host, static_cast(getpid())); + else + name = fmt("bro@.%ld", static_cast(getpid())); + } endpoint = std::unique_ptr(new broker::endpoint(name)); return true; } -bool comm::Manager::InitPostScript() - { - return true; - } - bool comm::Manager::Listen(uint16_t port, const char* addr) { auto rval = endpoint->listen(port, addr); @@ -93,17 +102,44 @@ void comm::Manager::Process() if ( ! u.relation.remote() ) continue; - // TODO: generate events switch ( u.status ) { case broker::peer_status::tag::established: - printf("established\n"); + if ( Comm::remote_connection_established ) + { + val_list* vl = new val_list; + vl->append(new StringVal(u.relation.remote_tuple().first)); + vl->append(new PortVal(u.relation.remote_tuple().second, + TRANSPORT_TCP)); + vl->append(new StringVal(u.peer_name)); + mgr.QueueEvent(Comm::remote_connection_established, vl); + } + break; + case broker::peer_status::tag::disconnected: - printf("disconnected\n"); + if ( Comm::remote_connection_broken ) + { + val_list* vl = new val_list; + vl->append(new StringVal(u.relation.remote_tuple().first)); + vl->append(new PortVal(u.relation.remote_tuple().second, + TRANSPORT_TCP)); + mgr.QueueEvent(Comm::remote_connection_broken, vl); + } + break; + case broker::peer_status::tag::incompatible: - printf("incompatible\n"); + if ( Comm::remote_connection_incompatible ) + { + val_list* vl = new val_list; + vl->append(new StringVal(u.relation.remote_tuple().first)); + vl->append(new PortVal(u.relation.remote_tuple().second, + TRANSPORT_TCP)); + mgr.QueueEvent(Comm::remote_connection_incompatible, vl); + } + break; + default: reporter->InternalWarning("unknown broker::peer_status::tag : %d", static_cast(u.status)); diff --git a/src/comm/comm.bif b/src/comm/comm.bif index ce54b916ca..67933df20e 100644 --- a/src/comm/comm.bif +++ b/src/comm/comm.bif @@ -5,6 +5,14 @@ module Comm; #include "comm/Manager.h" %%} +event Comm::remote_connection_established%(peer_address: string, + peer_port: port, + peer_name: string%); +event Comm::remote_connection_broken%(peer_address: string, + peer_port: port%); +event Comm::remote_connection_incompatible%(peer_address: string, + peer_port: port%); + function Comm::listen%(p: port, a: string &default=""%): bool %{ if ( ! p->IsTCP() ) From 1e462481dc2e3106431ac9cd71c4ea1644c7881b Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 14 Jan 2015 13:28:34 -0600 Subject: [PATCH 082/299] broker integration: add remote printing --- aux/broker | 2 +- scripts/base/frameworks/comm/main.bro | 6 ++ src/comm/Manager.cc | 116 ++++++++++++++++++++++++-- src/comm/Manager.h | 14 ++++ src/comm/comm.bif | 30 ++++++- 5 files changed, 158 insertions(+), 10 deletions(-) diff --git a/aux/broker b/aux/broker index 331966d1f3..1e8d675790 160000 --- a/aux/broker +++ b/aux/broker @@ -1 +1 @@ -Subproject commit 331966d1f3d24c63bedbda79e477f759c4d267f9 +Subproject commit 1e8d6757909750524c15f8eaf3c297243bc55425 diff --git a/scripts/base/frameworks/comm/main.bro b/scripts/base/frameworks/comm/main.bro index af4225f5dd..c69d36db52 100644 --- a/scripts/base/frameworks/comm/main.bro +++ b/scripts/base/frameworks/comm/main.bro @@ -4,4 +4,10 @@ module Comm; export { const endpoint_name = "" &redef; + + type SendFlags: record { + self: bool &default = F; + peers: bool &default = T; + unsolicited: bool &default = F; + }; } diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index 29ff71d7e0..7027daa79e 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -7,13 +7,31 @@ #include "Reporter.h" #include "comm/comm.bif.h" +using namespace std; + bool comm::Manager::InitPreScript() { return true; } +static int require_field(const RecordType* rt, const char* name) + { + auto rval = rt->FieldOffset(name); + + if ( rval < 0 ) + reporter->InternalError("no field named '%s' in record type '%s'", name, + rt->GetName().data()); + + return rval; + } + bool comm::Manager::InitPostScript() { + auto send_flags_type = internal_type("Comm::SendFlags")->AsRecordType(); + send_flags_self_idx = require_field(send_flags_type, "self"); + send_flags_peers_idx = require_field(send_flags_type, "peers"); + send_flags_unsolicited_idx = require_field(send_flags_type, "unsolicited"); + auto res = broker::init(); if ( res ) @@ -37,7 +55,7 @@ bool comm::Manager::InitPostScript() name = fmt("bro@.%ld", static_cast(getpid())); } - endpoint = std::unique_ptr(new broker::endpoint(name)); + endpoint = unique_ptr(new broker::endpoint(name)); return true; } @@ -56,31 +74,81 @@ bool comm::Manager::Listen(uint16_t port, const char* addr) } bool comm::Manager::Connect(string addr, uint16_t port, - std::chrono::duration retry_interval) + chrono::duration retry_interval) { - auto& peer = peers[std::make_pair(addr, port)]; + auto& peer = peers[make_pair(addr, port)]; if ( peer ) return false; - peer = endpoint->peer(std::move(addr), port, retry_interval); + peer = endpoint->peer(move(addr), port, retry_interval); return true; } bool comm::Manager::Disconnect(const string& addr, uint16_t port) { - auto it = peers.find(std::make_pair(addr, port)); + auto it = peers.find(make_pair(addr, port)); if ( it == peers.end() ) return false; - return endpoint->unpeer(it->second); + auto rval = endpoint->unpeer(it->second); + peers.erase(it); + return rval; + } + +bool comm::Manager::Print(string topic, string msg, const Val* flags) + { + endpoint->send(move(topic), broker::message{move(msg)}, get_flags(flags)); + return true; + } + +bool comm::Manager::SubscribeToPrints(string topic_prefix) + { + auto& q = print_subscriptions[topic_prefix]; + + if ( q ) + return false; + + q = broker::message_queue(move(topic_prefix), *endpoint); + return true; + } + +bool comm::Manager::UnsubscribeToPrints(const string& topic_prefix) + { + return print_subscriptions.erase(topic_prefix); + } + +int comm::Manager::get_flags(const Val* flags) + { + auto r = flags->AsRecordVal(); + int rval = 0; + Val* self_flag = r->LookupWithDefault(send_flags_self_idx); + Val* peers_flag = r->LookupWithDefault(send_flags_peers_idx); + Val* unsolicited_flag = r->LookupWithDefault(send_flags_unsolicited_idx); + + if ( self_flag->AsBool() ) + rval |= broker::SELF; + + if ( peers_flag->AsBool() ) + rval |= broker::PEERS; + + if ( unsolicited_flag->AsBool() ) + rval |= broker::UNSOLICITED; + + Unref(self_flag); + Unref(peers_flag); + Unref(unsolicited_flag); + return rval; } void comm::Manager::GetFds(iosource::FD_Set* read, iosource::FD_Set* write, iosource::FD_Set* except) { read->Insert(endpoint->peer_status().fd()); + + for ( const auto& ps : print_subscriptions ) + read->Insert(ps.second.fd()); } double comm::Manager::NextTimestamp(double* local_network_time) @@ -147,5 +215,41 @@ void comm::Manager::Process() } } + for ( const auto& ps : print_subscriptions ) + { + auto print_messages = ps.second.want_pop(); + + if ( print_messages.empty() ) + continue; + + idle = false; + + if ( ! Comm::print_handler ) + continue; + + for ( auto& pm : print_messages ) + { + if ( pm.size() != 1 ) + { + reporter->Warning("got print message of invalid size: %zd", + pm.size()); + continue; + } + + std::string* msg = broker::get(pm[0]); + + if ( ! msg ) + { + reporter->Warning("got print message of invalid type: %d", + static_cast(broker::which(pm[0]))); + continue; + } + + val_list* vl = new val_list; + vl->append(new StringVal(move(*msg))); + mgr.QueueEvent(Comm::print_handler, vl); + } + } + SetIdle(idle); } diff --git a/src/comm/Manager.h b/src/comm/Manager.h index 412c125d14..0f7d5a4a1c 100644 --- a/src/comm/Manager.h +++ b/src/comm/Manager.h @@ -2,6 +2,7 @@ #define BRO_COMM_MANAGER_H #include +#include #include #include #include @@ -28,8 +29,16 @@ public: bool Disconnect(const std::string& addr, uint16_t port); + bool Print(std::string topic, std::string msg, const Val* flags); + + bool SubscribeToPrints(std::string topic_prefix); + + bool UnsubscribeToPrints(const std::string& topic_prefix); + private: + int get_flags(const Val* flags); + // IOSource interface overrides: void GetFds(iosource::FD_Set* read, iosource::FD_Set* write, iosource::FD_Set* except) override; @@ -43,6 +52,11 @@ private: std::unique_ptr endpoint; std::map, broker::peering> peers; + std::map print_subscriptions; + + int send_flags_self_idx; + int send_flags_peers_idx; + int send_flags_unsolicited_idx; }; } // namespace comm diff --git a/src/comm/comm.bif b/src/comm/comm.bif index 67933df20e..6294864bba 100644 --- a/src/comm/comm.bif +++ b/src/comm/comm.bif @@ -1,10 +1,12 @@ -module Comm; - %%{ #include "comm/Manager.h" %%} +module Comm; + +type Comm::SendFlags: record; + event Comm::remote_connection_established%(peer_address: string, peer_port: port, peer_name: string%); @@ -13,7 +15,7 @@ event Comm::remote_connection_broken%(peer_address: string, event Comm::remote_connection_incompatible%(peer_address: string, peer_port: port%); -function Comm::listen%(p: port, a: string &default=""%): bool +function Comm::listen%(p: port, a: string &default = ""%): bool %{ if ( ! p->IsTCP() ) { @@ -49,3 +51,25 @@ function Comm::disconnect%(a: string, p: port%): bool auto rval = comm_mgr->Disconnect(a->CheckString(), p->Port()); return new Val(rval, TYPE_BOOL); %} + +event Comm::print_handler%(msg: string%); + +function Comm::print%(topic: string, msg: string, + flags: SendFlags &default = SendFlags()%): bool + %{ + auto rval = comm_mgr->Print(topic->CheckString(), msg->CheckString(), + flags); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::subscribe_to_prints%(topic_prefix: string &default = ""%): bool + %{ + auto rval = comm_mgr->SubscribeToPrints(topic_prefix->CheckString()); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::unsubscribe_to_prints%(topic_prefix: string &default = ""%): bool + %{ + auto rval = comm_mgr->UnsubscribeToPrints(topic_prefix->CheckString()); + return new Val(rval, TYPE_BOOL); + %} From 7e563b7275394dcb72d44c288cdb5bd96f4dffb9 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Thu, 15 Jan 2015 15:45:08 -0600 Subject: [PATCH 083/299] broker integration: add remote events --- scripts/base/frameworks/comm/main.bro | 9 + src/comm/CMakeLists.txt | 1 + src/comm/Data.cc | 533 ++++++++++++++++++++++++++ src/comm/Data.h | 29 ++ src/comm/Manager.cc | 177 +++++++++ src/comm/Manager.h | 11 + src/comm/comm.bif | 42 +- 7 files changed, 800 insertions(+), 2 deletions(-) create mode 100644 src/comm/Data.cc create mode 100644 src/comm/Data.h diff --git a/scripts/base/frameworks/comm/main.bro b/scripts/base/frameworks/comm/main.bro index c69d36db52..efe3069a1c 100644 --- a/scripts/base/frameworks/comm/main.bro +++ b/scripts/base/frameworks/comm/main.bro @@ -10,4 +10,13 @@ export { peers: bool &default = T; unsolicited: bool &default = F; }; + + type Data: record { + d: opaque of Comm::Data &optional; + }; + + type EventArgs: record { + name: string &optional; # nil for invalid event/args. + args: vector of Comm::Data; + }; } diff --git a/src/comm/CMakeLists.txt b/src/comm/CMakeLists.txt index c152adc49a..95ad701d71 100644 --- a/src/comm/CMakeLists.txt +++ b/src/comm/CMakeLists.txt @@ -6,6 +6,7 @@ include_directories(BEFORE ) set(comm_SRCS + Data.cc Manager.cc ) diff --git a/src/comm/Data.cc b/src/comm/Data.cc new file mode 100644 index 0000000000..58d5b30085 --- /dev/null +++ b/src/comm/Data.cc @@ -0,0 +1,533 @@ +#include "Data.h" +#include "comm/comm.bif.h" + +using namespace std; + +OpaqueType* comm::opaque_of_data_type; + +static broker::port::protocol to_broker_port_proto(TransportProto tp) + { + switch ( tp ) { + case TRANSPORT_TCP: + return broker::port::protocol::tcp; + case TRANSPORT_UDP: + return broker::port::protocol::udp; + case TRANSPORT_ICMP: + return broker::port::protocol::icmp; + case TRANSPORT_UNKNOWN: + default: + return broker::port::protocol::unknown; + } + } + +static TransportProto to_bro_port_proto(broker::port::protocol tp) + { + switch ( tp ) { + case broker::port::protocol::tcp: + return TRANSPORT_TCP; + case broker::port::protocol::udp: + return TRANSPORT_UDP; + case broker::port::protocol::icmp: + return TRANSPORT_ICMP; + case broker::port::protocol::unknown: + default: + return TRANSPORT_UNKNOWN; + } + } + +struct val_converter { + using result_type = Val*; + + BroType* type; + + result_type operator()(bool a) + { + if ( type->Tag() == TYPE_BOOL ) + return new Val(a, TYPE_BOOL); + return nullptr; + } + + result_type operator()(uint64_t a) + { + if ( type->Tag() == TYPE_COUNT ) + return new Val(a, TYPE_COUNT); + if ( type->Tag() == TYPE_COUNTER ) + return new Val(a, TYPE_COUNTER); + return nullptr; + } + + result_type operator()(int64_t a) + { + if ( type->Tag() == TYPE_INT ) + return new Val(a, TYPE_INT); + return nullptr; + } + + result_type operator()(double a) + { + if ( type->Tag() == TYPE_DOUBLE ) + return new Val(a, TYPE_DOUBLE); + return nullptr; + } + + result_type operator()(const std::string& a) + { + switch ( type->Tag() ) { + case TYPE_STRING: + return new StringVal(a.size(), a.data()); + case TYPE_FILE: + { + auto file = BroFile::GetFile(a.data()); + + if ( file ) + { + Ref(file); + return new Val(file); + } + + return nullptr; + } + case TYPE_FUNC: + { + auto id = lookup_ID(a.data(), GLOBAL_MODULE_NAME); + auto rval = id ? id->ID_Val() : nullptr; + Unref(id); + + if ( rval && rval->Type()->Tag() == TYPE_FUNC ) + return rval; + + return nullptr; + } + default: + return nullptr; + } + } + + result_type operator()(const broker::address& a) + { + if ( type->Tag() == TYPE_ADDR ) + { + auto bits = reinterpret_cast(&a.bytes()); + return new AddrVal(IPAddr(*bits)); + } + + return nullptr; + } + + result_type operator()(const broker::subnet& a) + { + if ( type->Tag() == TYPE_SUBNET ) + { + auto bits = reinterpret_cast(&a.network().bytes()); + return new SubNetVal(IPPrefix(IPAddr(*bits), a.length())); + } + + return nullptr; + } + + result_type operator()(const broker::port& a) + { + if ( type->Tag() == TYPE_PORT ) + return new PortVal(a.number(), to_bro_port_proto(a.type())); + + return nullptr; + } + + result_type operator()(const broker::time_point& a) + { + if ( type->Tag() == TYPE_TIME ) + return new Val(a.value, TYPE_TIME); + + return nullptr; + } + + result_type operator()(const broker::time_duration& a) + { + if ( type->Tag() == TYPE_INTERVAL ) + return new Val(a.value, TYPE_INTERVAL); + + return nullptr; + } + + result_type operator()(const broker::enum_value& a) + { + if ( type->Tag() == TYPE_ENUM ) + { + auto etype = type->AsEnumType(); + auto i = etype->Lookup(GLOBAL_MODULE_NAME, a.name.data()); + + if ( i == -1 ) + return nullptr; + + return new EnumVal(i, etype); + } + + return nullptr; + } + + result_type operator()(broker::set& a) + { + if ( ! type->IsSet() ) + return nullptr; + + auto tt = type->AsTableType(); + auto rval = new TableVal(tt); + + for ( auto& item : a ) + { + auto indices = broker::get(item); + + if ( ! indices ) + { + Unref(rval); + return nullptr; + } + + auto expected_index_types = tt->Indices()->Types(); + + if ( expected_index_types->length() != indices->size() ) + { + Unref(rval); + return nullptr; + } + + auto list_val = new ListVal(TYPE_ANY); + + for ( auto i = 0u; i < indices->size(); ++i ) + { + auto index_val = comm::data_to_val(move((*indices)[i]), + (*expected_index_types)[i]); + + if ( ! index_val ) + { + Unref(rval); + Unref(list_val); + return nullptr; + } + + list_val->Append(index_val); + } + + + rval->Assign(list_val, nullptr); + Unref(list_val); + } + + return rval; + } + + result_type operator()(broker::table& a) + { + if ( ! type->IsTable() ) + return nullptr; + + auto tt = type->AsTableType(); + auto rval = new TableVal(tt); + + for ( auto& item : a ) + { + auto indices = broker::get(item.first); + + if ( ! indices ) + { + Unref(rval); + return nullptr; + } + + auto expected_index_types = tt->Indices()->Types(); + + if ( expected_index_types->length() != indices->size() ) + { + Unref(rval); + return nullptr; + } + + auto list_val = new ListVal(TYPE_ANY); + + for ( auto i = 0u; i < indices->size(); ++i ) + { + auto index_val = comm::data_to_val(move((*indices)[i]), + (*expected_index_types)[i]); + + if ( ! index_val ) + { + Unref(rval); + Unref(list_val); + return nullptr; + } + + list_val->Append(index_val); + } + + auto value_val = comm::data_to_val(move(item.second), + tt->YieldType()); + + if ( ! value_val ) + { + Unref(rval); + Unref(list_val); + return nullptr; + } + + rval->Assign(list_val, value_val); + Unref(list_val); + } + + return rval; + } + + result_type operator()(broker::vector& a) + { + if ( type->Tag() != TYPE_VECTOR ) + return nullptr; + + auto vt = type->AsVectorType(); + auto rval = new VectorVal(vt); + + for ( auto& item : a ) + { + auto item_val = comm::data_to_val(move(item), vt->YieldType()); + + if ( ! item_val ) + { + Unref(rval); + return nullptr; + } + + rval->Assign(rval->Size(), item_val); + } + + return rval; + } + + result_type operator()(broker::record& a) + { + if ( type->Tag() != TYPE_RECORD ) + return nullptr; + + auto rt = type->AsRecordType(); + + if ( a.fields.size() != rt->NumFields() ) + return nullptr; + + auto rval = new RecordVal(rt); + + for ( auto i = 0u; i < a.fields.size(); ++i ) + { + if ( ! a.fields[i] ) + { + rval->Assign(i, nullptr); + continue; + } + + auto item_val = comm::data_to_val(move(*a.fields[i]), + rt->FieldType(i)); + + if ( ! item_val ) + { + Unref(rval); + return nullptr; + } + + rval->Assign(i, item_val); + } + + return nullptr; + } +}; + +Val* comm::data_to_val(broker::data d, BroType* type) + { + return broker::visit(val_converter{type}, d); + } + +broker::util::optional comm::val_to_data(const Val* v) + { + switch ( v->Type()->Tag() ) { + case TYPE_BOOL: + return {v->AsBool()}; + case TYPE_INT: + return {v->AsInt()}; + case TYPE_COUNT: + return {v->AsCount()}; + case TYPE_COUNTER: + return {v->AsCounter()}; + case TYPE_PORT: + { + auto p = v->AsPortVal(); + return {broker::port(p->Port(), to_broker_port_proto(p->PortType()))}; + } + case TYPE_ADDR: + { + auto a = v->AsAddr(); + in6_addr tmp; + a.CopyIPv6(&tmp); + return {broker::address(reinterpret_cast(&tmp), + broker::address::family::ipv6, + broker::address::byte_order::network)}; + } + break; + case TYPE_SUBNET: + { + auto s = v->AsSubNet(); + in6_addr tmp; + s.Prefix().CopyIPv6(&tmp); + auto a = broker::address(reinterpret_cast(&tmp), + broker::address::family::ipv6, + broker::address::byte_order::network); + return {broker::subnet(a, s.Length())}; + } + break; + case TYPE_DOUBLE: + return {v->AsDouble()}; + case TYPE_TIME: + return {broker::time_point(v->AsTime())}; + case TYPE_INTERVAL: + return {broker::time_duration(v->AsInterval())}; + case TYPE_ENUM: + { + auto enum_type = v->Type()->AsEnumType(); + auto enum_name = enum_type->Lookup(v->AsEnum()); + return {broker::enum_value(enum_name ? "" : enum_name)}; + } + case TYPE_STRING: + { + auto s = v->AsString(); + return {string(reinterpret_cast(s->Bytes()), s->Len())}; + } + case TYPE_FILE: + return {string(v->AsFile()->Name())}; + case TYPE_FUNC: + return {string(v->AsFunc()->Name())}; + case TYPE_TABLE: + { + auto is_set = v->Type()->IsSet(); + auto table = v->AsTable(); + auto table_val = v->AsTableVal(); + auto c = table->InitForIteration(); + broker::data rval; + + if ( is_set ) + rval = broker::set(); + else + rval = broker::table(); + + struct iter_guard { + iter_guard(HashKey* arg_k, ListVal* arg_lv) + : k(arg_k), lv(arg_lv) + {} + + ~iter_guard() + { + delete k; + Unref(lv); + } + + HashKey* k; + ListVal* lv; + }; + + for ( auto i = 0; i < table->Length(); ++i ) + { + HashKey* k; + auto entry = table->NextEntry(k, c); + auto vl = table_val->RecoverIndex(k); + iter_guard ig(k, vl); + broker::vector key; + + for ( auto k = 0; k < vl->Length(); ++k ) + { + auto key_part = val_to_data((*vl->Vals())[k]); + + if ( ! key_part ) + return {}; + + key.emplace_back(move(*key_part)); + } + + if ( is_set ) + broker::get(rval)->emplace(move(key)); + else + { + auto val = val_to_data(entry->Value()); + + if ( ! val ) + return {}; + + broker::get(rval)->emplace(move(key), + move(*val)); + } + } + + return {rval}; + } + case TYPE_VECTOR: + { + auto vec = v->AsVectorVal(); + broker::vector rval; + rval.reserve(vec->Size()); + + for ( auto i = 0u; i < vec->Size(); ++i ) + { + auto item_val = vec->Lookup(i); + + if ( ! item_val ) + continue; + + auto item = val_to_data(item_val); + + if ( ! item ) + return {}; + + rval.emplace_back(move(*item)); + } + + return {rval}; + } + case TYPE_RECORD: + { + auto rec = v->AsRecordVal(); + broker::record rval; + auto num_fields = v->Type()->AsRecordType()->NumFields(); + rval.fields.reserve(num_fields); + + for ( auto i = 0u; i < num_fields; ++i ) + { + auto item_val = rec->LookupWithDefault(i); + + if ( ! item_val ) + { + rval.fields.emplace_back(broker::record::field{}); + continue; + } + + auto item = val_to_data(item_val); + Unref(item_val); + + if ( ! item ) + return {}; + + rval.fields.emplace_back(broker::record::field{move(*item)}); + } + + return {rval}; + } + default: + reporter->Error("unsupported Comm::Data type: %s", + type_name(v->Type()->Tag())); + break; + } + + return {}; + } + +RecordVal* comm::make_data_val(const Val* v) + { + auto rval = new RecordVal(BifType::Record::Comm::Data); + auto data = val_to_data(v); + + if ( data ) + rval->Assign(0, new DataVal(move(*data))); + + return rval; + } diff --git a/src/comm/Data.h b/src/comm/Data.h new file mode 100644 index 0000000000..e3197b61da --- /dev/null +++ b/src/comm/Data.h @@ -0,0 +1,29 @@ +#ifndef BRO_COMM_DATA_H +#define BRO_COMM_DATA_H + +#include +#include "Val.h" + +namespace comm { + +extern OpaqueType* opaque_of_data_type; + +RecordVal* make_data_val(const Val* v); + +broker::util::optional val_to_data(const Val* v); + +Val* data_to_val(broker::data d, BroType* type); + +class DataVal : public OpaqueVal { +public: + + DataVal(broker::data arg_data) + : OpaqueVal(comm::opaque_of_data_type), data(std::move(arg_data)) + {} + + broker::data data; +}; + +} // namespace comm + +#endif // BRO_COMM_DATA_H diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index 7027daa79e..b4f118706a 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -1,4 +1,5 @@ #include "Manager.h" +#include "Data.h" #include #include #include @@ -32,6 +33,9 @@ bool comm::Manager::InitPostScript() send_flags_peers_idx = require_field(send_flags_type, "peers"); send_flags_unsolicited_idx = require_field(send_flags_type, "unsolicited"); + comm::opaque_of_data_type = new OpaqueType("Comm::Data"); + vector_of_data_type = new VectorType(internal_type("Comm::Data")->Ref()); + auto res = broker::init(); if ( res ) @@ -103,6 +107,96 @@ bool comm::Manager::Print(string topic, string msg, const Val* flags) return true; } +bool comm::Manager::Event(std::string topic, const RecordVal* args, + const Val* flags) + { + if ( ! args->Lookup(0) ) + return false; + + auto event_name = args->Lookup(0)->AsString()->CheckString(); + auto vv = args->Lookup(1)->AsVectorVal(); + broker::message msg; + msg.reserve(vv->Size() + 1); + msg.emplace_back(event_name); + + for ( auto i = 0u; i < vv->Size(); ++i ) + { + auto val = vv->Lookup(i)->AsRecordVal()->Lookup(0); + auto data_val = dynamic_cast(val); + msg.emplace_back(data_val->data); + } + + endpoint->send(move(topic), move(msg), get_flags(flags)); + return true; + } + +RecordVal* comm::Manager::MakeEventArgs(const val_list* args) + { + auto rval = new RecordVal(BifType::Record::Comm::EventArgs); + auto arg_vec = new VectorVal(vector_of_data_type); + rval->Assign(1, arg_vec); + const Func* func; + + for ( auto i = 0u; i < args->length(); ++i ) + { + auto arg_val = (*args)[i]; + + if ( i == 0 ) + { + // Event val must come first. + + if ( arg_val->Type()->Tag() != TYPE_FUNC ) + { + reporter->Error("1st param of Comm::event_args must be event"); + return rval; + } + + func = arg_val->AsFunc(); + + if ( func->Flavor() != FUNC_FLAVOR_EVENT ) + { + reporter->Error("1st param of Comm::event_args must be event"); + return rval; + } + + auto num_args = func->FType()->Args()->NumFields(); + + if ( num_args != args->length() - 1 ) + { + reporter->Error("bad # of Comm::event_args: got %d, expect %d", + args->length(), num_args + 1); + return rval; + } + + rval->Assign(0, new StringVal(func->Name())); + continue; + } + + auto expected_type = (*func->FType()->ArgTypes()->Types())[i - 1]; + + if ( ! same_type((*args)[i]->Type(), expected_type) ) + { + rval->Assign(0, 0); + reporter->Error("Comm::event_args param %d type mismatch", i); + return rval; + } + + auto data_val = make_data_val((*args)[i]); + + if ( ! data_val->Lookup(0) ) + { + Unref(data_val); + rval->Assign(0, 0); + reporter->Error("Comm::event_args unsupported event/params"); + return rval; + } + + arg_vec->Assign(i - 1, data_val); + } + + return rval; + } + bool comm::Manager::SubscribeToPrints(string topic_prefix) { auto& q = print_subscriptions[topic_prefix]; @@ -119,6 +213,22 @@ bool comm::Manager::UnsubscribeToPrints(const string& topic_prefix) return print_subscriptions.erase(topic_prefix); } +bool comm::Manager::SubscribeToEvents(string topic_prefix) + { + auto& q = event_subscriptions[topic_prefix]; + + if ( q ) + return false; + + q = broker::message_queue(move(topic_prefix), *endpoint); + return true; + } + +bool comm::Manager::UnsubscribeToEvents(const string& topic_prefix) + { + return event_subscriptions.erase(topic_prefix); + } + int comm::Manager::get_flags(const Val* flags) { auto r = flags->AsRecordVal(); @@ -149,6 +259,9 @@ void comm::Manager::GetFds(iosource::FD_Set* read, iosource::FD_Set* write, for ( const auto& ps : print_subscriptions ) read->Insert(ps.second.fd()); + + for ( const auto& ps : event_subscriptions ) + read->Insert(ps.second.fd()); } double comm::Manager::NextTimestamp(double* local_network_time) @@ -251,5 +364,69 @@ void comm::Manager::Process() } } + for ( const auto& es : event_subscriptions ) + { + auto event_messages = es.second.want_pop(); + + if ( event_messages.empty() ) + continue; + + idle = false; + + for ( auto& em : event_messages ) + { + if ( em.empty() ) + { + reporter->Warning("got empty event message"); + continue; + } + + std::string* event_name = broker::get(em[0]); + + if ( ! event_name ) + { + reporter->Warning("got event message w/o event name: %d", + static_cast(broker::which(em[0]))); + continue; + } + + EventHandlerPtr ehp = event_registry->Lookup(event_name->data()); + + if ( ! ehp ) + continue; + + auto arg_types = ehp->FType()->ArgTypes()->Types(); + + if ( arg_types->length() != em.size() - 1 ) + { + reporter->Warning("got event message with invalid # of args," + " got %zd, expected %d", em.size() - 1, + arg_types->length()); + continue; + } + + val_list* vl = new val_list; + + for ( auto i = 1u; i < em.size(); ++i ) + { + auto val = data_to_val(move(em[i]), (*arg_types)[i - 1]); + + if ( val ) + vl->append(val); + else + { + reporter->Warning("failed to convert remote event arg # %d", + i - 1); + break; + } + } + + if ( vl->length() == em.size() - 1 ) + mgr.QueueEvent(ehp, vl); + else + delete_vals(vl); + } + } + SetIdle(idle); } diff --git a/src/comm/Manager.h b/src/comm/Manager.h index 0f7d5a4a1c..020f78a03b 100644 --- a/src/comm/Manager.h +++ b/src/comm/Manager.h @@ -31,10 +31,18 @@ public: bool Print(std::string topic, std::string msg, const Val* flags); + bool Event(std::string topic, const RecordVal* args, const Val* flags); + + RecordVal* MakeEventArgs(const val_list* args); + bool SubscribeToPrints(std::string topic_prefix); bool UnsubscribeToPrints(const std::string& topic_prefix); + bool SubscribeToEvents(std::string topic_prefix); + + bool UnsubscribeToEvents(const std::string& topic_prefix); + private: int get_flags(const Val* flags); @@ -53,10 +61,13 @@ private: std::unique_ptr endpoint; std::map, broker::peering> peers; std::map print_subscriptions; + std::map event_subscriptions; int send_flags_self_idx; int send_flags_peers_idx; int send_flags_unsolicited_idx; + + VectorType* vector_of_data_type; }; } // namespace comm diff --git a/src/comm/comm.bif b/src/comm/comm.bif index 6294864bba..fe405222cc 100644 --- a/src/comm/comm.bif +++ b/src/comm/comm.bif @@ -1,17 +1,29 @@ %%{ #include "comm/Manager.h" +#include "comm/Data.h" %%} module Comm; type Comm::SendFlags: record; +type Comm::Data: record; + +type Comm::EventArgs: record; + +function Comm::data%(d: any%): Comm::Data + %{ + return comm::make_data_val(d); + %} + event Comm::remote_connection_established%(peer_address: string, peer_port: port, peer_name: string%); + event Comm::remote_connection_broken%(peer_address: string, peer_port: port%); + event Comm::remote_connection_incompatible%(peer_address: string, peer_port: port%); @@ -62,14 +74,40 @@ function Comm::print%(topic: string, msg: string, return new Val(rval, TYPE_BOOL); %} -function Comm::subscribe_to_prints%(topic_prefix: string &default = ""%): bool +function Comm::subscribe_to_prints%(topic_prefix: string%): bool %{ auto rval = comm_mgr->SubscribeToPrints(topic_prefix->CheckString()); return new Val(rval, TYPE_BOOL); %} -function Comm::unsubscribe_to_prints%(topic_prefix: string &default = ""%): bool +function Comm::unsubscribe_to_prints%(topic_prefix: string%): bool %{ auto rval = comm_mgr->UnsubscribeToPrints(topic_prefix->CheckString()); return new Val(rval, TYPE_BOOL); %} + +function Comm::event_args%(...%): Comm::EventArgs + %{ + auto rval = comm_mgr->MakeEventArgs(@ARGS@); + return rval; + %} + +function Comm::event%(topic: string, args: Comm::EventArgs, + flags: SendFlags &default = SendFlags()%): bool + %{ + auto rval = comm_mgr->Event(topic->CheckString(), args->AsRecordVal(), + flags); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::subscribe_to_events%(topic_prefix: string%): bool + %{ + auto rval = comm_mgr->SubscribeToEvents(topic_prefix->CheckString()); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::unsubscribe_to_events%(topic_prefix: string%): bool + %{ + auto rval = comm_mgr->UnsubscribeToEvents(topic_prefix->CheckString()); + return new Val(rval, TYPE_BOOL); + %} From f51dc5cbb87bc4fa65d124b4e4f3c49001435a2a Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Sat, 17 Jan 2015 08:07:18 -0800 Subject: [PATCH 084/299] Fixing (harmless) Coverity warning. --- src/file_analysis/FileReassembler.cc | 5 +++++ src/file_analysis/FileReassembler.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/file_analysis/FileReassembler.cc b/src/file_analysis/FileReassembler.cc index d2b4eda23d..8b678e5209 100644 --- a/src/file_analysis/FileReassembler.cc +++ b/src/file_analysis/FileReassembler.cc @@ -12,6 +12,11 @@ FileReassembler::FileReassembler(File *f, uint64 starting_offset) { } +FileReassembler::FileReassembler() + : Reassembler(), the_file(0), flushing(false) + { + } + FileReassembler::~FileReassembler() { } diff --git a/src/file_analysis/FileReassembler.h b/src/file_analysis/FileReassembler.h index aa68e865ad..396aa062e1 100644 --- a/src/file_analysis/FileReassembler.h +++ b/src/file_analysis/FileReassembler.h @@ -48,7 +48,7 @@ public: { return flushing; } protected: - FileReassembler() { } + FileReassembler(); DECLARE_SERIAL(FileReassembler); From 87962a48dd452f766062a70e1f8d97dd4f6d3626 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 20 Jan 2015 16:18:49 -0600 Subject: [PATCH 085/299] Add a new attribute: &deprecated. While scripts are parsed, a warning is raised for each usage of an identifier marked as &deprecated. This also works for BIFs. Addresses BIT-924, BIT-757. --- src/Attr.cc | 3 +- src/Attr.h | 3 +- src/Expr.cc | 35 +++++++--- src/ID.h | 3 + src/Type.cc | 18 +++-- src/Type.h | 7 +- src/builtin-func.y | 19 ++++-- src/parse.y | 37 ++++++++-- src/plugin/ComponentManager.h | 3 +- src/scan.l | 1 + .../btest/Baseline/language.deprecated/out | 26 +++++++ testing/btest/language/deprecated.bro | 68 +++++++++++++++++++ 12 files changed, 192 insertions(+), 31 deletions(-) create mode 100644 testing/btest/Baseline/language.deprecated/out create mode 100644 testing/btest/language/deprecated.bro diff --git a/src/Attr.cc b/src/Attr.cc index 13106b02b7..fc8d3000d1 100644 --- a/src/Attr.cc +++ b/src/Attr.cc @@ -18,7 +18,7 @@ const char* attr_name(attr_tag t) "&encrypt", "&raw_output", "&mergeable", "&priority", "&group", "&log", "&error_handler", "&type_column", - "(&tracked)", + "(&tracked)", "&deprecated", }; return attr_names[int(t)]; @@ -212,6 +212,7 @@ void Attributes::DescribeReST(ODesc* d) const void Attributes::CheckAttr(Attr* a) { switch ( a->Tag() ) { + case ATTR_DEPRECATED: case ATTR_OPTIONAL: case ATTR_REDEF: break; diff --git a/src/Attr.h b/src/Attr.h index 228bc2e5fc..63f2524c21 100644 --- a/src/Attr.h +++ b/src/Attr.h @@ -34,7 +34,8 @@ typedef enum { ATTR_ERROR_HANDLER, ATTR_TYPE_COLUMN, // for input framework ATTR_TRACKED, // hidden attribute, tracked by NotifierRegistry -#define NUM_ATTRS (int(ATTR_TRACKED) + 1) + ATTR_DEPRECATED, +#define NUM_ATTRS (int(ATTR_DEPRECATED) + 1) } attr_tag; class Attr : public BroObj { diff --git a/src/Expr.cc b/src/Expr.cc index 671f9b2d41..d2dcb1585b 100644 --- a/src/Expr.cc +++ b/src/Expr.cc @@ -3213,6 +3213,10 @@ FieldExpr::FieldExpr(Expr* arg_op, const char* arg_field_name) { SetType(rt->FieldType(field)->Ref()); td = rt->FieldDecl(field); + + if ( td->FindAttr(ATTR_DEPRECATED) ) + reporter->Warning("deprecated (%s$%s)", rt->GetName().c_str(), + field_name); } } } @@ -3333,6 +3337,9 @@ HasFieldExpr::HasFieldExpr(Expr* arg_op, const char* arg_field_name) if ( field < 0 ) ExprError("no such field in record"); + else if ( rt->FieldDecl(field)->FindAttr(ATTR_DEPRECATED) ) + reporter->Warning("deprecated (%s?$%s)", rt->GetName().c_str(), + field_name); SetType(base_type(TYPE_BOOL)); } @@ -4147,16 +4154,28 @@ RecordCoerceExpr::RecordCoerceExpr(Expr* op, RecordType* r) } for ( i = 0; i < map_size; ++i ) - if ( map[i] == -1 && - ! t_r->FieldDecl(i)->FindAttr(ATTR_OPTIONAL) ) + { + if ( map[i] == -1 ) { - char buf[512]; - safe_snprintf(buf, sizeof(buf), - "non-optional field \"%s\" missing", t_r->FieldName(i)); - Error(buf); - SetError(); - break; + if ( ! t_r->FieldDecl(i)->FindAttr(ATTR_OPTIONAL) ) + { + char buf[512]; + safe_snprintf(buf, sizeof(buf), + "non-optional field \"%s\" missing", + t_r->FieldName(i)); + Error(buf); + SetError(); + break; + } } + else + { + if ( t_r->FieldDecl(i)->FindAttr(ATTR_DEPRECATED) ) + reporter->Warning("deprecated (%s$%s)", + t_r->GetName().c_str(), + t_r->FieldName(i)); + } + } } } diff --git a/src/ID.h b/src/ID.h index 31cfad4191..ca5d222373 100644 --- a/src/ID.h +++ b/src/ID.h @@ -80,6 +80,9 @@ public: Attr* FindAttr(attr_tag t) const { return attrs ? attrs->FindAttr(t) : 0; } + bool IsDeprecated() const + { return FindAttr(ATTR_DEPRECATED) != 0; } + void Error(const char* msg, const BroObj* o2 = 0); void Describe(ODesc* d) const; diff --git a/src/Type.cc b/src/Type.cc index ead31f1b7d..b5466c27ba 100644 --- a/src/Type.cc +++ b/src/Type.cc @@ -1434,7 +1434,7 @@ EnumType::~EnumType() // Note, we use reporter->Error() here (not Error()) to include the current script // location in the error message, rather than the one where the type was // originally defined. -void EnumType::AddName(const string& module_name, const char* name, bool is_export) +void EnumType::AddName(const string& module_name, const char* name, bool is_export, bool deprecated) { /* implicit, auto-increment */ if ( counter < 0) @@ -1443,11 +1443,11 @@ void EnumType::AddName(const string& module_name, const char* name, bool is_expo SetError(); return; } - CheckAndAddName(module_name, name, counter, is_export); + CheckAndAddName(module_name, name, counter, is_export, deprecated); counter++; } -void EnumType::AddName(const string& module_name, const char* name, bro_int_t val, bool is_export) +void EnumType::AddName(const string& module_name, const char* name, bro_int_t val, bool is_export, bool deprecated) { /* explicit value specified */ if ( counter > 0 ) @@ -1457,11 +1457,11 @@ void EnumType::AddName(const string& module_name, const char* name, bro_int_t va return; } counter = -1; - CheckAndAddName(module_name, name, val, is_export); + CheckAndAddName(module_name, name, val, is_export, deprecated); } void EnumType::CheckAndAddName(const string& module_name, const char* name, - bro_int_t val, bool is_export) + bro_int_t val, bool is_export, bool deprecated) { if ( Lookup(val) ) { @@ -1477,6 +1477,14 @@ void EnumType::CheckAndAddName(const string& module_name, const char* name, id = install_ID(name, module_name.c_str(), true, is_export); id->SetType(this->Ref()); id->SetEnumConst(); + + if ( deprecated ) + { + attr_list* attr = new attr_list; + attr->append(new Attr(ATTR_DEPRECATED)); + id->AddAttrs(new Attributes(attr, id->Type(), false)); + } + broxygen_mgr->Identifier(id); } else diff --git a/src/Type.h b/src/Type.h index a9f1e42a6d..f902b0d907 100644 --- a/src/Type.h +++ b/src/Type.h @@ -554,12 +554,12 @@ public: // The value of this name is next internal counter value, starting // with zero. The internal counter is incremented. - void AddName(const string& module_name, const char* name, bool is_export); + void AddName(const string& module_name, const char* name, bool is_export, bool deprecated); // The value of this name is set to val. Once a value has been // explicitly assigned using this method, no further names can be // added that aren't likewise explicitly initalized. - void AddName(const string& module_name, const char* name, bro_int_t val, bool is_export); + void AddName(const string& module_name, const char* name, bro_int_t val, bool is_export, bool deprecated); // -1 indicates not found. bro_int_t Lookup(const string& module_name, const char* name) const; @@ -580,7 +580,8 @@ protected: const char* name, bro_int_t val, bool is_export); void CheckAndAddName(const string& module_name, - const char* name, bro_int_t val, bool is_export); + const char* name, bro_int_t val, bool is_export, + bool deprecated); typedef std::map< const char*, bro_int_t, ltstr > NameMap; NameMap names; diff --git a/src/builtin-func.y b/src/builtin-func.y index 1b22436fff..0f895ced52 100644 --- a/src/builtin-func.y +++ b/src/builtin-func.y @@ -287,7 +287,7 @@ void record_bif_item(const char* id, const char* type) %left ',' ':' -%type TOK_C_TOKEN TOK_ID TOK_CSTR TOK_WS TOK_COMMENT TOK_ATTR TOK_INT opt_ws type attr_list opt_attr_list +%type TOK_C_TOKEN TOK_ID TOK_CSTR TOK_WS TOK_COMMENT TOK_ATTR TOK_INT opt_ws type attr_list opt_attr_list opt_func_attrs %type TOK_ATOM TOK_BOOL %union { @@ -372,7 +372,13 @@ type_def_types: TOK_RECORD { set_definition_type(TYPE_DEF, "Table"); } ; -event_def: event_prefix opt_ws plain_head opt_attr_list +opt_func_attrs: attr_list opt_ws + { $$ = $1; } + | /* nothing */ + { $$ = ""; } + ; + +event_def: event_prefix opt_ws plain_head opt_func_attrs { fprintf(fp_bro_init, "%s", $4); } end_of_head ';' { print_event_c_prototype(fp_func_h, true); @@ -380,13 +386,16 @@ event_def: event_prefix opt_ws plain_head opt_attr_list print_event_c_body(fp_func_def); } -func_def: func_prefix opt_ws typed_head end_of_head body +func_def: func_prefix opt_ws typed_head opt_func_attrs + { fprintf(fp_bro_init, "%s", $4); } end_of_head body ; -enum_def: enum_def_1 enum_list TOK_RPB +enum_def: enum_def_1 enum_list TOK_RPB opt_attr_list { // First, put an end to the enum type decl. - fprintf(fp_bro_init, "};\n"); + fprintf(fp_bro_init, "} "); + fprintf(fp_bro_init, "%s", $4); + fprintf(fp_bro_init, ";\n"); if ( decl.module_name != GLOBAL_MODULE_NAME ) fprintf(fp_netvar_h, "}; } }\n"); else diff --git a/src/parse.y b/src/parse.y index 83760dbbf0..9261775932 100644 --- a/src/parse.y +++ b/src/parse.y @@ -2,7 +2,7 @@ // See the file "COPYING" in the main distribution directory for copyright. %} -%expect 75 +%expect 78 %token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY %token TOK_ATENDIF TOK_ATELSE TOK_ATIF TOK_ATIFDEF TOK_ATIFNDEF @@ -24,7 +24,7 @@ %token TOK_ATTR_PERSISTENT TOK_ATTR_SYNCHRONIZED %token TOK_ATTR_RAW_OUTPUT TOK_ATTR_MERGEABLE %token TOK_ATTR_PRIORITY TOK_ATTR_LOG TOK_ATTR_ERROR_HANDLER -%token TOK_ATTR_TYPE_COLUMN +%token TOK_ATTR_TYPE_COLUMN TOK_ATTR_DEPRECATED %token TOK_DEBUG @@ -44,7 +44,7 @@ %right '!' %left '$' '[' ']' '(' ')' TOK_HAS_FIELD TOK_HAS_ATTR -%type opt_no_test opt_no_test_block +%type opt_no_test opt_no_test_block opt_deprecated %type TOK_ID TOK_PATTERN_TEXT single_pattern %type local_id global_id def_global_id event_id global_or_event_id resolve_id begin_func %type local_id_list @@ -671,6 +671,9 @@ expr: } else $$ = new NameExpr(id); + + if ( id->IsDeprecated() ) + reporter->Warning("deprecated (%s)", id->Name()); } } @@ -759,7 +762,7 @@ enum_body_elem: error messages if someboy tries to use constant variables as enumerator. */ - TOK_ID '=' TOK_CONSTANT + TOK_ID '=' TOK_CONSTANT opt_deprecated { set_location(@1, @3); assert(cur_enum_type); @@ -768,7 +771,7 @@ enum_body_elem: reporter->Error("enumerator is not a count constant"); else cur_enum_type->AddName(current_module, $1, - $3->InternalUnsigned(), is_export); + $3->InternalUnsigned(), is_export, $4); } | TOK_ID '=' '-' TOK_CONSTANT @@ -780,11 +783,11 @@ enum_body_elem: reporter->Error("enumerator is not a count constant"); } - | TOK_ID + | TOK_ID opt_deprecated { set_location(@1); assert(cur_enum_type); - cur_enum_type->AddName(current_module, $1, is_export); + cur_enum_type->AddName(current_module, $1, is_export, $2); } ; @@ -963,7 +966,12 @@ type: $$ = error_type(); } else + { Ref($$); + + if ( $1->IsDeprecated() ) + reporter->Warning("deprecated (%s)", $1->Name()); + } } ; @@ -1265,6 +1273,8 @@ attr: { $$ = new Attr(ATTR_LOG); } | TOK_ATTR_ERROR_HANDLER { $$ = new Attr(ATTR_ERROR_HANDLER); } + | TOK_ATTR_DEPRECATED + { $$ = new Attr(ATTR_DEPRECATED); } ; stmt: @@ -1450,6 +1460,10 @@ event: { set_location(@1, @4); $$ = new EventExpr($1, $3); + ID* id = lookup_ID($1, current_module.c_str()); + + if ( id && id->IsDeprecated() ) + reporter->Warning("deprecated (%s)", id->Name()); } ; @@ -1556,6 +1570,9 @@ global_or_event_id: if ( ! $$->IsGlobal() ) $$->Error("already a local identifier"); + if ( $$->IsDeprecated() ) + reporter->Warning("deprecated (%s)", $$->Name()); + delete [] $1; } @@ -1597,6 +1614,12 @@ opt_no_test_block: | { $$ = false; } +opt_deprecated: + TOK_ATTR_DEPRECATED + { $$ = true; } + | + { $$ = false; } + %% int yyerror(const char msg[]) diff --git a/src/plugin/ComponentManager.h b/src/plugin/ComponentManager.h index 7337cf069a..0069c77359 100644 --- a/src/plugin/ComponentManager.h +++ b/src/plugin/ComponentManager.h @@ -243,7 +243,8 @@ void ComponentManager::RegisterComponent(C* component, // Install an identfier for enum value string id = fmt("%s%s", prefix.c_str(), cname.c_str()); tag_enum_type->AddName(module, id.c_str(), - component->Tag().AsEnumVal()->InternalInt(), true); + component->Tag().AsEnumVal()->InternalInt(), true, + false); } } // namespace plugin diff --git a/src/scan.l b/src/scan.l index 0820567c30..ae11382fb3 100644 --- a/src/scan.l +++ b/src/scan.l @@ -260,6 +260,7 @@ when return TOK_WHEN; &create_expire return TOK_ATTR_EXPIRE_CREATE; &default return TOK_ATTR_DEFAULT; &delete_func return TOK_ATTR_DEL_FUNC; +&deprecated return TOK_ATTR_DEPRECATED; &raw_output return TOK_ATTR_RAW_OUTPUT; &encrypt return TOK_ATTR_ENCRYPT; &error_handler return TOK_ATTR_ERROR_HANDLER; diff --git a/testing/btest/Baseline/language.deprecated/out b/testing/btest/Baseline/language.deprecated/out new file mode 100644 index 0000000000..9587bf033f --- /dev/null +++ b/testing/btest/Baseline/language.deprecated/out @@ -0,0 +1,26 @@ +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 30: deprecated (ONE) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 31: deprecated (TWO) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 33: deprecated (GREEN) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 34: deprecated (BLUE) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 36: deprecated (blah) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 40: deprecated (my_event) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 41: deprecated (my_event) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 42: deprecated (my_hook) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 44: deprecated (my_record$b) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 45: deprecated (my_record$b) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 46: deprecated (my_record$b) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 48: deprecated (my_record?$b) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 49: deprecated (my_record$b) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 52: deprecated (my_record$b) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 55: deprecated (my_event) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 60: deprecated (my_hook) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 65: deprecated (blah) +ZERO +ONE +TWO +RED +GREEN +BLUE +generate my_hook please +generate my_event please +schedule my_event please diff --git a/testing/btest/language/deprecated.bro b/testing/btest/language/deprecated.bro new file mode 100644 index 0000000000..0a6d269fad --- /dev/null +++ b/testing/btest/language/deprecated.bro @@ -0,0 +1,68 @@ +# @TEST-EXEC: bro -b %INPUT >out 2>&1 +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff out + +type blah: string &deprecated; + +global my_event: event(arg: string) &deprecated; + +global my_hook: hook(arg: string) &deprecated; + +type my_record: record { + a: count &default = 1; + b: string &optional &deprecated; +}; + +type my_enum: enum { + RED, + GREEN &deprecated, + BLUE &deprecated +}; + +type my_other_enum: enum { + ZERO = 0, + ONE = 1 &deprecated, + TWO = 2 &deprecated +}; + +event bro_init() + { + print ZERO; + print ONE; + print TWO; + print RED; + print GREEN; + print BLUE; + + local l: blah = "testing"; + + local ls: string = " test"; + + event my_event("generate my_event please"); + schedule 1sec { my_event("schedule my_event please") }; + hook my_hook("generate my_hook please"); + + local mr = my_record($a = 3, $b = "yeah"); + mr = [$a = 4, $b = "ye"]; + mr = record($a = 5, $b = "y"); + + if ( ! mr?$b ) + mr$b = "nooooooo"; + + mr$a = 2; + mr$b = "noooo"; + } + +event my_event(arg: string) + { + print arg; + } + +hook my_hook(arg: string) + { + print arg; + } + +function hmm(b: blah) + { + print b; + } From 011e2cdd323d36bae0ecf3a716af0afe0f9cabdf Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 21 Jan 2015 12:27:09 -0600 Subject: [PATCH 086/299] Improve use of &deprecated on functions. - Don't report warnings on function definition if declaration is marked deprecated. - Allow &deprecated to apply to a standalone function definition. --- src/ID.cc | 10 ++++++++ src/ID.h | 2 ++ src/Type.cc | 6 +---- src/Var.cc | 4 ++++ src/parse.y | 23 ++++++++++++++++++- .../btest/Baseline/language.deprecated/out | 2 ++ testing/btest/language/deprecated.bro | 12 ++++++++++ 7 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/ID.cc b/src/ID.cc index aa965b880e..a308ffa81d 100644 --- a/src/ID.cc +++ b/src/ID.cc @@ -248,6 +248,16 @@ void ID::UpdateValAttrs() } } +void ID::MakeDeprecated() + { + if ( IsDeprecated() ) + return; + + attr_list* attr = new attr_list; + attr->append(new Attr(ATTR_DEPRECATED)); + AddAttrs(new Attributes(attr, Type(), false)); + } + void ID::AddAttrs(Attributes* a) { if ( attrs ) diff --git a/src/ID.h b/src/ID.h index ca5d222373..805a8e391b 100644 --- a/src/ID.h +++ b/src/ID.h @@ -83,6 +83,8 @@ public: bool IsDeprecated() const { return FindAttr(ATTR_DEPRECATED) != 0; } + void MakeDeprecated(); + void Error(const char* msg, const BroObj* o2 = 0); void Describe(ODesc* d) const; diff --git a/src/Type.cc b/src/Type.cc index b5466c27ba..9aa86da8dc 100644 --- a/src/Type.cc +++ b/src/Type.cc @@ -1479,11 +1479,7 @@ void EnumType::CheckAndAddName(const string& module_name, const char* name, id->SetEnumConst(); if ( deprecated ) - { - attr_list* attr = new attr_list; - attr->append(new Attr(ATTR_DEPRECATED)); - id->AddAttrs(new Attributes(attr, id->Type(), false)); - } + id->MakeDeprecated(); broxygen_mgr->Identifier(id); } diff --git a/src/Var.cc b/src/Var.cc index 0a196b9cac..95ec5802ef 100644 --- a/src/Var.cc +++ b/src/Var.cc @@ -435,6 +435,10 @@ void end_func(Stmt* body, attr_list* attrs) loop_over_list(*attrs, i) { Attr* a = (*attrs)[i]; + + if ( a->Tag() == ATTR_DEPRECATED ) + continue; + if ( a->Tag() != ATTR_PRIORITY ) { a->Error("illegal attribute for function body"); diff --git a/src/parse.y b/src/parse.y index 9261775932..f74880dc13 100644 --- a/src/parse.y +++ b/src/parse.y @@ -227,6 +227,18 @@ static bool expr_is_table_type_name(const Expr* expr) return false; } + +static bool has_attr(const attr_list* al, attr_tag tag) + { + if ( ! al ) + return false; + + for ( int i = 0; i < al->length(); ++i ) + if ( (*al)[i]->Tag() == tag ) + return true; + + return false; + } %} %union { @@ -1147,6 +1159,9 @@ func_body: { saved_in_init.push_back(in_init); in_init = 0; + + if ( has_attr($1, ATTR_DEPRECATED) ) + current_scope()->ScopeID()->MakeDeprecated(); } stmt_list @@ -1571,7 +1586,13 @@ global_or_event_id: $$->Error("already a local identifier"); if ( $$->IsDeprecated() ) - reporter->Warning("deprecated (%s)", $$->Name()); + { + BroType* t = $$->Type(); + + if ( t->Tag() != TYPE_FUNC || + t->AsFuncType()->Flavor() != FUNC_FLAVOR_FUNCTION ) + reporter->Warning("deprecated (%s)", $$->Name()); + } delete [] $1; } diff --git a/testing/btest/Baseline/language.deprecated/out b/testing/btest/Baseline/language.deprecated/out index 9587bf033f..5bdf87a62b 100644 --- a/testing/btest/Baseline/language.deprecated/out +++ b/testing/btest/Baseline/language.deprecated/out @@ -15,6 +15,8 @@ warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 55: deprecated (my_event) warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 60: deprecated (my_hook) warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 65: deprecated (blah) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 74: deprecated (dont_use_me) +warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.bro, line 79: deprecated (dont_use_me_either) ZERO ONE TWO diff --git a/testing/btest/language/deprecated.bro b/testing/btest/language/deprecated.bro index 0a6d269fad..ec9c3c9e1e 100644 --- a/testing/btest/language/deprecated.bro +++ b/testing/btest/language/deprecated.bro @@ -66,3 +66,15 @@ function hmm(b: blah) { print b; } + +global dont_use_me: function() &deprecated; + +function dont_use_me() + { + dont_use_me(); + } + +function dont_use_me_either() &deprecated + { + dont_use_me_either(); + } From 23f04835c6173c1fee0c202de2c4a9e83f691203 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 21 Jan 2015 15:34:42 -0600 Subject: [PATCH 087/299] Deprecate split* family of BIFs. These functions are now deprecated in favor of alternative versions that return a vector of strings rather than a table of strings. Deprecated functions: - split: use split_string instead. - split1: use split_string1 instead. - split_all: use split_string_all instead. - split_n: use split_string_n instead. - cat_string_array: see join_string_vec instead. - cat_string_array_n: see join_string_vec instead. - join_string_array: see join_string_vec instead. - sort_string_array: use sort instead instead. - find_ip_addresses: use extract_ip_addresses instead. Changed functions: - has_valid_octets: uses a string_vec parameter instead of string_array. Addresses BIT-924, BIT-757. --- scripts/base/files/unified2/main.bro | 22 +- scripts/base/frameworks/logging/main.bro | 24 +- scripts/base/frameworks/software/main.bro | 122 ++++---- scripts/base/protocols/dhcp/utils.bro | 4 +- scripts/base/protocols/ftp/main.bro | 2 +- scripts/base/protocols/http/main.bro | 8 +- scripts/base/protocols/http/utils.bro | 8 +- scripts/base/protocols/smtp/main.bro | 12 +- scripts/base/utils/active-http.bro | 10 +- scripts/base/utils/addrs.bro | 36 ++- scripts/base/utils/exec.bro | 12 +- scripts/base/utils/files.bro | 2 +- scripts/base/utils/numbers.bro | 8 +- scripts/base/utils/paths.bro | 24 +- scripts/base/utils/patterns.bro | 4 +- scripts/base/utils/urls.bro | 42 +-- .../policy/frameworks/files/detect-MHR.bro | 6 +- .../frameworks/intel/seen/http-headers.bro | 2 +- scripts/policy/frameworks/intel/seen/smtp.bro | 20 +- .../policy/frameworks/software/vulnerable.bro | 16 +- .../policy/misc/detect-traceroute/main.bro | 8 +- .../http/software-browser-plugins.bro | 8 +- scripts/policy/protocols/smtp/blocklists.bro | 2 +- scripts/policy/protocols/ssl/notary.bro | 12 +- src/strings.bif | 162 ++++++++++- testing/btest/Baseline/bifs.split_string/out | 32 +++ testing/btest/Baseline/plugins.hooks/output | 264 +++++++++--------- .../Baseline/scripts.base.utils.addrs/output | 14 +- testing/btest/bifs/split_string.bro | 36 +++ testing/btest/scripts/base/utils/addrs.test | 11 +- 30 files changed, 574 insertions(+), 359 deletions(-) create mode 100644 testing/btest/Baseline/bifs.split_string/out create mode 100644 testing/btest/bifs/split_string.bro diff --git a/scripts/base/files/unified2/main.bro b/scripts/base/files/unified2/main.bro index 627bcc9fee..73f98aa5f8 100644 --- a/scripts/base/files/unified2/main.bro +++ b/scripts/base/files/unified2/main.bro @@ -152,26 +152,26 @@ redef record fa_file += { event Unified2::read_sid_msg_line(desc: Input::EventDescription, tpe: Input::Event, line: string) { - local parts = split_n(line, / \|\| /, F, 100); - if ( |parts| >= 2 && /^[0-9]+$/ in parts[1] ) - sid_map[to_count(parts[1])] = parts[2]; + local parts = split_string_n(line, / \|\| /, F, 100); + if ( |parts| >= 2 && /^[0-9]+$/ in parts[0] ) + sid_map[to_count(parts[0])] = parts[1]; } event Unified2::read_gen_msg_line(desc: Input::EventDescription, tpe: Input::Event, line: string) { - local parts = split_n(line, / \|\| /, F, 3); - if ( |parts| >= 2 && /^[0-9]+$/ in parts[1] ) - gen_map[to_count(parts[1])] = parts[3]; + local parts = split_string_n(line, / \|\| /, F, 3); + if ( |parts| >= 2 && /^[0-9]+$/ in parts[0] ) + gen_map[to_count(parts[0])] = parts[2]; } event Unified2::read_classification_line(desc: Input::EventDescription, tpe: Input::Event, line: string) { - local parts = split_n(line, /: /, F, 2); + local parts = split_string_n(line, /: /, F, 2); if ( |parts| == 2 ) { - local parts2 = split_n(parts[2], /,/, F, 4); + local parts2 = split_string_n(parts[1], /,/, F, 4); if ( |parts2| > 1 ) - classification_map[|classification_map|+1] = parts2[1]; + classification_map[|classification_map|+1] = parts2[0]; } } @@ -249,9 +249,9 @@ event bro_init() &priority=5 event file_new(f: fa_file) { local file_dir = ""; - local parts = split_all(f$source, /\/[^\/]*$/); + local parts = split_string_all(f$source, /\/[^\/]*$/); if ( |parts| == 3 ) - file_dir = parts[1]; + file_dir = parts[0]; if ( (watch_file != "" && f$source == watch_file) || (watch_dir != "" && compress_path(watch_dir) == file_dir) ) diff --git a/scripts/base/frameworks/logging/main.bro b/scripts/base/frameworks/logging/main.bro index bf1affcb01..d4d5c0244e 100644 --- a/scripts/base/frameworks/logging/main.bro +++ b/scripts/base/frameworks/logging/main.bro @@ -405,30 +405,30 @@ function default_path_func(id: ID, path: string, rec: any) : string local id_str = fmt("%s", id); - local parts = split1(id_str, /::/); + local parts = split_string1(id_str, /::/); if ( |parts| == 2 ) { # Example: Notice::LOG -> "notice" - if ( parts[2] == "LOG" ) + if ( parts[1] == "LOG" ) { - local module_parts = split_n(parts[1], /[^A-Z][A-Z][a-z]*/, T, 4); + local module_parts = split_string_n(parts[0], /[^A-Z][A-Z][a-z]*/, T, 4); local output = ""; - if ( 1 in module_parts ) - output = module_parts[1]; + if ( 0 in module_parts ) + output = module_parts[0]; + if ( 1 in module_parts && module_parts[1] != "" ) + output = cat(output, sub_bytes(module_parts[1],1,1), "_", sub_bytes(module_parts[1], 2, |module_parts[1]|)); if ( 2 in module_parts && module_parts[2] != "" ) - output = cat(output, sub_bytes(module_parts[2],1,1), "_", sub_bytes(module_parts[2], 2, |module_parts[2]|)); + output = cat(output, "_", module_parts[2]); if ( 3 in module_parts && module_parts[3] != "" ) - output = cat(output, "_", module_parts[3]); - if ( 4 in module_parts && module_parts[4] != "" ) - output = cat(output, sub_bytes(module_parts[4],1,1), "_", sub_bytes(module_parts[4], 2, |module_parts[4]|)); + output = cat(output, sub_bytes(module_parts[3],1,1), "_", sub_bytes(module_parts[3], 2, |module_parts[3]|)); return to_lower(output); } # Example: Notice::POLICY_LOG -> "notice_policy" - if ( /_LOG$/ in parts[2] ) - parts[2] = sub(parts[2], /_LOG$/, ""); + if ( /_LOG$/ in parts[1] ) + parts[1] = sub(parts[1], /_LOG$/, ""); - return cat(to_lower(parts[1]),"_",to_lower(parts[2])); + return cat(to_lower(parts[0]),"_",to_lower(parts[1])); } else return to_lower(id_str); diff --git a/scripts/base/frameworks/software/main.bro b/scripts/base/frameworks/software/main.bro index f5c9927126..f7b8ce9326 100644 --- a/scripts/base/frameworks/software/main.bro +++ b/scripts/base/frameworks/software/main.bro @@ -133,62 +133,62 @@ function parse(unparsed_version: string): Description { # The regular expression should match the complete version number # and software name. - local version_parts = split_n(unparsed_version, /\/?( [\(])?v?[0-9\-\._, ]{2,}/, T, 1); - if ( 1 in version_parts ) + local version_parts = split_string_n(unparsed_version, /\/?( [\(])?v?[0-9\-\._, ]{2,}/, T, 1); + if ( 0 in version_parts ) { - if ( /^\(/ in version_parts[1] ) - software_name = strip(sub(version_parts[1], /[\(]/, "")); + if ( /^\(/ in version_parts[0] ) + software_name = strip(sub(version_parts[0], /[\(]/, "")); else - software_name = strip(version_parts[1]); + software_name = strip(version_parts[0]); } if ( |version_parts| >= 2 ) { # Remove the name/version separator if it's left at the beginning # of the version number from the previous split_all. - local sv = strip(version_parts[2]); + local sv = strip(version_parts[1]); if ( /^[\/\-\._v\(]/ in sv ) - sv = strip(sub(version_parts[2], /^\(?[\/\-\._v\(]/, "")); - local version_numbers = split_n(sv, /[\-\._,\[\(\{ ]/, F, 3); - if ( 5 in version_numbers && version_numbers[5] != "" ) - v$addl = strip(version_numbers[5]); - else if ( 3 in version_parts && version_parts[3] != "" && - version_parts[3] != ")" ) + sv = strip(sub(version_parts[1], /^\(?[\/\-\._v\(]/, "")); + local version_numbers = split_string_n(sv, /[\-\._,\[\(\{ ]/, F, 3); + if ( 4 in version_numbers && version_numbers[4] != "" ) + v$addl = strip(version_numbers[4]); + else if ( 2 in version_parts && version_parts[2] != "" && + version_parts[2] != ")" ) { - if ( /^[[:blank:]]*\([a-zA-Z0-9\-\._[:blank:]]*\)/ in version_parts[3] ) + if ( /^[[:blank:]]*\([a-zA-Z0-9\-\._[:blank:]]*\)/ in version_parts[2] ) { - v$addl = split_n(version_parts[3], /[\(\)]/, F, 2)[2]; + v$addl = split_string_n(version_parts[2], /[\(\)]/, F, 2)[1]; } else { - local vp = split_n(version_parts[3], /[\-\._,;\[\]\(\)\{\} ]/, F, 3); - if ( |vp| >= 1 && vp[1] != "" ) + local vp = split_string_n(version_parts[2], /[\-\._,;\[\]\(\)\{\} ]/, F, 3); + if ( |vp| >= 1 && vp[0] != "" ) + { + v$addl = strip(vp[0]); + } + else if ( |vp| >= 2 && vp[1] != "" ) { v$addl = strip(vp[1]); } - else if ( |vp| >= 2 && vp[2] != "" ) + else if ( |vp| >= 3 && vp[2] != "" ) { v$addl = strip(vp[2]); } - else if ( |vp| >= 3 && vp[3] != "" ) - { - v$addl = strip(vp[3]); - } else { - v$addl = strip(version_parts[3]); + v$addl = strip(version_parts[2]); } } } - if ( 4 in version_numbers && version_numbers[4] != "" ) - v$minor3 = extract_count(version_numbers[4]); if ( 3 in version_numbers && version_numbers[3] != "" ) - v$minor2 = extract_count(version_numbers[3]); + v$minor3 = extract_count(version_numbers[3]); if ( 2 in version_numbers && version_numbers[2] != "" ) - v$minor = extract_count(version_numbers[2]); + v$minor2 = extract_count(version_numbers[2]); if ( 1 in version_numbers && version_numbers[1] != "" ) - v$major = extract_count(version_numbers[1]); + v$minor = extract_count(version_numbers[1]); + if ( 0 in version_numbers && version_numbers[0] != "" ) + v$major = extract_count(version_numbers[0]); } } @@ -200,14 +200,14 @@ function parse_mozilla(unparsed_version: string): Description { local software_name = ""; local v: Version; - local parts: table[count] of string; + local parts: string_vec; if ( /Opera [0-9\.]*$/ in unparsed_version ) { software_name = "Opera"; - parts = split_all(unparsed_version, /Opera [0-9\.]*$/); - if ( 2 in parts ) - v = parse(parts[2])$version; + parts = split_string_all(unparsed_version, /Opera [0-9\.]*$/); + if ( 1 in parts ) + v = parse(parts[1])$version; } else if ( / MSIE |Trident\// in unparsed_version ) { @@ -222,28 +222,28 @@ function parse_mozilla(unparsed_version: string): Description v = [$major=11,$minor=0]; else { - parts = split_all(unparsed_version, /MSIE [0-9]{1,2}\.*[0-9]*b?[0-9]*/); - if ( 2 in parts ) - v = parse(parts[2])$version; + parts = split_string_all(unparsed_version, /MSIE [0-9]{1,2}\.*[0-9]*b?[0-9]*/); + if ( 1 in parts ) + v = parse(parts[1])$version; } } else if ( /Version\/.*Safari\// in unparsed_version ) { software_name = "Safari"; - parts = split_all(unparsed_version, /Version\/[0-9\.]*/); - if ( 2 in parts ) + parts = split_string_all(unparsed_version, /Version\/[0-9\.]*/); + if ( 1 in parts ) { - v = parse(parts[2])$version; + v = parse(parts[1])$version; if ( / Mobile\/?.* Safari/ in unparsed_version ) v$addl = "Mobile"; } } else if ( /(Firefox|Netscape|Thunderbird)\/[0-9\.]*/ in unparsed_version ) { - parts = split_all(unparsed_version, /(Firefox|Netscape|Thunderbird)\/[0-9\.]*/); - if ( 2 in parts ) + parts = split_string_all(unparsed_version, /(Firefox|Netscape|Thunderbird)\/[0-9\.]*/); + if ( 1 in parts ) { - local tmp_s = parse(parts[2]); + local tmp_s = parse(parts[1]); software_name = tmp_s$name; v = tmp_s$version; } @@ -251,48 +251,48 @@ function parse_mozilla(unparsed_version: string): Description else if ( /Chrome\/.*Safari\// in unparsed_version ) { software_name = "Chrome"; - parts = split_all(unparsed_version, /Chrome\/[0-9\.]*/); - if ( 2 in parts ) - v = parse(parts[2])$version; + parts = split_string_all(unparsed_version, /Chrome\/[0-9\.]*/); + if ( 1 in parts ) + v = parse(parts[1])$version; } else if ( /^Opera\// in unparsed_version ) { if ( /Opera M(ini|obi)\// in unparsed_version ) { - parts = split_all(unparsed_version, /Opera M(ini|obi)/); - if ( 2 in parts ) - software_name = parts[2]; - parts = split_all(unparsed_version, /Version\/[0-9\.]*/); - if ( 2 in parts ) - v = parse(parts[2])$version; + parts = split_string_all(unparsed_version, /Opera M(ini|obi)/); + if ( 1 in parts ) + software_name = parts[1]; + parts = split_string_all(unparsed_version, /Version\/[0-9\.]*/); + if ( 1 in parts ) + v = parse(parts[1])$version; else { - parts = split_all(unparsed_version, /Opera Mini\/[0-9\.]*/); - if ( 2 in parts ) - v = parse(parts[2])$version; + parts = split_string_all(unparsed_version, /Opera Mini\/[0-9\.]*/); + if ( 1 in parts ) + v = parse(parts[1])$version; } } else { software_name = "Opera"; - parts = split_all(unparsed_version, /Version\/[0-9\.]*/); - if ( 2 in parts ) - v = parse(parts[2])$version; + parts = split_string_all(unparsed_version, /Version\/[0-9\.]*/); + if ( 1 in parts ) + v = parse(parts[1])$version; } } else if ( /AppleWebKit\/[0-9\.]*/ in unparsed_version ) { software_name = "Unspecified WebKit"; - parts = split_all(unparsed_version, /AppleWebKit\/[0-9\.]*/); - if ( 2 in parts ) - v = parse(parts[2])$version; + parts = split_string_all(unparsed_version, /AppleWebKit\/[0-9\.]*/); + if ( 1 in parts ) + v = parse(parts[1])$version; } else if ( / Java\/[0-9]\./ in unparsed_version ) { software_name = "Java"; - parts = split_all(unparsed_version, /Java\/[0-9\._]*/); - if ( 2 in parts ) - v = parse(parts[2])$version; + parts = split_string_all(unparsed_version, /Java\/[0-9\._]*/); + if ( 1 in parts ) + v = parse(parts[1])$version; } return [$version=v, $unparsed_version=unparsed_version, $name=software_name]; diff --git a/scripts/base/protocols/dhcp/utils.bro b/scripts/base/protocols/dhcp/utils.bro index e49bfe6af9..9d5a422128 100644 --- a/scripts/base/protocols/dhcp/utils.bro +++ b/scripts/base/protocols/dhcp/utils.bro @@ -13,7 +13,7 @@ export { function reverse_ip(ip: addr): addr { - local octets = split(cat(ip), /\./); - return to_addr(cat(octets[4], ".", octets[3], ".", octets[2], ".", octets[1])); + local octets = split_string(cat(ip), /\./); + return to_addr(cat(octets[3], ".", octets[2], ".", octets[1], ".", octets[0])); } diff --git a/scripts/base/protocols/ftp/main.bro b/scripts/base/protocols/ftp/main.bro index 9bc1f0d0f1..24195c1d7e 100644 --- a/scripts/base/protocols/ftp/main.bro +++ b/scripts/base/protocols/ftp/main.bro @@ -274,7 +274,7 @@ event file_transferred(c: connection, prefix: string, descr: string, if ( [id$resp_h, id$resp_p] in ftp_data_expected ) { local s = ftp_data_expected[id$resp_h, id$resp_p]; - s$mime_type = split1(mime_type, /;/)[1]; + s$mime_type = split_string1(mime_type, /;/)[0]; } } diff --git a/scripts/base/protocols/http/main.bro b/scripts/base/protocols/http/main.bro index 0457da8ccf..2349635844 100644 --- a/scripts/base/protocols/http/main.bro +++ b/scripts/base/protocols/http/main.bro @@ -242,7 +242,7 @@ event http_header(c: connection, is_orig: bool, name: string, value: string) &pr else if ( name == "HOST" ) # The split is done to remove the occasional port value that shows up here. - c$http$host = split1(value, /:/)[1]; + c$http$host = split_string1(value, /:/)[0]; else if ( name == "RANGE" ) c$http$range_request = T; @@ -262,12 +262,12 @@ event http_header(c: connection, is_orig: bool, name: string, value: string) &pr if ( /^[bB][aA][sS][iI][cC] / in value ) { local userpass = decode_base64(sub(value, /[bB][aA][sS][iI][cC][[:blank:]]/, "")); - local up = split(userpass, /:/); + local up = split_string(userpass, /:/); if ( |up| >= 2 ) { - c$http$username = up[1]; + c$http$username = up[0]; if ( c$http$capture_password ) - c$http$password = up[2]; + c$http$password = up[1]; } else { diff --git a/scripts/base/protocols/http/utils.bro b/scripts/base/protocols/http/utils.bro index 3c75ae254b..88549f8404 100644 --- a/scripts/base/protocols/http/utils.bro +++ b/scripts/base/protocols/http/utils.bro @@ -42,12 +42,12 @@ function extract_keys(data: string, kv_splitter: pattern): string_vec { local key_vec: vector of string = vector(); - local parts = split(data, kv_splitter); + local parts = split_string(data, kv_splitter); for ( part_index in parts ) { - local key_val = split1(parts[part_index], /=/); - if ( 1 in key_val ) - key_vec[|key_vec|] = key_val[1]; + local key_val = split_string1(parts[part_index], /=/); + if ( 0 in key_val ) + key_vec[|key_vec|] = key_val[0]; } return key_vec; } diff --git a/scripts/base/protocols/smtp/main.bro b/scripts/base/protocols/smtp/main.bro index a22d93d2fa..925b0f4da5 100644 --- a/scripts/base/protocols/smtp/main.bro +++ b/scripts/base/protocols/smtp/main.bro @@ -98,7 +98,7 @@ event bro_init() &priority=5 function find_address_in_smtp_header(header: string): string { - local ips = find_ip_addresses(header); + local ips = extract_ip_addresses(header); # If there are more than one IP address found, return the second. if ( |ips| > 1 ) return ips[1]; @@ -163,7 +163,7 @@ event smtp_request(c: connection, is_orig: bool, command: string, arg: string) & { if ( ! c$smtp?$rcptto ) c$smtp$rcptto = set(); - add c$smtp$rcptto[split1(arg, /:[[:blank:]]*/)[2]]; + add c$smtp$rcptto[split_string1(arg, /:[[:blank:]]*/)[1]]; c$smtp$has_client_activity = T; } @@ -172,8 +172,8 @@ event smtp_request(c: connection, is_orig: bool, command: string, arg: string) & # Flush last message in case we didn't see the server's acknowledgement. smtp_message(c); - local partially_done = split1(arg, /:[[:blank:]]*/)[2]; - c$smtp$mailfrom = split1(partially_done, /[[:blank:]]?/)[1]; + local partially_done = split_string1(arg, /:[[:blank:]]*/)[1]; + c$smtp$mailfrom = split_string1(partially_done, /[[:blank:]]?/)[0]; c$smtp$has_client_activity = T; } } @@ -234,14 +234,14 @@ event mime_one_header(c: connection, h: mime_header_rec) &priority=5 if ( ! c$smtp?$to ) c$smtp$to = set(); - local to_parts = split(h$value, /[[:blank:]]*,[[:blank:]]*/); + local to_parts = split_string(h$value, /[[:blank:]]*,[[:blank:]]*/); for ( i in to_parts ) add c$smtp$to[to_parts[i]]; } else if ( h$name == "X-ORIGINATING-IP" ) { - local addresses = find_ip_addresses(h$value); + local addresses = extract_ip_addresses(h$value); if ( 1 in addresses ) c$smtp$x_originating_ip = to_addr(addresses[1]); } diff --git a/scripts/base/utils/active-http.bro b/scripts/base/utils/active-http.bro index 5dc512408a..de78eeac6d 100644 --- a/scripts/base/utils/active-http.bro +++ b/scripts/base/utils/active-http.bro @@ -105,21 +105,21 @@ function request(req: Request): ActiveHTTP::Response # The reply is the first line. if ( i == 0 ) { - local response_line = split_n(headers[0], /[[:blank:]]+/, F, 2); + local response_line = split_string_n(headers[0], /[[:blank:]]+/, F, 2); if ( |response_line| != 3 ) return resp; - resp$code = to_count(response_line[2]); - resp$msg = response_line[3]; + resp$code = to_count(response_line[1]); + resp$msg = response_line[2]; resp$body = join_string_vec(result$files[bodyfile], ""); } else { local line = headers[i]; - local h = split1(line, /:/); + local h = split_string1(line, /:/); if ( |h| != 2 ) next; - resp$headers[h[1]] = sub_bytes(h[2], 0, |h[2]|-1); + resp$headers[h[0]] = sub_bytes(h[1], 0, |h[1]|-1); } } return resp; diff --git a/scripts/base/utils/addrs.bro b/scripts/base/utils/addrs.bro index 9e33e6d585..e8fd746e5e 100644 --- a/scripts/base/utils/addrs.bro +++ b/scripts/base/utils/addrs.bro @@ -32,7 +32,7 @@ const ip_addr_regex = ## octets: an array of strings to check for valid octet values. ## ## Returns: T if every element is between 0 and 255, inclusive, else F. -function has_valid_octets(octets: string_array): bool +function has_valid_octets(octets: string_vec): bool { local num = 0; for ( i in octets ) @@ -51,10 +51,10 @@ function has_valid_octets(octets: string_array): bool ## Returns: T if the string is a valid IPv4 or IPv6 address format. function is_valid_ip(ip_str: string): bool { - local octets: string_array; + local octets: string_vec; if ( ip_str == ipv4_addr_regex ) { - octets = split(ip_str, /\./); + octets = split_string(ip_str, /\./); if ( |octets| != 4 ) return F; @@ -67,13 +67,13 @@ function is_valid_ip(ip_str: string): bool { # the regexes for hybrid IPv6-IPv4 address formats don't for valid # octets within the IPv4 part, so do that now - octets = split(ip_str, /\./); + octets = split_string(ip_str, /\./); if ( |octets| != 4 ) return F; # get rid of remaining IPv6 stuff in first octet - local tmp = split(octets[1], /:/); - octets[1] = tmp[|tmp|]; + local tmp = split_string(octets[0], /:/); + octets[0] = tmp[|tmp| - 1]; return has_valid_octets(octets); } @@ -92,14 +92,32 @@ function is_valid_ip(ip_str: string): bool ## input: a string that may contain an IP address anywhere within it. ## ## Returns: an array containing all valid IP address strings found in *input*. -function find_ip_addresses(input: string): string_array +function find_ip_addresses(input: string): string_array &deprecated { - local parts = split_all(input, ip_addr_regex); + local parts = split_string_all(input, ip_addr_regex); local output: string_array; for ( i in parts ) { - if ( i % 2 == 0 && is_valid_ip(parts[i]) ) + if ( i % 2 == 1 && is_valid_ip(parts[i]) ) + output[|output|] = parts[i]; + } + return output; + } + +## Extracts all IP (v4 or v6) address strings from a given string. +## +## input: a string that may contain an IP address anywhere within it. +## +## Returns: an array containing all valid IP address strings found in *input*. +function extract_ip_addresses(input: string): string_vec + { + local parts = split_string_all(input, ip_addr_regex); + local output: string_vec; + + for ( i in parts ) + { + if ( i % 2 == 1 && is_valid_ip(parts[i]) ) output[|output|] = parts[i]; } return output; diff --git a/scripts/base/utils/exec.bro b/scripts/base/utils/exec.bro index 37ec35cb2c..15d88e9851 100644 --- a/scripts/base/utils/exec.bro +++ b/scripts/base/utils/exec.bro @@ -82,9 +82,9 @@ event Exec::line(description: Input::EventDescription, tpe: Input::Event, s: str event Exec::file_line(description: Input::EventDescription, tpe: Input::Event, s: string) { - local parts = split1(description$name, /_/); - local name = parts[1]; - local track_file = parts[2]; + local parts = split_string1(description$name, /_/); + local name = parts[0]; + local track_file = parts[1]; local result = results[name]; if ( ! result?$files ) @@ -99,13 +99,13 @@ event Exec::file_line(description: Input::EventDescription, tpe: Input::Event, s event Input::end_of_data(orig_name: string, source:string) { local name = orig_name; - local parts = split1(name, /_/); - name = parts[1]; + local parts = split_string1(name, /_/); + name = parts[0]; if ( name !in pending_commands || |parts| < 2 ) return; - local track_file = parts[2]; + local track_file = parts[1]; # If the file is empty, still add it to the result$files table. This is needed # because it is expected that the file was read even if it was empty. diff --git a/scripts/base/utils/files.bro b/scripts/base/utils/files.bro index b88ae5763e..766efd649c 100644 --- a/scripts/base/utils/files.bro +++ b/scripts/base/utils/files.bro @@ -23,7 +23,7 @@ function extract_filename_from_content_disposition(data: string): string # Remove quotes around the filename if they are there. if ( /^\"/ in filename ) - filename = split_n(filename, /\"/, F, 2)[2]; + filename = split_string_n(filename, /\"/, F, 2)[1]; # Remove the language and encoding if it's there. if ( /^[a-zA-Z0-9\!#$%&+-^_`{}~]+'[a-zA-Z0-9\!#$%&+-^_`{}~]*'/ in filename ) diff --git a/scripts/base/utils/numbers.bro b/scripts/base/utils/numbers.bro index 9b100862d4..da8c15d7a0 100644 --- a/scripts/base/utils/numbers.bro +++ b/scripts/base/utils/numbers.bro @@ -2,9 +2,9 @@ ## If no integer can be found, 0 is returned. function extract_count(s: string): count { - local parts = split_n(s, /[0-9]+/, T, 1); - if ( 2 in parts ) - return to_count(parts[2]); + local parts = split_string_n(s, /[0-9]+/, T, 1); + if ( 1 in parts ) + return to_count(parts[1]); else return 0; - } \ No newline at end of file + } diff --git a/scripts/base/utils/paths.bro b/scripts/base/utils/paths.bro index ce083eb6d0..6de5b85e2e 100644 --- a/scripts/base/utils/paths.bro +++ b/scripts/base/utils/paths.bro @@ -13,12 +13,12 @@ const absolute_path_pat = /(\/|[A-Za-z]:[\\\/]).*/; function extract_path(input: string): string { const dir_pattern = /(\/|[A-Za-z]:[\\\/])([^\"\ ]|(\\\ ))*/; - local parts = split_all(input, dir_pattern); + local parts = split_string_all(input, dir_pattern); if ( |parts| < 3 ) return ""; - return parts[2]; + return parts[1]; } ## Compresses a given path by removing '..'s and the parent directory it @@ -31,27 +31,27 @@ function compress_path(dir: string): string { const cdup_sep = /((\/)*([^\/]|\\\/)+)?((\/)+\.\.(\/)*)/; - local parts = split_n(dir, cdup_sep, T, 1); + local parts = split_string_n(dir, cdup_sep, T, 1); if ( |parts| > 1 ) { # reaching a point with two parent dir references back-to-back means # we don't know about anything higher in the tree to pop off - if ( parts[2] == "../.." ) - return cat_string_array(parts); - if ( sub_bytes(parts[2], 0, 1) == "/" ) - parts[2] = "/"; + if ( parts[1] == "../.." ) + return join_string_vec(parts, ""); + if ( sub_bytes(parts[1], 0, 1) == "/" ) + parts[1] = "/"; else - parts[2] = ""; - dir = cat_string_array(parts); + parts[1] = ""; + dir = join_string_vec(parts, ""); return compress_path(dir); } const multislash_sep = /(\/\.?){2,}/; - parts = split_all(dir, multislash_sep); + parts = split_string_all(dir, multislash_sep); for ( i in parts ) - if ( i % 2 == 0 ) + if ( i % 2 == 1 ) parts[i] = "/"; - dir = cat_string_array(parts); + dir = join_string_vec(parts, ""); # remove trailing slashes from path if ( |dir| > 1 && sub_bytes(dir, |dir|, 1) == "/" ) diff --git a/scripts/base/utils/patterns.bro b/scripts/base/utils/patterns.bro index 957e19a14b..47b8cf4e37 100644 --- a/scripts/base/utils/patterns.bro +++ b/scripts/base/utils/patterns.bro @@ -50,11 +50,11 @@ type PatternMatchResult: record { ## Returns: a record indicating the match status. function match_pattern(s: string, p: pattern): PatternMatchResult { - local a = split_n(s, p, T, 1); + local a = split_string_n(s, p, T, 1); if ( |a| == 1 ) # no match return [$matched = F, $str = "", $off = 0]; else - return [$matched = T, $str = a[2], $off = |a[1]| + 1]; + return [$matched = T, $str = a[1], $off = |a[0]| + 1]; } diff --git a/scripts/base/utils/urls.bro b/scripts/base/utils/urls.bro index d4279cd0ce..41a2ab5639 100644 --- a/scripts/base/utils/urls.bro +++ b/scripts/base/utils/urls.bro @@ -48,7 +48,7 @@ function find_all_urls_without_scheme(s: string): string_set function decompose_uri(s: string): URI { - local parts: string_array; + local parts: string_vec; local u: URI = [$netlocation="", $path="/"]; if ( /\?/ in s) @@ -56,55 +56,55 @@ function decompose_uri(s: string): URI # Parse query. u$params = table(); - parts = split1(s, /\?/); - s = parts[1]; - local query: string = parts[2]; + parts = split_string1(s, /\?/); + s = parts[0]; + local query: string = parts[1]; if ( /&/ in query ) { - local opv: table[count] of string = split(query, /&/); + local opv = split_string(query, /&/); for ( each in opv ) { if ( /=/ in opv[each] ) { - parts = split1(opv[each], /=/); - u$params[parts[1]] = parts[2]; + parts = split_string1(opv[each], /=/); + u$params[parts[0]] = parts[1]; } } } else { - parts = split1(query, /=/); - u$params[parts[1]] = parts[2]; + parts = split_string1(query, /=/); + u$params[parts[0]] = parts[1]; } } if ( /:\/\// in s ) { # Parse scheme and remove from s. - parts = split1(s, /:\/\//); - u$scheme = parts[1]; - s = parts[2]; + parts = split_string1(s, /:\/\//); + u$scheme = parts[0]; + s = parts[1]; } if ( /\// in s ) { # Parse path and remove from s. - parts = split1(s, /\//); - s = parts[1]; - u$path = fmt("/%s", parts[2]); + parts = split_string1(s, /\//); + s = parts[0]; + u$path = fmt("/%s", parts[1]); if ( |u$path| > 1 && u$path[|u$path| - 1] != "/" ) { local last_token: string = find_last(u$path, /\/.+/); - local full_filename = split1(last_token, /\//)[2]; + local full_filename = split_string1(last_token, /\//)[1]; if ( /\./ in full_filename ) { u$file_name = full_filename; - u$file_base = split1(full_filename, /\./)[1]; - u$file_ext = split1(full_filename, /\./)[2]; + u$file_base = split_string1(full_filename, /\./)[0]; + u$file_ext = split_string1(full_filename, /\./)[1]; } else { @@ -117,9 +117,9 @@ function decompose_uri(s: string): URI if ( /:/ in s ) { # Parse location and port. - parts = split1(s, /:/); - u$netlocation = parts[1]; - u$portnum = to_count(parts[2]); + parts = split_string1(s, /:/); + u$netlocation = parts[0]; + u$portnum = to_count(parts[1]); } else u$netlocation = s; diff --git a/scripts/policy/frameworks/files/detect-MHR.bro b/scripts/policy/frameworks/files/detect-MHR.bro index d0b8a852e6..6917212356 100644 --- a/scripts/policy/frameworks/files/detect-MHR.bro +++ b/scripts/policy/frameworks/files/detect-MHR.bro @@ -42,15 +42,15 @@ function do_mhr_lookup(hash: string, fi: Notice::FileInfo) when ( local MHR_result = lookup_hostname_txt(hash_domain) ) { # Data is returned as " " - local MHR_answer = split1(MHR_result, / /); + local MHR_answer = split_string1(MHR_result, / /); if ( |MHR_answer| == 2 ) { - local mhr_detect_rate = to_count(MHR_answer[2]); + local mhr_detect_rate = to_count(MHR_answer[1]); if ( mhr_detect_rate >= notice_threshold ) { - local mhr_first_detected = double_to_time(to_double(MHR_answer[1])); + local mhr_first_detected = double_to_time(to_double(MHR_answer[0])); local readable_first_detected = strftime("%Y-%m-%d %H:%M:%S", mhr_first_detected); local message = fmt("Malware Hash Registry Detection rate: %d%% Last seen: %s", mhr_detect_rate, readable_first_detected); local virustotal_url = fmt(match_sub_url, hash); diff --git a/scripts/policy/frameworks/intel/seen/http-headers.bro b/scripts/policy/frameworks/intel/seen/http-headers.bro index a961896640..864b685126 100644 --- a/scripts/policy/frameworks/intel/seen/http-headers.bro +++ b/scripts/policy/frameworks/intel/seen/http-headers.bro @@ -31,7 +31,7 @@ event http_header(c: connection, is_orig: bool, name: string, value: string) case "X-FORWARDED-FOR": if ( is_valid_ip(value) ) { - local addrs = find_ip_addresses(value); + local addrs = extract_ip_addresses(value); for ( i in addrs ) { Intel::seen([$host=to_addr(addrs[i]), diff --git a/scripts/policy/frameworks/intel/seen/smtp.bro b/scripts/policy/frameworks/intel/seen/smtp.bro index d760995e51..0393dbab7d 100644 --- a/scripts/policy/frameworks/intel/seen/smtp.bro +++ b/scripts/policy/frameworks/intel/seen/smtp.bro @@ -30,10 +30,10 @@ event mime_end_entity(c: connection) if ( c$smtp?$mailfrom ) { - local mailfromparts = split_n(c$smtp$mailfrom, /<.+>/, T, 1); + local mailfromparts = split_string_n(c$smtp$mailfrom, /<.+>/, T, 1); if ( |mailfromparts| > 2 ) { - Intel::seen([$indicator=mailfromparts[2][1:-2], + Intel::seen([$indicator=mailfromparts[1][1:-2], $indicator_type=Intel::EMAIL, $conn=c, $where=SMTP::IN_MAIL_FROM]); @@ -44,10 +44,10 @@ event mime_end_entity(c: connection) { for ( rcptto in c$smtp$rcptto ) { - local rcpttoparts = split_n(rcptto, /<.+>/, T, 1); + local rcpttoparts = split_string_n(rcptto, /<.+>/, T, 1); if ( |rcpttoparts| > 2 ) { - Intel::seen([$indicator=rcpttoparts[2][1:-2], + Intel::seen([$indicator=rcpttoparts[1][1:-2], $indicator_type=Intel::EMAIL, $conn=c, $where=SMTP::IN_RCPT_TO]); @@ -57,10 +57,10 @@ event mime_end_entity(c: connection) if ( c$smtp?$from ) { - local fromparts = split_n(c$smtp$from, /<.+>/, T, 1); + local fromparts = split_string_n(c$smtp$from, /<.+>/, T, 1); if ( |fromparts| > 2 ) { - Intel::seen([$indicator=fromparts[2][1:-2], + Intel::seen([$indicator=fromparts[1][1:-2], $indicator_type=Intel::EMAIL, $conn=c, $where=SMTP::IN_FROM]); @@ -71,10 +71,10 @@ event mime_end_entity(c: connection) { for ( email_to in c$smtp$to ) { - local toparts = split_n(email_to, /<.+>/, T, 1); + local toparts = split_string_n(email_to, /<.+>/, T, 1); if ( |toparts| > 2 ) { - Intel::seen([$indicator=toparts[2][1:-2], + Intel::seen([$indicator=toparts[1][1:-2], $indicator_type=Intel::EMAIL, $conn=c, $where=SMTP::IN_TO]); @@ -84,10 +84,10 @@ event mime_end_entity(c: connection) if ( c$smtp?$reply_to ) { - local replytoparts = split_n(c$smtp$reply_to, /<.+>/, T, 1); + local replytoparts = split_string_n(c$smtp$reply_to, /<.+>/, T, 1); if ( |replytoparts| > 2 ) { - Intel::seen([$indicator=replytoparts[2][1:-2], + Intel::seen([$indicator=replytoparts[1][1:-2], $indicator_type=Intel::EMAIL, $conn=c, $where=SMTP::IN_REPLY_TO]); diff --git a/scripts/policy/frameworks/software/vulnerable.bro b/scripts/policy/frameworks/software/vulnerable.bro index ee8d90b21f..527623d621 100644 --- a/scripts/policy/frameworks/software/vulnerable.bro +++ b/scripts/policy/frameworks/software/vulnerable.bro @@ -55,18 +55,18 @@ function decode_vulnerable_version_range(vuln_sw: string): VulnerableVersionRang return vvr; } - local versions = split1(vuln_sw, /\x09/); + local versions = split_string1(vuln_sw, /\x09/); for ( i in versions ) { - local field_and_ver = split1(versions[i], /=/); + local field_and_ver = split_string1(versions[i], /=/); if ( |field_and_ver| != 2 ) return vvr; #failure! - local ver = Software::parse(field_and_ver[2])$version; - if ( field_and_ver[1] == "min" ) + local ver = Software::parse(field_and_ver[1])$version; + if ( field_and_ver[0] == "min" ) vvr$min = ver; - else if ( field_and_ver[1] == "max" ) + else if ( field_and_ver[0] == "max" ) vvr$max = ver; } @@ -84,15 +84,15 @@ event grab_vulnerable_versions(i: count) when ( local result = lookup_hostname_txt(cat(i,".",vulnerable_versions_update_endpoint)) ) { - local parts = split1(result, /\x09/); + local parts = split_string1(result, /\x09/); if ( |parts| != 2 ) #failure or end of list! { schedule vulnerable_versions_update_interval { grab_vulnerable_versions(1) }; return; } - local sw = parts[1]; - local vvr = decode_vulnerable_version_range(parts[2]); + local sw = parts[0]; + local vvr = decode_vulnerable_version_range(parts[1]); if ( sw !in internal_vulnerable_versions ) internal_vulnerable_versions[sw] = set(); add internal_vulnerable_versions[sw][vvr]; diff --git a/scripts/policy/misc/detect-traceroute/main.bro b/scripts/policy/misc/detect-traceroute/main.bro index aa403e6a08..68151e209a 100644 --- a/scripts/policy/misc/detect-traceroute/main.bro +++ b/scripts/policy/misc/detect-traceroute/main.bro @@ -74,10 +74,10 @@ event bro_init() &priority=5 $threshold=icmp_time_exceeded_threshold, $threshold_crossed(key: SumStats::Key, result: SumStats::Result) = { - local parts = split_n(key$str, /-/, F, 2); - local src = to_addr(parts[1]); - local dst = to_addr(parts[2]); - local proto = parts[3]; + local parts = split_string_n(key$str, /-/, F, 2); + local src = to_addr(parts[0]); + local dst = to_addr(parts[1]); + local proto = parts[2]; Log::write(LOG, [$ts=network_time(), $src=src, $dst=dst, $proto=proto]); NOTICE([$note=Traceroute::Detected, $msg=fmt("%s seems to be running traceroute using %s", src, proto), diff --git a/scripts/policy/protocols/http/software-browser-plugins.bro b/scripts/policy/protocols/http/software-browser-plugins.bro index b466a9da40..ab4bb93b15 100644 --- a/scripts/policy/protocols/http/software-browser-plugins.bro +++ b/scripts/policy/protocols/http/software-browser-plugins.bro @@ -45,13 +45,13 @@ event log_http(rec: Info) if ( rec$omniture && rec?$uri ) { # We do {5,} because sometimes we see p=6 in the urls. - local parts = split_n(rec$uri, /&p=([^&]{5,});&/, T, 1); - if ( 2 in parts ) + local parts = split_string_n(rec$uri, /&p=([^&]{5,});&/, T, 1); + if ( 1 in parts ) { # We do sub_bytes here just to remove the extra extracted # characters from the regex split above. - local sw = sub_bytes(parts[2], 4, |parts[2]|-5); - local plugins = split(sw, /[[:blank:]]*;[[:blank:]]*/); + local sw = sub_bytes(parts[1], 4, |parts[1]|-5); + local plugins = split_string(sw, /[[:blank:]]*;[[:blank:]]*/); for ( i in plugins ) Software::found(rec$id, [$unparsed_version=plugins[i], $host=rec$id$orig_h, $software_type=BROWSER_PLUGIN]); diff --git a/scripts/policy/protocols/smtp/blocklists.bro b/scripts/policy/protocols/smtp/blocklists.bro index b1fb0e498d..57aef4ee48 100644 --- a/scripts/policy/protocols/smtp/blocklists.bro +++ b/scripts/policy/protocols/smtp/blocklists.bro @@ -47,7 +47,7 @@ event smtp_reply(c: connection, is_orig: bool, code: count, cmd: string, local message = fmt("%s received an error message mentioning an SMTP block list", c$id$orig_h); # Determine if the originator's IP address is in the message. - local ips = find_ip_addresses(msg); + local ips = extract_ip_addresses(msg); local text_ip = ""; if ( |ips| > 0 && to_addr(ips[0]) == c$id$orig_h ) { diff --git a/scripts/policy/protocols/ssl/notary.bro b/scripts/policy/protocols/ssl/notary.bro index e2b0bb2faf..9e55933820 100644 --- a/scripts/policy/protocols/ssl/notary.bro +++ b/scripts/policy/protocols/ssl/notary.bro @@ -70,23 +70,23 @@ event ssl_established(c: connection) &priority=3 clear_waitlist(digest); return; } - local fields = split(str, / /); + local fields = split_string(str, / /); if ( |fields| != 5 ) # version 1 has 5 fields. { clear_waitlist(digest); return; } - local version = split(fields[1], /=/)[2]; + local version = split_string(fields[0], /=/)[2]; if ( version != "1" ) { clear_waitlist(digest); return; } local r = notary_cache[digest]; - r$first_seen = to_count(split(fields[2], /=/)[2]); - r$last_seen = to_count(split(fields[3], /=/)[2]); - r$times_seen = to_count(split(fields[4], /=/)[2]); - r$valid = split(fields[5], /=/)[2] == "1"; + r$first_seen = to_count(split_string(fields[1], /=/)[1]); + r$last_seen = to_count(split_string(fields[2], /=/)[1]); + r$times_seen = to_count(split_string(fields[3], /=/)[1]); + r$valid = split_string(fields[4], /=/)[1] == "1"; # Assign notary answer to all records waiting for this digest. if ( digest in waitlist ) diff --git a/src/strings.bif b/src/strings.bif index 4a30ca2aa4..84d6014cff 100644 --- a/src/strings.bif +++ b/src/strings.bif @@ -130,7 +130,7 @@ BroString* cat_string_array_n(TableVal* tbl, int start, int end) ## .. 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 +function cat_string_array%(a: string_array%): string &deprecated %{ TableVal* tbl = a->AsTableVal(); return new StringVal(cat_string_array_n(tbl, 1, a->AsTable()->Length())); @@ -149,7 +149,7 @@ function cat_string_array%(a: string_array%): string ## .. 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 +function cat_string_array_n%(a: string_array, start: count, end: count%): string &deprecated %{ TableVal* tbl = a->AsTableVal(); return new StringVal(cat_string_array_n(tbl, start, end)); @@ -168,7 +168,7 @@ function cat_string_array_n%(a: string_array, start: count, end: count%): string ## .. 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 +function join_string_array%(sep: string, a: string_array%): string &deprecated %{ vector vs; TableVal* tbl = a->AsTableVal(); @@ -230,7 +230,7 @@ function join_string_vec%(vec: string_vec, sep: string%): string ## Returns: A sorted copy of *a*. ## ## .. bro:see:: sort -function sort_string_array%(a: string_array%): string_array +function sort_string_array%(a: string_array%): string_array &deprecated %{ TableVal* tbl = a->AsTableVal(); int n = a->AsTable()->Length(); @@ -338,6 +338,62 @@ static int match_prefix(int s_len, const char* s, int t_len, const char* t) return 1; } +VectorVal* do_split_string(StringVal* str_val, RE_Matcher* re, int incl_sep, + int max_num_sep) + { + VectorVal* rval = new VectorVal(new VectorType(base_type(TYPE_STRING))); + const u_char* s = str_val->Bytes(); + int n = str_val->Len(); + const u_char* end_of_s = s + n; + int num = 0; + int num_sep = 0; + + int offset = 0; + while ( n >= 0 ) + { + offset = 0; + // Find next match offset. + int end_of_match = 0; + while ( n > 0 && + (end_of_match = re->MatchPrefix(s + offset, n)) <= 0 ) + { + // Move on to next byte. + ++offset; + --n; + } + + if ( max_num_sep && num_sep >= max_num_sep ) + { + offset = end_of_s - s; + n=0; + } + + rval->Assign(num++, new StringVal(offset, (const char*) s)); + + // No more separators will be needed if this is the end of string. + if ( n <= 0 ) + break; + + if ( incl_sep ) + { // including the part that matches the pattern + rval->Assign(num++, new StringVal(end_of_match, (const char*) s+offset)); + } + + if ( max_num_sep && num_sep >= max_num_sep ) + break; + + ++num_sep; + + n -= end_of_match; + s += offset + end_of_match;; + + if ( s > end_of_s ) + reporter->InternalError("RegMatch in split goes beyond the string"); + } + + return rval; + } + Val* do_split(StringVal* str_val, RE_Matcher* re, int incl_sep, int max_num_sep) { TableVal* a = new TableVal(string_array); @@ -493,17 +549,33 @@ Val* do_sub(StringVal* str_val, RE_Matcher* re, StringVal* repl, int do_all) ## 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 +## .. bro:see:: split1 split_all split_n str_split split_string1 split_string_all split_string_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 +function split%(str: string, re: pattern%): string_array &deprecated %{ return do_split(str, re, 0, 0); %} +## 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:: split_string1 split_string_all split_string_n str_split +## +function split_string%(str: string, re: pattern%): string_vec + %{ + return do_split_string(str, re, 0, 0); + %} + ## Splits a string *once* into a two-element array of strings according to a ## pattern. This function is the same as :bro:id:`split`, but *str* is only ## split once (if possible) at the earliest position and an array of two strings @@ -518,12 +590,32 @@ function split%(str: string, re: pattern%): string_array ## 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 +## .. bro:see:: split split_all split_n str_split split_string split_string_all split_string_n str_split +function split1%(str: string, re: pattern%): string_array &deprecated %{ return do_split(str, re, 0, 1); %} +## Splits a string *once* into a two-element array of strings according to a +## pattern. This function is the same as :bro:id:`split_string`, but *str* 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_string split_string_all split_string_n str_split +function split_string1%(str: string, re: pattern%): string_vec + %{ + return do_split_string(str, re, 0, 1); + %} + ## 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 @@ -538,12 +630,32 @@ function split1%(str: string, re: pattern%): string_array ## 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_n str_split -function split_all%(str: string, re: pattern%): string_array +## .. bro:see:: split split1 split_n str_split split_string split_string1 split_string_n str_split +function split_all%(str: string, re: pattern%): string_array &deprecated %{ return do_split(str, re, 1, 0); %} +## Splits a string into an array of strings according to a pattern. This +## function is the same as :bro:id:`split_string`, except that the separators +## are returned as well. For example, ``split_string_all("a-b--cd", /(\-)+/)`` +## returns ``{"a", "-", "b", "--", "cd"}``: odd-indexed elements do match the +## pattern and even-indexed ones do not. +## +## 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* (even-indexed) +## and the part that matches *re* (odd-indexed). +## +## .. bro:see:: split_string split_string1 split_string_n str_split +function split_string_all%(str: string, re: pattern%): string_vec + %{ + return do_split_string(str, re, 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 @@ -563,13 +675,39 @@ function split_all%(str: string, re: pattern%): string_array ## not matching *re* (odd-indexed) and the part that matches *re* ## (even-indexed). ## -## .. bro:see:: split split1 split_all str_split +## .. bro:see:: split split1 split_all str_split split_string split_string1 split_string_all str_split function split_n%(str: string, re: pattern, - incl_sep: bool, max_num_sep: count%): string_array + incl_sep: bool, max_num_sep: count%): string_array &deprecated %{ return do_split(str, re, incl_sep, max_num_sep); %} +## Splits a string a given number of times into an array of strings according +## to a pattern. This function is similar to :bro:id:`split_string1` and +## :bro:id:`split_string_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_string_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* (event-indexed) and the part that matches *re* +## (odd-indexed). +## +## .. bro:see:: split_string split_string1 split_string_all str_split +function split_string_n%(str: string, re: pattern, + incl_sep: bool, max_num_sep: count%): string_vec + %{ + return do_split_string(str, re, incl_sep, max_num_sep); + %} + ## Substitutes a given replacement string for the first occurrence of a pattern ## in a given string. ## diff --git a/testing/btest/Baseline/bifs.split_string/out b/testing/btest/Baseline/bifs.split_string/out new file mode 100644 index 0000000000..0ec2541f3d --- /dev/null +++ b/testing/btest/Baseline/bifs.split_string/out @@ -0,0 +1,32 @@ +t +s is a t +t +--------------------- +t +s is a test +--------------------- +t +hi +s is a t +es +t +--------------------- +t +s is a test +--------------------- +t +hi +s is a test +--------------------- +[, thi, s i, s a tes, t] +--------------------- +X-Mailer +Testing Test (http://www.example.com) +--------------------- +A += + B += + C += + D diff --git a/testing/btest/Baseline/plugins.hooks/output b/testing/btest/Baseline/plugins.hooks/output index cf6cabda5e..927a64692f 100644 --- a/testing/btest/Baseline/plugins.hooks/output +++ b/testing/btest/Baseline/plugins.hooks/output @@ -129,37 +129,37 @@ 0.000000 MetaHookPost CallFunction(Files::register_protocol, (Analyzer::ANALYZER_IRC_DATA, [get_file_handle=IRC::get_file_handle{ return (cat(Analyzer::ANALYZER_IRC_DATA, IRC::c$start_time, IRC::c$id, IRC::is_orig))}, describe=anonymous-function{ return ()}])) -> 0.000000 MetaHookPost CallFunction(Files::register_protocol, (Analyzer::ANALYZER_SMTP, [get_file_handle=SMTP::get_file_handle{ return (cat(Analyzer::ANALYZER_SMTP, SMTP::c$start_time, SMTP::c$smtp$trans_depth, SMTP::c$smtp_state$mime_depth))}, describe=SMTP::describe_file{ SMTP::cid{ if (SMTP::f$source != SMTP) return ()for ([SMTP::cid] in SMTP::f$conns) { SMTP::c = SMTP::f$conns[SMTP::cid]return (SMTP::describe(SMTP::c$smtp))}return ()}}])) -> 0.000000 MetaHookPost CallFunction(Files::register_protocol, (Analyzer::ANALYZER_SSL, [get_file_handle=SSL::get_file_handle{ return ()}, describe=SSL::describe_file{ SSL::cid{ if (SSL::f$source != SSL || !SSL::f?$info || !SSL::f$info?$x509 || !SSL::f$info$x509?$certificate) return ()for ([SSL::cid] in SSL::f$conns) { if (SSL::f$conns[SSL::cid]?$ssl) { SSL::c = SSL::f$conns[SSL::cid]return (cat(SSL::c$id$resp_h, :, SSL::c$id$resp_p))}}return (cat(Serial: , SSL::f$info$x509$certificate$serial, Subject: , SSL::f$info$x509$certificate$subject, Issuer: , SSL::f$info$x509$certificate$issuer))}}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Cluster::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Communication::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Conn::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (DHCP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (DNP3::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (DNS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (DPD::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (FTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Files::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (HTTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (IRC::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Intel::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Modbus::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Notice::ALARM_LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Notice::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (PacketFilter::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (RADIUS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Reporter::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (SMTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (SNMP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (SOCKS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (SSH::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (SSL::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Signatures::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Software::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Syslog::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Tunnel::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Unified2::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (Weird::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (X509::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> -0.000000 MetaHookPost CallFunction(Log::__add_filter, (mysql::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Cluster::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Communication::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Conn::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (DHCP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (DNP3::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (DNS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (DPD::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (FTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Files::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (HTTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (IRC::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Intel::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Modbus::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Notice::ALARM_LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Notice::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (PacketFilter::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (RADIUS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Reporter::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (SMTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (SNMP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (SOCKS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (SSH::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (SSL::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Signatures::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Software::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Syslog::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Tunnel::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Unified2::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (Weird::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (X509::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> +0.000000 MetaHookPost CallFunction(Log::__add_filter, (mysql::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, (Cluster::LOG, [columns=, ev=])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, (Communication::LOG, [columns=, ev=])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, (Conn::LOG, [columns=, ev=Conn::log_conn])) -> @@ -191,7 +191,7 @@ 0.000000 MetaHookPost CallFunction(Log::__create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -> -0.000000 MetaHookPost CallFunction(Log::__write, (PacketFilter::LOG, [ts=1421274039.845117, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::__write, (PacketFilter::LOG, [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, (Cluster::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, (Communication::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, (Conn::LOG)) -> @@ -285,8 +285,8 @@ 0.000000 MetaHookPost CallFunction(Log::create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -> -0.000000 MetaHookPost CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1421274039.845117, node=bro, filter=ip or not ip, init=T, success=T])) -> -0.000000 MetaHookPost CallFunction(Log::write, (PacketFilter::LOG, [ts=1421274039.845117, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::write, (PacketFilter::LOG, [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(Notice::want_pp, ()) -> 0.000000 MetaHookPost CallFunction(PacketFilter::build, ()) -> 0.000000 MetaHookPost CallFunction(PacketFilter::combine_filters, (ip or not ip, and, )) -> @@ -319,8 +319,8 @@ 0.000000 MetaHookPost CallFunction(reading_live_traffic, ()) -> 0.000000 MetaHookPost CallFunction(reading_traces, ()) -> 0.000000 MetaHookPost CallFunction(set_to_regex, ({}, (^\.?|\.)(~~)$)) -> -0.000000 MetaHookPost CallFunction(split1, (PacketFilter::LOG, <...>/)) -> -0.000000 MetaHookPost CallFunction(split_n, (PacketFilter, <...>/, T, 4)) -> +0.000000 MetaHookPost CallFunction(split_string1, (PacketFilter::LOG, <...>/)) -> +0.000000 MetaHookPost CallFunction(split_string_n, (PacketFilter, <...>/, T, 4)) -> 0.000000 MetaHookPost CallFunction(string_to_pattern, ((^\.?|\.)()$, F)) -> 0.000000 MetaHookPost CallFunction(sub, ((^\.?|\.)(~~)$, <...>/, )) -> 0.000000 MetaHookPost CallFunction(sub_bytes, (tFilter, 1, 1)) -> @@ -668,37 +668,37 @@ 0.000000 MetaHookPre CallFunction(Files::register_protocol, (Analyzer::ANALYZER_IRC_DATA, [get_file_handle=IRC::get_file_handle{ return (cat(Analyzer::ANALYZER_IRC_DATA, IRC::c$start_time, IRC::c$id, IRC::is_orig))}, describe=anonymous-function{ return ()}])) 0.000000 MetaHookPre CallFunction(Files::register_protocol, (Analyzer::ANALYZER_SMTP, [get_file_handle=SMTP::get_file_handle{ return (cat(Analyzer::ANALYZER_SMTP, SMTP::c$start_time, SMTP::c$smtp$trans_depth, SMTP::c$smtp_state$mime_depth))}, describe=SMTP::describe_file{ SMTP::cid{ if (SMTP::f$source != SMTP) return ()for ([SMTP::cid] in SMTP::f$conns) { SMTP::c = SMTP::f$conns[SMTP::cid]return (SMTP::describe(SMTP::c$smtp))}return ()}}])) 0.000000 MetaHookPre CallFunction(Files::register_protocol, (Analyzer::ANALYZER_SSL, [get_file_handle=SSL::get_file_handle{ return ()}, describe=SSL::describe_file{ SSL::cid{ if (SSL::f$source != SSL || !SSL::f?$info || !SSL::f$info?$x509 || !SSL::f$info$x509?$certificate) return ()for ([SSL::cid] in SSL::f$conns) { if (SSL::f$conns[SSL::cid]?$ssl) { SSL::c = SSL::f$conns[SSL::cid]return (cat(SSL::c$id$resp_h, :, SSL::c$id$resp_p))}}return (cat(Serial: , SSL::f$info$x509$certificate$serial, Subject: , SSL::f$info$x509$certificate$subject, Issuer: , SSL::f$info$x509$certificate$issuer))}}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Cluster::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Communication::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Conn::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (DHCP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (DNP3::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (DNS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (DPD::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (FTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Files::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (HTTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (IRC::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Intel::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Modbus::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Notice::ALARM_LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Notice::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (PacketFilter::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (RADIUS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Reporter::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (SMTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (SNMP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (SOCKS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (SSH::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (SSL::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Signatures::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Software::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Syslog::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Tunnel::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Unified2::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (Weird::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (X509::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) -0.000000 MetaHookPre CallFunction(Log::__add_filter, (mysql::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Cluster::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Communication::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Conn::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (DHCP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (DNP3::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (DNS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (DPD::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (FTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Files::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (HTTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (IRC::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Intel::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Modbus::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Notice::ALARM_LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Notice::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (PacketFilter::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (RADIUS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Reporter::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (SMTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (SNMP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (SOCKS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (SSH::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (SSL::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Signatures::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Software::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Syslog::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Tunnel::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Unified2::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (Weird::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (X509::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) +0.000000 MetaHookPre CallFunction(Log::__add_filter, (mysql::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, (Cluster::LOG, [columns=, ev=])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, (Communication::LOG, [columns=, ev=])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, (Conn::LOG, [columns=, ev=Conn::log_conn])) @@ -730,7 +730,7 @@ 0.000000 MetaHookPre CallFunction(Log::__create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -0.000000 MetaHookPre CallFunction(Log::__write, (PacketFilter::LOG, [ts=1421274039.845117, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::__write, (PacketFilter::LOG, [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, (Cluster::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, (Communication::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, (Conn::LOG)) @@ -824,8 +824,8 @@ 0.000000 MetaHookPre CallFunction(Log::create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) 0.000000 MetaHookPre CallFunction(Log::create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) 0.000000 MetaHookPre CallFunction(Log::create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -0.000000 MetaHookPre CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1421274039.845117, node=bro, filter=ip or not ip, init=T, success=T])) -0.000000 MetaHookPre CallFunction(Log::write, (PacketFilter::LOG, [ts=1421274039.845117, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::write, (PacketFilter::LOG, [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(Notice::want_pp, ()) 0.000000 MetaHookPre CallFunction(PacketFilter::build, ()) 0.000000 MetaHookPre CallFunction(PacketFilter::combine_filters, (ip or not ip, and, )) @@ -858,8 +858,8 @@ 0.000000 MetaHookPre CallFunction(reading_live_traffic, ()) 0.000000 MetaHookPre CallFunction(reading_traces, ()) 0.000000 MetaHookPre CallFunction(set_to_regex, ({}, (^\.?|\.)(~~)$)) -0.000000 MetaHookPre CallFunction(split1, (PacketFilter::LOG, <...>/)) -0.000000 MetaHookPre CallFunction(split_n, (PacketFilter, <...>/, T, 4)) +0.000000 MetaHookPre CallFunction(split_string1, (PacketFilter::LOG, <...>/)) +0.000000 MetaHookPre CallFunction(split_string_n, (PacketFilter, <...>/, T, 4)) 0.000000 MetaHookPre CallFunction(string_to_pattern, ((^\.?|\.)()$, F)) 0.000000 MetaHookPre CallFunction(sub, ((^\.?|\.)(~~)$, <...>/, )) 0.000000 MetaHookPre CallFunction(sub_bytes, (tFilter, 1, 1)) @@ -1207,37 +1207,37 @@ 0.000000 | HookCallFunction Files::register_protocol(Analyzer::ANALYZER_IRC_DATA, [get_file_handle=IRC::get_file_handle{ return (cat(Analyzer::ANALYZER_IRC_DATA, IRC::c$start_time, IRC::c$id, IRC::is_orig))}, describe=anonymous-function{ return ()}]) 0.000000 | HookCallFunction Files::register_protocol(Analyzer::ANALYZER_SMTP, [get_file_handle=SMTP::get_file_handle{ return (cat(Analyzer::ANALYZER_SMTP, SMTP::c$start_time, SMTP::c$smtp$trans_depth, SMTP::c$smtp_state$mime_depth))}, describe=SMTP::describe_file{ SMTP::cid{ if (SMTP::f$source != SMTP) return ()for ([SMTP::cid] in SMTP::f$conns) { SMTP::c = SMTP::f$conns[SMTP::cid]return (SMTP::describe(SMTP::c$smtp))}return ()}}]) 0.000000 | HookCallFunction Files::register_protocol(Analyzer::ANALYZER_SSL, [get_file_handle=SSL::get_file_handle{ return ()}, describe=SSL::describe_file{ SSL::cid{ if (SSL::f$source != SSL || !SSL::f?$info || !SSL::f$info?$x509 || !SSL::f$info$x509?$certificate) return ()for ([SSL::cid] in SSL::f$conns) { if (SSL::f$conns[SSL::cid]?$ssl) { SSL::c = SSL::f$conns[SSL::cid]return (cat(SSL::c$id$resp_h, :, SSL::c$id$resp_p))}}return (cat(Serial: , SSL::f$info$x509$certificate$serial, Subject: , SSL::f$info$x509$certificate$subject, Issuer: , SSL::f$info$x509$certificate$issuer))}}]) -0.000000 | HookCallFunction Log::__add_filter(Cluster::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(Communication::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(Conn::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(DHCP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(DNP3::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(DNS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(DPD::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(FTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(Files::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(HTTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(IRC::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(Intel::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(Modbus::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(Notice::ALARM_LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(Notice::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(PacketFilter::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(RADIUS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(Reporter::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(SMTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(SNMP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(SOCKS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(SSH::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(SSL::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(Signatures::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(Software::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(Syslog::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(Tunnel::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(Unified2::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(Weird::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(X509::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) -0.000000 | HookCallFunction Log::__add_filter(mysql::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[1]), _, to_lower(Log::parts[2])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Cluster::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Communication::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Conn::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(DHCP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(DNP3::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(DNS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(DPD::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(FTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Files::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(HTTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(IRC::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Intel::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Modbus::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Notice::ALARM_LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Notice::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(PacketFilter::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(RADIUS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Reporter::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(SMTP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(SNMP::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(SOCKS::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(SSH::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(SSL::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Signatures::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Software::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Syslog::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Tunnel::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Unified2::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(Weird::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(X509::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) +0.000000 | HookCallFunction Log::__add_filter(mysql::LOG, [name=default, writer=Log::WRITER_ASCII, pred=, path=, path_func=Log::default_path_func{ if ( != Log::path) return (Log::path)Log::id_str = fmt(%s, Log::id)Log::parts = split_string1(Log::id_str, <...>/, )return (cat(to_lower(Log::parts[0]), _, to_lower(Log::parts[1])))}elsereturn (to_lower(Log::id_str))}, include=, exclude=, log_local=T, log_remote=T, interv=0 secs, postprocessor=, config={}]) 0.000000 | HookCallFunction Log::__create_stream(Cluster::LOG, [columns=, ev=]) 0.000000 | HookCallFunction Log::__create_stream(Communication::LOG, [columns=, ev=]) 0.000000 | HookCallFunction Log::__create_stream(Conn::LOG, [columns=, ev=Conn::log_conn]) @@ -1269,7 +1269,7 @@ 0.000000 | HookCallFunction Log::__create_stream(Weird::LOG, [columns=, ev=Weird::log_weird]) 0.000000 | HookCallFunction Log::__create_stream(X509::LOG, [columns=, ev=X509::log_x509]) 0.000000 | HookCallFunction Log::__create_stream(mysql::LOG, [columns=, ev=MySQL::log_mysql]) -0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1421274039.845117, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction Log::add_default_filter(Cluster::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Communication::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Conn::LOG) @@ -1363,8 +1363,8 @@ 0.000000 | HookCallFunction Log::create_stream(Weird::LOG, [columns=, ev=Weird::log_weird]) 0.000000 | HookCallFunction Log::create_stream(X509::LOG, [columns=, ev=X509::log_x509]) 0.000000 | HookCallFunction Log::create_stream(mysql::LOG, [columns=, ev=MySQL::log_mysql]) -0.000000 | HookCallFunction Log::default_path_func(PacketFilter::LOG, , [ts=1421274039.845117, node=bro, filter=ip or not ip, init=T, success=T]) -0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1421274039.845117, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::default_path_func(PacketFilter::LOG, , [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction Notice::want_pp() 0.000000 | HookCallFunction PacketFilter::build() 0.000000 | HookCallFunction PacketFilter::combine_filters(ip or not ip, and, ) @@ -1397,8 +1397,8 @@ 0.000000 | HookCallFunction reading_live_traffic() 0.000000 | HookCallFunction reading_traces() 0.000000 | HookCallFunction set_to_regex({}, (^\.?|\.)(~~)$) -0.000000 | HookCallFunction split1(PacketFilter::LOG, <...>/) -0.000000 | HookCallFunction split_n(PacketFilter, <...>/, T, 4) +0.000000 | HookCallFunction split_string1(PacketFilter::LOG, <...>/) +0.000000 | HookCallFunction split_string_n(PacketFilter, <...>/, T, 4) 0.000000 | HookCallFunction string_to_pattern((^\.?|\.)()$, F) 0.000000 | HookCallFunction sub((^\.?|\.)(~~)$, <...>/, ) 0.000000 | HookCallFunction sub_bytes(tFilter, 1, 1) @@ -1486,7 +1486,7 @@ 1362692526.939527 MetaHookPost CallFunction(network_time, ()) -> 1362692526.939527 MetaHookPost CallFunction(protocol_confirmation, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/tcp], orig=[size=136, state=4, num_pkts=2, num_bytes_ip=116, flow_label=0], resp=[size=0, state=4, num_pkts=1, num_bytes_ip=60, flow_label=0], start_time=1362692526.869344, duration=0.070183, service={}, addl=, hot=0, history=ShAD, uid=CXWv6p3arKYeMETxOg, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], Analyzer::ANALYZER_HTTP, 3)) -> 1362692526.939527 MetaHookPost CallFunction(set_file_handle, (Analyzer::ANALYZER_HTTP1362692526.869344T11141.142.228.5:59856 > 192.150.187.43:80)) -> -1362692526.939527 MetaHookPost CallFunction(split1, (bro.org, <...>/)) -> +1362692526.939527 MetaHookPost CallFunction(split_string1, (bro.org, <...>/)) -> 1362692526.939527 MetaHookPost DrainEvents() -> 1362692526.939527 MetaHookPost QueueEvent(get_file_handle(Analyzer::ANALYZER_HTTP, [id=[orig_h=141.142.228.5, orig_p=59856<...>/tcp], orig=[size=136, state=4, num_pkts=2, num_bytes_ip=116, flow_label=0], resp=[size=0, state=4, num_pkts=1, num_bytes_ip=60, flow_label=0], start_time=1362692526.869344, duration=0.070183, service={HTTP}, addl=, hot=0, history=ShAD, uid=CXWv6p3arKYeMETxOg, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], T)) -> false 1362692526.939527 MetaHookPost QueueEvent(http_begin_entity([id=[orig_h=141.142.228.5, orig_p=59856<...>/tcp], orig=[size=136, state=4, num_pkts=2, num_bytes_ip=116, flow_label=0], resp=[size=0, state=4, num_pkts=1, num_bytes_ip=60, flow_label=0], start_time=1362692526.869344, duration=0.070183, service={HTTP}, addl=, hot=0, history=ShAD, uid=CXWv6p3arKYeMETxOg, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], T)) -> false @@ -1523,7 +1523,7 @@ 1362692526.939527 MetaHookPre CallFunction(network_time, ()) 1362692526.939527 MetaHookPre CallFunction(protocol_confirmation, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/tcp], orig=[size=136, state=4, num_pkts=2, num_bytes_ip=116, flow_label=0], resp=[size=0, state=4, num_pkts=1, num_bytes_ip=60, flow_label=0], start_time=1362692526.869344, duration=0.070183, service={}, addl=, hot=0, history=ShAD, uid=CXWv6p3arKYeMETxOg, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], Analyzer::ANALYZER_HTTP, 3)) 1362692526.939527 MetaHookPre CallFunction(set_file_handle, (Analyzer::ANALYZER_HTTP1362692526.869344T11141.142.228.5:59856 > 192.150.187.43:80)) -1362692526.939527 MetaHookPre CallFunction(split1, (bro.org, <...>/)) +1362692526.939527 MetaHookPre CallFunction(split_string1, (bro.org, <...>/)) 1362692526.939527 MetaHookPre DrainEvents() 1362692526.939527 MetaHookPre QueueEvent(get_file_handle(Analyzer::ANALYZER_HTTP, [id=[orig_h=141.142.228.5, orig_p=59856<...>/tcp], orig=[size=136, state=4, num_pkts=2, num_bytes_ip=116, flow_label=0], resp=[size=0, state=4, num_pkts=1, num_bytes_ip=60, flow_label=0], start_time=1362692526.869344, duration=0.070183, service={HTTP}, addl=, hot=0, history=ShAD, uid=CXWv6p3arKYeMETxOg, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], T)) 1362692526.939527 MetaHookPre QueueEvent(http_begin_entity([id=[orig_h=141.142.228.5, orig_p=59856<...>/tcp], orig=[size=136, state=4, num_pkts=2, num_bytes_ip=116, flow_label=0], resp=[size=0, state=4, num_pkts=1, num_bytes_ip=60, flow_label=0], start_time=1362692526.869344, duration=0.070183, service={HTTP}, addl=, hot=0, history=ShAD, uid=CXWv6p3arKYeMETxOg, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], T)) @@ -1561,7 +1561,7 @@ 1362692526.939527 | HookCallFunction network_time() 1362692526.939527 | HookCallFunction protocol_confirmation([id=[orig_h=141.142.228.5, orig_p=59856<...>/tcp], orig=[size=136, state=4, num_pkts=2, num_bytes_ip=116, flow_label=0], resp=[size=0, state=4, num_pkts=1, num_bytes_ip=60, flow_label=0], start_time=1362692526.869344, duration=0.070183, service={}, addl=, hot=0, history=ShAD, uid=CXWv6p3arKYeMETxOg, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], Analyzer::ANALYZER_HTTP, 3) 1362692526.939527 | HookCallFunction set_file_handle(Analyzer::ANALYZER_HTTP1362692526.869344T11141.142.228.5:59856 > 192.150.187.43:80) -1362692526.939527 | HookCallFunction split1(bro.org, <...>/) +1362692526.939527 | HookCallFunction split_string1(bro.org, <...>/) 1362692526.939527 | HookDrainEvents 1362692526.939527 | HookQueueEvent get_file_handle(Analyzer::ANALYZER_HTTP, [id=[orig_h=141.142.228.5, orig_p=59856<...>/tcp], orig=[size=136, state=4, num_pkts=2, num_bytes_ip=116, flow_label=0], resp=[size=0, state=4, num_pkts=1, num_bytes_ip=60, flow_label=0], start_time=1362692526.869344, duration=0.070183, service={HTTP}, addl=, hot=0, history=ShAD, uid=CXWv6p3arKYeMETxOg, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], T) 1362692526.939527 | HookQueueEvent http_begin_entity([id=[orig_h=141.142.228.5, orig_p=59856<...>/tcp], orig=[size=136, state=4, num_pkts=2, num_bytes_ip=116, flow_label=0], resp=[size=0, state=4, num_pkts=1, num_bytes_ip=60, flow_label=0], start_time=1362692526.869344, duration=0.070183, service={HTTP}, addl=, hot=0, history=ShAD, uid=CXWv6p3arKYeMETxOg, tunnel=, dpd=, conn=, extract_orig=F, extract_resp=F, dhcp=, dnp3=, dns=, dns_state=, ftp=, ftp_data_reuse=F, ssl=, http=, http_state=, irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], T) @@ -1607,7 +1607,7 @@ 1362692527.009512 MetaHookPost CallFunction(http_reply, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=, status_msg=, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=0], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], 1.1, 200, OK)) -> 1362692527.009512 MetaHookPost CallFunction(id_string, ([orig_h=141.142.228.5, orig_p=59856<...>/tcp])) -> 1362692527.009512 MetaHookPost CallFunction(set_file_handle, (Analyzer::ANALYZER_HTTP1362692526.869344F11141.142.228.5:59856 > 192.150.187.43:80)) -> -1362692527.009512 MetaHookPost CallFunction(split_all, (HTTP, <...>/)) -> +1362692527.009512 MetaHookPost CallFunction(split_string_all, (HTTP, <...>/)) -> 1362692527.009512 MetaHookPost DrainEvents() -> 1362692527.009512 MetaHookPost QueueEvent(file_new([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=])) -> false 1362692527.009512 MetaHookPost QueueEvent(file_over_new_connection([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) -> false @@ -1653,7 +1653,7 @@ 1362692527.009512 MetaHookPre CallFunction(http_reply, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=, status_msg=, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=0], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], 1.1, 200, OK)) 1362692527.009512 MetaHookPre CallFunction(id_string, ([orig_h=141.142.228.5, orig_p=59856<...>/tcp])) 1362692527.009512 MetaHookPre CallFunction(set_file_handle, (Analyzer::ANALYZER_HTTP1362692526.869344F11141.142.228.5:59856 > 192.150.187.43:80)) -1362692527.009512 MetaHookPre CallFunction(split_all, (HTTP, <...>/)) +1362692527.009512 MetaHookPre CallFunction(split_string_all, (HTTP, <...>/)) 1362692527.009512 MetaHookPre DrainEvents() 1362692527.009512 MetaHookPre QueueEvent(file_new([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=])) 1362692527.009512 MetaHookPre QueueEvent(file_over_new_connection([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F)) @@ -1700,7 +1700,7 @@ 1362692527.009512 | HookCallFunction http_reply([id=[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=, status_msg=, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=, orig_mime_depth=1, resp_mime_depth=0]}, current_request=1, current_response=0], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], 1.1, 200, OK) 1362692527.009512 | HookCallFunction id_string([orig_h=141.142.228.5, orig_p=59856<...>/tcp]) 1362692527.009512 | HookCallFunction set_file_handle(Analyzer::ANALYZER_HTTP1362692526.869344F11141.142.228.5:59856 > 192.150.187.43:80) -1362692527.009512 | HookCallFunction split_all(HTTP, <...>/) +1362692527.009512 | HookCallFunction split_string_all(HTTP, <...>/) 1362692527.009512 | HookDrainEvents 1362692527.009512 | HookQueueEvent file_new([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=]}, last_active=1362692527.009512, seen_bytes=0, total_bytes=, missing_bytes=0, overflow_bytes=0, timeout_interval=2.0 mins, bof_buffer_size=4096, bof_buffer=, info=, ftp=, http=, irc=, u2_events=]) 1362692527.009512 | HookQueueEvent file_over_new_connection([id=FakNcS1Jfe01uljb3, parent_id=, source=HTTP, is_orig=F, conns={[[orig_h=141.142.228.5, orig_p=59856<...>/1.14 (darwin12.2.0), request_body_len=0, response_body_len=0, status_code=200, status_msg=OK, info_code=, info_msg=, filename=, tags={}, username=, password=, capture_password=F, proxied=, range_request=F, orig_fuids=, orig_mime_types=, resp_fuids=, resp_mime_types=, current_entity=[filename=], orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F) @@ -1750,10 +1750,10 @@ 1362692527.009775 MetaHookPost CallFunction(http_message_done, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, [start=1362692527.009512, interrupted=F, finish_msg=message ends normally, body_length=4705, content_gap_length=0, header_length=280])) -> 1362692527.009775 MetaHookPost CallFunction(id_string, ([orig_h=141.142.228.5, orig_p=59856<...>/tcp])) -> 1362692527.009775 MetaHookPost CallFunction(set_file_handle, (Analyzer::ANALYZER_HTTP1362692526.869344F11141.142.228.5:59856 > 192.150.187.43:80)) -> -1362692527.009775 MetaHookPost CallFunction(split1, (Files::LOG, <...>/)) -> -1362692527.009775 MetaHookPost CallFunction(split1, (HTTP::LOG, <...>/)) -> -1362692527.009775 MetaHookPost CallFunction(split_n, (Files, <...>/, T, 4)) -> -1362692527.009775 MetaHookPost CallFunction(split_n, (HTTP, <...>/, T, 4)) -> +1362692527.009775 MetaHookPost CallFunction(split_string1, (Files::LOG, <...>/)) -> +1362692527.009775 MetaHookPost CallFunction(split_string1, (HTTP::LOG, <...>/)) -> +1362692527.009775 MetaHookPost CallFunction(split_string_n, (Files, <...>/, T, 4)) -> +1362692527.009775 MetaHookPost CallFunction(split_string_n, (HTTP, <...>/, T, 4)) -> 1362692527.009775 MetaHookPost CallFunction(to_lower, (Files)) -> 1362692527.009775 MetaHookPost CallFunction(to_lower, (HTTP)) -> 1362692527.009775 MetaHookPost DrainEvents() -> @@ -1785,10 +1785,10 @@ 1362692527.009775 MetaHookPre CallFunction(http_message_done, ([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, [start=1362692527.009512, interrupted=F, finish_msg=message ends normally, body_length=4705, content_gap_length=0, header_length=280])) 1362692527.009775 MetaHookPre CallFunction(id_string, ([orig_h=141.142.228.5, orig_p=59856<...>/tcp])) 1362692527.009775 MetaHookPre CallFunction(set_file_handle, (Analyzer::ANALYZER_HTTP1362692526.869344F11141.142.228.5:59856 > 192.150.187.43:80)) -1362692527.009775 MetaHookPre CallFunction(split1, (Files::LOG, <...>/)) -1362692527.009775 MetaHookPre CallFunction(split1, (HTTP::LOG, <...>/)) -1362692527.009775 MetaHookPre CallFunction(split_n, (Files, <...>/, T, 4)) -1362692527.009775 MetaHookPre CallFunction(split_n, (HTTP, <...>/, T, 4)) +1362692527.009775 MetaHookPre CallFunction(split_string1, (Files::LOG, <...>/)) +1362692527.009775 MetaHookPre CallFunction(split_string1, (HTTP::LOG, <...>/)) +1362692527.009775 MetaHookPre CallFunction(split_string_n, (Files, <...>/, T, 4)) +1362692527.009775 MetaHookPre CallFunction(split_string_n, (HTTP, <...>/, T, 4)) 1362692527.009775 MetaHookPre CallFunction(to_lower, (Files)) 1362692527.009775 MetaHookPre CallFunction(to_lower, (HTTP)) 1362692527.009775 MetaHookPre DrainEvents() @@ -1821,10 +1821,10 @@ 1362692527.009775 | HookCallFunction http_message_done([id=[orig_h=141.142.228.5, orig_p=59856<...>/plain], current_entity=, orig_mime_depth=1, resp_mime_depth=1]}, current_request=1, current_response=1], irc=, modbus=, mysql=, radius=, snmp=, smtp=, smtp_state=, socks=, ssh=, syslog=], F, [start=1362692527.009512, interrupted=F, finish_msg=message ends normally, body_length=4705, content_gap_length=0, header_length=280]) 1362692527.009775 | HookCallFunction id_string([orig_h=141.142.228.5, orig_p=59856<...>/tcp]) 1362692527.009775 | HookCallFunction set_file_handle(Analyzer::ANALYZER_HTTP1362692526.869344F11141.142.228.5:59856 > 192.150.187.43:80) -1362692527.009775 | HookCallFunction split1(Files::LOG, <...>/) -1362692527.009775 | HookCallFunction split1(HTTP::LOG, <...>/) -1362692527.009775 | HookCallFunction split_n(Files, <...>/, T, 4) -1362692527.009775 | HookCallFunction split_n(HTTP, <...>/, T, 4) +1362692527.009775 | HookCallFunction split_string1(Files::LOG, <...>/) +1362692527.009775 | HookCallFunction split_string1(HTTP::LOG, <...>/) +1362692527.009775 | HookCallFunction split_string_n(Files, <...>/, T, 4) +1362692527.009775 | HookCallFunction split_string_n(HTTP, <...>/, T, 4) 1362692527.009775 | HookCallFunction to_lower(Files) 1362692527.009775 | HookCallFunction to_lower(HTTP) 1362692527.009775 | HookDrainEvents @@ -1879,8 +1879,8 @@ 1362692527.080972 MetaHookPost CallFunction(net_stats, ()) -> 1362692527.080972 MetaHookPost CallFunction(reading_traces, ()) -> 1362692527.080972 MetaHookPost CallFunction(set_file_handle, (Analyzer::ANALYZER_HTTP1362692526.869344T11141.142.228.5:59856 > 192.150.187.43:80)) -> -1362692527.080972 MetaHookPost CallFunction(split1, (Conn::LOG, <...>/)) -> -1362692527.080972 MetaHookPost CallFunction(split_n, (Conn, <...>/, T, 4)) -> +1362692527.080972 MetaHookPost CallFunction(split_string1, (Conn::LOG, <...>/)) -> +1362692527.080972 MetaHookPost CallFunction(split_string_n, (Conn, <...>/, T, 4)) -> 1362692527.080972 MetaHookPost CallFunction(sub_bytes, (HTTP, 0, 1)) -> 1362692527.080972 MetaHookPost CallFunction(to_lower, (Conn)) -> 1362692527.080972 MetaHookPost CallFunction(to_lower, (HTTP)) -> @@ -1913,8 +1913,8 @@ 1362692527.080972 MetaHookPre CallFunction(net_stats, ()) 1362692527.080972 MetaHookPre CallFunction(reading_traces, ()) 1362692527.080972 MetaHookPre CallFunction(set_file_handle, (Analyzer::ANALYZER_HTTP1362692526.869344T11141.142.228.5:59856 > 192.150.187.43:80)) -1362692527.080972 MetaHookPre CallFunction(split1, (Conn::LOG, <...>/)) -1362692527.080972 MetaHookPre CallFunction(split_n, (Conn, <...>/, T, 4)) +1362692527.080972 MetaHookPre CallFunction(split_string1, (Conn::LOG, <...>/)) +1362692527.080972 MetaHookPre CallFunction(split_string_n, (Conn, <...>/, T, 4)) 1362692527.080972 MetaHookPre CallFunction(sub_bytes, (HTTP, 0, 1)) 1362692527.080972 MetaHookPre CallFunction(to_lower, (Conn)) 1362692527.080972 MetaHookPre CallFunction(to_lower, (HTTP)) @@ -1948,8 +1948,8 @@ 1362692527.080972 | HookCallFunction net_stats() 1362692527.080972 | HookCallFunction reading_traces() 1362692527.080972 | HookCallFunction set_file_handle(Analyzer::ANALYZER_HTTP1362692526.869344T11141.142.228.5:59856 > 192.150.187.43:80) -1362692527.080972 | HookCallFunction split1(Conn::LOG, <...>/) -1362692527.080972 | HookCallFunction split_n(Conn, <...>/, T, 4) +1362692527.080972 | HookCallFunction split_string1(Conn::LOG, <...>/) +1362692527.080972 | HookCallFunction split_string_n(Conn, <...>/, T, 4) 1362692527.080972 | HookCallFunction sub_bytes(HTTP, 0, 1) 1362692527.080972 | HookCallFunction to_lower(Conn) 1362692527.080972 | HookCallFunction to_lower(HTTP) diff --git a/testing/btest/Baseline/scripts.base.utils.addrs/output b/testing/btest/Baseline/scripts.base.utils.addrs/output index d93268a565..37afcb4719 100644 --- a/testing/btest/Baseline/scripts.base.utils.addrs/output +++ b/testing/btest/Baseline/scripts.base.utils.addrs/output @@ -30,14 +30,6 @@ T F F F -============ test find_ip_addresses() -{ -[0] = 1.1.1.1, -[2] = 3.3.3.3, -[1] = 2.2.2.2 -} -{ -[0] = 1.1.1.1, -[2] = 3.3.3.3, -[1] = 0:0:0:0:0:0:0:0 -} +============ test extract_ip_addresses() +[1.1.1.1, 2.2.2.2, 3.3.3.3] +[1.1.1.1, 0:0:0:0:0:0:0:0, 3.3.3.3] diff --git a/testing/btest/bifs/split_string.bro b/testing/btest/bifs/split_string.bro new file mode 100644 index 0000000000..e4d32b7f73 --- /dev/null +++ b/testing/btest/bifs/split_string.bro @@ -0,0 +1,36 @@ +# +# @TEST-EXEC: bro -b %INPUT >out +# @TEST-EXEC: btest-diff out + +function print_string_vector(v: string_vec) + { + for ( i in v ) + print v[i]; + } + +event bro_init() + { + local a = "this is a test"; + local pat = /hi|es/; + local idx = vector( 3, 6, 13); + + print_string_vector(split_string(a, pat)); + print "---------------------"; + print_string_vector(split_string1(a, pat)); + print "---------------------"; + print_string_vector(split_string_all(a, pat)); + print "---------------------"; + print_string_vector(split_string_n(a, pat, F, 1)); + print "---------------------"; + print_string_vector(split_string_n(a, pat, T, 1)); + print "---------------------"; + print str_split(a, idx); + print "---------------------"; + a = "X-Mailer: Testing Test (http://www.example.com)"; + pat = /:[[:blank:]]*/; + print_string_vector(split_string1(a, pat)); + print "---------------------"; + a = "A = B = C = D"; + pat = /=/; + print_string_vector(split_string_all(a, pat)); + } diff --git a/testing/btest/scripts/base/utils/addrs.test b/testing/btest/scripts/base/utils/addrs.test index 08bce5f35f..224fd9dc62 100644 --- a/testing/btest/scripts/base/utils/addrs.test +++ b/testing/btest/scripts/base/utils/addrs.test @@ -1,8 +1,7 @@ -# @TEST-EXEC: bro %INPUT > output +# @TEST-EXEC: bro -b %INPUT > output # @TEST-EXEC: btest-diff output -# This is loaded by default -#@load base/utils/addrs +@load base/utils/addrs event bro_init() { @@ -98,8 +97,8 @@ event bro_init() ip = "2001:db8:0:0:0:FFFF:192.168.0.256"; print is_valid_ip(ip); - print "============ test find_ip_addresses()"; - print find_ip_addresses("this is 1.1.1.1 a test 2.2.2.2 string with ip addresses 3.3.3.3"); - print find_ip_addresses("this is 1.1.1.1 a test 0:0:0:0:0:0:0:0 string with ip addresses 3.3.3.3"); + print "============ test extract_ip_addresses()"; + print extract_ip_addresses("this is 1.1.1.1 a test 2.2.2.2 string with ip addresses 3.3.3.3"); + print extract_ip_addresses("this is 1.1.1.1 a test 0:0:0:0:0:0:0:0 string with ip addresses 3.3.3.3"); } From f4d18e6940b7773bbaf4bf735bf1b25dfac7a751 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 21 Jan 2015 16:15:17 -0600 Subject: [PATCH 088/299] Update NEWS for deprecated/changed functions. --- NEWS | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/NEWS b/NEWS index 5e2ef52ca1..d78d1b3fc4 100644 --- a/NEWS +++ b/NEWS @@ -53,6 +53,35 @@ Changed Functionality record gives the how many bytes have been written so far (i.e. the "offset"). +- has_valid_octets: now uses a string_vec parameter instead of + string_array. + +Deprecated Functionality +------------------------ + +- The split* family of functions are to be replaced with alternate + versions that return a vector of strings rather than a table of + strings. This also allows deprecation for some related string + concatenation/extraction functions. The full list is: + + * split: use split_string instead. + + * split1: use split_string1 instead. + + * split_all: use split_string_all instead. + + * split_n: use split_string_n instead. + + * cat_string_array: see join_string_vec instead. + + * cat_string_array_n: see join_string_vec instead. + + * join_string_array: see join_string_vec instead. + + * sort_string_array: use sort instead instead. + + * find_ip_addresses: use extract_ip_addresses instead. + Bro 2.3 ======= From 7b2316262d74f061a2bca2c02348479f0f238830 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 21 Jan 2015 16:38:31 -0600 Subject: [PATCH 089/299] Update documentation (broken links, outdated tests). --- doc/script-reference/attributes.rst | 8 ++++++++ src/analyzer/protocol/mysql/events.bif | 8 ++++---- src/file_analysis/file_analysis.bif | 2 +- .../output | 4 ++-- .../output | 11 ++++------- .../output | 9 +++++---- .../output | 10 +++++----- .../include-doc_frameworks_file_analysis_02_bro.btest | 4 ++-- .../include-doc_httpmonitor_file_extraction_bro.btest | 11 ++++------- ...ripts_policy_frameworks_files_detect-MHR_bro.btest | 9 +++++---- ...pts_policy_frameworks_files_detect-MHR_bro@4.btest | 10 +++++----- 11 files changed, 45 insertions(+), 41 deletions(-) diff --git a/doc/script-reference/attributes.rst b/doc/script-reference/attributes.rst index 5680a034ff..ef6c6a54a1 100644 --- a/doc/script-reference/attributes.rst +++ b/doc/script-reference/attributes.rst @@ -49,6 +49,8 @@ The Bro scripting language supports the following attributes. +-----------------------------+-----------------------------------------------+ | :bro:attr:`&type_column` |Used by input framework for "port" type. | +-----------------------------+-----------------------------------------------+ +| :bro:attr:`&deprecated` |Marks an identifier as deprecated. | ++-----------------------------+-----------------------------------------------+ Here is a more detailed explanation of each attribute: @@ -230,3 +232,9 @@ Here is a more detailed explanation of each attribute: msg: string; }; +.. bro:attr:: &deprecated + + The associated identifier is marked as deprecated and will be + removed in a future version of Bro. Look in the NEWS file for more + explanation and/or instructions to migrate code that uses deprecated + functionality. diff --git a/src/analyzer/protocol/mysql/events.bif b/src/analyzer/protocol/mysql/events.bif index d7160c1ac6..bd81e8b8a4 100644 --- a/src/analyzer/protocol/mysql/events.bif +++ b/src/analyzer/protocol/mysql/events.bif @@ -9,7 +9,7 @@ ## ## arg: The argument for the command (empty string if not provided). ## -## .. bro:see:: mysql_error mysql_ok mysql_server_version mysql_handshake_response +## .. bro:see:: mysql_error mysql_ok mysql_server_version mysql_handshake event mysql_command_request%(c: connection, command: count, arg: string%); ## Generated for an unsuccessful MySQL response. @@ -23,7 +23,7 @@ event mysql_command_request%(c: connection, command: count, arg: string%); ## ## msg: Any extra details about the error (empty string if not provided). ## -## .. bro:see:: mysql_command_request mysql_ok mysql_server_version mysql_handshake_response +## .. bro:see:: mysql_command_request mysql_ok mysql_server_version mysql_handshake event mysql_error%(c: connection, code: count, msg: string%); ## Generated for a successful MySQL response. @@ -35,7 +35,7 @@ event mysql_error%(c: connection, code: count, msg: string%); ## ## affected_rows: The number of rows that were affected. ## -## .. bro:see:: mysql_command_request mysql_error mysql_server_version mysql_handshake_response +## .. bro:see:: mysql_command_request mysql_error mysql_server_version mysql_handshake event mysql_ok%(c: connection, affected_rows: count%); ## Generated for the initial server handshake packet, which includes the MySQL server version. @@ -47,7 +47,7 @@ event mysql_ok%(c: connection, affected_rows: count%); ## ## ver: The server version string. ## -## .. bro:see:: mysql_command_request mysql_error mysql_ok mysql_handshake_response +## .. bro:see:: mysql_command_request mysql_error mysql_ok mysql_handshake event mysql_server_version%(c: connection, ver: string%); ## Generated for a client handshake response packet, which includes the username the client is attempting diff --git a/src/file_analysis/file_analysis.bif b/src/file_analysis/file_analysis.bif index 4e4b4c6cdb..480d8c84d8 100644 --- a/src/file_analysis/file_analysis.bif +++ b/src/file_analysis/file_analysis.bif @@ -29,7 +29,7 @@ function Files::__disable_reassembly%(file_id: string%): bool return new Val(result, TYPE_BOOL); %} -## :bro:see:`Files::set_reassembly_buffer`. +## :bro:see:`Files::set_reassembly_buffer_size`. function Files::__set_reassembly_buffer%(file_id: string, max: count%): bool %{ bool result = file_mgr->SetReassemblyBuffer(file_id->CheckString(), max); diff --git a/testing/btest/Baseline/doc.sphinx.include-doc_frameworks_file_analysis_02_bro/output b/testing/btest/Baseline/doc.sphinx.include-doc_frameworks_file_analysis_02_bro/output index 3b93ee757c..5e86c8d685 100644 --- a/testing/btest/Baseline/doc.sphinx.include-doc_frameworks_file_analysis_02_bro/output +++ b/testing/btest/Baseline/doc.sphinx.include-doc_frameworks_file_analysis_02_bro/output @@ -2,10 +2,10 @@ file_analysis_02.bro -event file_new(f: fa_file) +event file_mime_type(f: fa_file, mime_type: string) { print "new file", f$id; - if ( f?$mime_type && f$mime_type == "text/plain" ) + if ( mime_type == "text/plain" ) Files::add_analyzer(f, Files::ANALYZER_MD5); } diff --git a/testing/btest/Baseline/doc.sphinx.include-doc_httpmonitor_file_extraction_bro/output b/testing/btest/Baseline/doc.sphinx.include-doc_httpmonitor_file_extraction_bro/output index acae92f44b..b193e4a530 100644 --- a/testing/btest/Baseline/doc.sphinx.include-doc_httpmonitor_file_extraction_bro/output +++ b/testing/btest/Baseline/doc.sphinx.include-doc_httpmonitor_file_extraction_bro/output @@ -11,18 +11,15 @@ global mime_to_ext: table[string] of string = { ["text/html"] = "html", }; -event file_new(f: fa_file) +event file_mime_type(f: fa_file, mime_type: string) { if ( f$source != "HTTP" ) return; - if ( ! f?$mime_type ) + if ( mime_type !in mime_to_ext ) return; - if ( f$mime_type !in mime_to_ext ) - return; - - local fname = fmt("%s-%s.%s", f$source, f$id, mime_to_ext[f$mime_type]); + local fname = fmt("%s-%s.%s", f$source, f$id, mime_to_ext[mime_type]); print fmt("Extracting file %s", fname); Files::add_analyzer(f, Files::ANALYZER_EXTRACT, [$extract_filename=fname]); - } + } \ No newline at end of file diff --git a/testing/btest/Baseline/doc.sphinx.include-scripts_policy_frameworks_files_detect-MHR_bro/output b/testing/btest/Baseline/doc.sphinx.include-scripts_policy_frameworks_files_detect-MHR_bro/output index bcf6ccd309..03ba9cb3cd 100644 --- a/testing/btest/Baseline/doc.sphinx.include-scripts_policy_frameworks_files_detect-MHR_bro/output +++ b/testing/btest/Baseline/doc.sphinx.include-scripts_policy_frameworks_files_detect-MHR_bro/output @@ -46,15 +46,15 @@ function do_mhr_lookup(hash: string, fi: Notice::FileInfo) when ( local MHR_result = lookup_hostname_txt(hash_domain) ) { # Data is returned as " " - local MHR_answer = split1(MHR_result, / /); + local MHR_answer = split_string1(MHR_result, / /); if ( |MHR_answer| == 2 ) { - local mhr_detect_rate = to_count(MHR_answer[2]); + local mhr_detect_rate = to_count(MHR_answer[1]); if ( mhr_detect_rate >= notice_threshold ) { - local mhr_first_detected = double_to_time(to_double(MHR_answer[1])); + local mhr_first_detected = double_to_time(to_double(MHR_answer[0])); local readable_first_detected = strftime("%Y-%m-%d %H:%M:%S", mhr_first_detected); local message = fmt("Malware Hash Registry Detection rate: %d%% Last seen: %s", mhr_detect_rate, readable_first_detected); local virustotal_url = fmt(match_sub_url, hash); @@ -70,6 +70,7 @@ function do_mhr_lookup(hash: string, fi: Notice::FileInfo) event file_hash(f: fa_file, kind: string, hash: string) { - if ( kind == "sha1" && f?$mime_type && match_file_types in f$mime_type ) + if ( kind == "sha1" && f?$info && f$info?$mime_type && + match_file_types in f$info$mime_type ) do_mhr_lookup(hash, Notice::create_file_info(f)); } diff --git a/testing/btest/Baseline/doc.sphinx.include-scripts_policy_frameworks_files_detect-MHR_bro@4/output b/testing/btest/Baseline/doc.sphinx.include-scripts_policy_frameworks_files_detect-MHR_bro@4/output index be9619fa1c..55950caf6b 100644 --- a/testing/btest/Baseline/doc.sphinx.include-scripts_policy_frameworks_files_detect-MHR_bro@4/output +++ b/testing/btest/Baseline/doc.sphinx.include-scripts_policy_frameworks_files_detect-MHR_bro@4/output @@ -9,15 +9,15 @@ function do_mhr_lookup(hash: string, fi: Notice::FileInfo) when ( local MHR_result = lookup_hostname_txt(hash_domain) ) { # Data is returned as " " - local MHR_answer = split1(MHR_result, / /); + local MHR_answer = split_string1(MHR_result, / /); if ( |MHR_answer| == 2 ) { - local mhr_detect_rate = to_count(MHR_answer[2]); + local mhr_detect_rate = to_count(MHR_answer[1]); if ( mhr_detect_rate >= notice_threshold ) { - local mhr_first_detected = double_to_time(to_double(MHR_answer[1])); + local mhr_first_detected = double_to_time(to_double(MHR_answer[0])); local readable_first_detected = strftime("%Y-%m-%d %H:%M:%S", mhr_first_detected); local message = fmt("Malware Hash Registry Detection rate: %d%% Last seen: %s", mhr_detect_rate, readable_first_detected); local virustotal_url = fmt(match_sub_url, hash); @@ -33,6 +33,6 @@ function do_mhr_lookup(hash: string, fi: Notice::FileInfo) event file_hash(f: fa_file, kind: string, hash: string) { - if ( kind == "sha1" && f?$mime_type && match_file_types in f$mime_type ) + if ( kind == "sha1" && f?$info && f$info?$mime_type && + match_file_types in f$info$mime_type ) do_mhr_lookup(hash, Notice::create_file_info(f)); - } diff --git a/testing/btest/doc/sphinx/include-doc_frameworks_file_analysis_02_bro.btest b/testing/btest/doc/sphinx/include-doc_frameworks_file_analysis_02_bro.btest index 3b93ee757c..5e86c8d685 100644 --- a/testing/btest/doc/sphinx/include-doc_frameworks_file_analysis_02_bro.btest +++ b/testing/btest/doc/sphinx/include-doc_frameworks_file_analysis_02_bro.btest @@ -2,10 +2,10 @@ file_analysis_02.bro -event file_new(f: fa_file) +event file_mime_type(f: fa_file, mime_type: string) { print "new file", f$id; - if ( f?$mime_type && f$mime_type == "text/plain" ) + if ( mime_type == "text/plain" ) Files::add_analyzer(f, Files::ANALYZER_MD5); } diff --git a/testing/btest/doc/sphinx/include-doc_httpmonitor_file_extraction_bro.btest b/testing/btest/doc/sphinx/include-doc_httpmonitor_file_extraction_bro.btest index acae92f44b..b193e4a530 100644 --- a/testing/btest/doc/sphinx/include-doc_httpmonitor_file_extraction_bro.btest +++ b/testing/btest/doc/sphinx/include-doc_httpmonitor_file_extraction_bro.btest @@ -11,18 +11,15 @@ global mime_to_ext: table[string] of string = { ["text/html"] = "html", }; -event file_new(f: fa_file) +event file_mime_type(f: fa_file, mime_type: string) { if ( f$source != "HTTP" ) return; - if ( ! f?$mime_type ) + if ( mime_type !in mime_to_ext ) return; - if ( f$mime_type !in mime_to_ext ) - return; - - local fname = fmt("%s-%s.%s", f$source, f$id, mime_to_ext[f$mime_type]); + local fname = fmt("%s-%s.%s", f$source, f$id, mime_to_ext[mime_type]); print fmt("Extracting file %s", fname); Files::add_analyzer(f, Files::ANALYZER_EXTRACT, [$extract_filename=fname]); - } + } \ No newline at end of file diff --git a/testing/btest/doc/sphinx/include-scripts_policy_frameworks_files_detect-MHR_bro.btest b/testing/btest/doc/sphinx/include-scripts_policy_frameworks_files_detect-MHR_bro.btest index bcf6ccd309..03ba9cb3cd 100644 --- a/testing/btest/doc/sphinx/include-scripts_policy_frameworks_files_detect-MHR_bro.btest +++ b/testing/btest/doc/sphinx/include-scripts_policy_frameworks_files_detect-MHR_bro.btest @@ -46,15 +46,15 @@ function do_mhr_lookup(hash: string, fi: Notice::FileInfo) when ( local MHR_result = lookup_hostname_txt(hash_domain) ) { # Data is returned as " " - local MHR_answer = split1(MHR_result, / /); + local MHR_answer = split_string1(MHR_result, / /); if ( |MHR_answer| == 2 ) { - local mhr_detect_rate = to_count(MHR_answer[2]); + local mhr_detect_rate = to_count(MHR_answer[1]); if ( mhr_detect_rate >= notice_threshold ) { - local mhr_first_detected = double_to_time(to_double(MHR_answer[1])); + local mhr_first_detected = double_to_time(to_double(MHR_answer[0])); local readable_first_detected = strftime("%Y-%m-%d %H:%M:%S", mhr_first_detected); local message = fmt("Malware Hash Registry Detection rate: %d%% Last seen: %s", mhr_detect_rate, readable_first_detected); local virustotal_url = fmt(match_sub_url, hash); @@ -70,6 +70,7 @@ function do_mhr_lookup(hash: string, fi: Notice::FileInfo) event file_hash(f: fa_file, kind: string, hash: string) { - if ( kind == "sha1" && f?$mime_type && match_file_types in f$mime_type ) + if ( kind == "sha1" && f?$info && f$info?$mime_type && + match_file_types in f$info$mime_type ) do_mhr_lookup(hash, Notice::create_file_info(f)); } diff --git a/testing/btest/doc/sphinx/include-scripts_policy_frameworks_files_detect-MHR_bro@4.btest b/testing/btest/doc/sphinx/include-scripts_policy_frameworks_files_detect-MHR_bro@4.btest index be9619fa1c..55950caf6b 100644 --- a/testing/btest/doc/sphinx/include-scripts_policy_frameworks_files_detect-MHR_bro@4.btest +++ b/testing/btest/doc/sphinx/include-scripts_policy_frameworks_files_detect-MHR_bro@4.btest @@ -9,15 +9,15 @@ function do_mhr_lookup(hash: string, fi: Notice::FileInfo) when ( local MHR_result = lookup_hostname_txt(hash_domain) ) { # Data is returned as " " - local MHR_answer = split1(MHR_result, / /); + local MHR_answer = split_string1(MHR_result, / /); if ( |MHR_answer| == 2 ) { - local mhr_detect_rate = to_count(MHR_answer[2]); + local mhr_detect_rate = to_count(MHR_answer[1]); if ( mhr_detect_rate >= notice_threshold ) { - local mhr_first_detected = double_to_time(to_double(MHR_answer[1])); + local mhr_first_detected = double_to_time(to_double(MHR_answer[0])); local readable_first_detected = strftime("%Y-%m-%d %H:%M:%S", mhr_first_detected); local message = fmt("Malware Hash Registry Detection rate: %d%% Last seen: %s", mhr_detect_rate, readable_first_detected); local virustotal_url = fmt(match_sub_url, hash); @@ -33,6 +33,6 @@ function do_mhr_lookup(hash: string, fi: Notice::FileInfo) event file_hash(f: fa_file, kind: string, hash: string) { - if ( kind == "sha1" && f?$mime_type && match_file_types in f$mime_type ) + if ( kind == "sha1" && f?$info && f$info?$mime_type && + match_file_types in f$info$mime_type ) do_mhr_lookup(hash, Notice::create_file_info(f)); - } From e62c711e095454b5b29f52a4e9f384e42f72fc30 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 21 Jan 2015 16:57:16 -0600 Subject: [PATCH 090/299] Fix typo. --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index d78d1b3fc4..e09af7f17f 100644 --- a/NEWS +++ b/NEWS @@ -78,7 +78,7 @@ Deprecated Functionality * join_string_array: see join_string_vec instead. - * sort_string_array: use sort instead instead. + * sort_string_array: use sort instead. * find_ip_addresses: use extract_ip_addresses instead. From 4d0a09a0379206f0cacd0c8a8053c0d1070ca62b Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Thu, 22 Jan 2015 07:25:27 -0800 Subject: [PATCH 091/299] Updating submodule(s). [nomail] --- aux/btest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aux/btest b/aux/btest index d67d89aaee..93d4989ed1 160000 --- a/aux/btest +++ b/aux/btest @@ -1 +1 @@ -Subproject commit d67d89aaee32ad5edb9068db55d1310c2f36970a +Subproject commit 93d4989ed1537e4d143cf09d44077159f869a4b2 From 5df71ddc911a87234371121146e8d382efde9192 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Thu, 22 Jan 2015 11:29:53 -0600 Subject: [PATCH 092/299] broker integration: add auto sending remote events i.e. ability to toggle whether all local dispatches of an event also generate a remote event message to peers. --- src/EventHandler.cc | 50 +++++++++++++++++++++++++++++++++- src/EventHandler.h | 19 ++++++++++++- src/comm/Manager.cc | 65 +++++++++++++++++++++++++++++++++++++++++++++ src/comm/Manager.h | 5 ++++ src/comm/comm.bif | 13 +++++++++ 5 files changed, 150 insertions(+), 2 deletions(-) diff --git a/src/EventHandler.cc b/src/EventHandler.cc index 0f25d63ba8..d623f43b66 100644 --- a/src/EventHandler.cc +++ b/src/EventHandler.cc @@ -5,6 +5,11 @@ #include "RemoteSerializer.h" #include "NetVar.h" +#ifdef ENABLE_BROKER +#include "comm/Manager.h" +#include "comm/Data.h" +#endif + EventHandler::EventHandler(const char* arg_name) { name = copy_string(arg_name); @@ -26,7 +31,12 @@ EventHandler::operator bool() const { return enabled && ((local && local->HasBodies()) || receivers.length() - || generate_always); + || generate_always +#ifdef ENABLE_BROKER + || ! auto_remote_send.empty() + // TODO: and require a subscriber interested in a topic or unsolicited flags? +#endif + ); } FuncType* EventHandler::FType() @@ -73,6 +83,44 @@ void EventHandler::Call(val_list* vl, bool no_remote) SerialInfo info(remote_serializer); remote_serializer->SendCall(&info, receivers[i], name, vl); } + +#ifdef ENABLE_BROKER + + if ( ! auto_remote_send.empty() ) + { + // TODO: also short-circuit based on interested subscribers/flags? + broker::message msg; + msg.reserve(vl->length() + 1); + msg.emplace_back(Name()); + bool valid_args = true; + + for ( auto i = 0u; i < vl->length(); ++i ) + { + auto opt_data = comm::val_to_data((*vl)[i]); + + if ( opt_data ) + msg.emplace_back(move(*opt_data)); + else + { + valid_args = false; + auto_remote_send.clear(); + reporter->Error("failed auto-remote event '%s', disabled", + Name()); + break; + } + } + + if ( valid_args ) + for ( auto it = auto_remote_send.begin(); + it != auto_remote_send.end(); ++it ) + { + if ( std::next(it) == auto_remote_send.end() ) + comm_mgr->Event(it->first, move(msg), it->second); + else + comm_mgr->Event(it->first, msg, it->second); + } + } +#endif } if ( local ) diff --git a/src/EventHandler.h b/src/EventHandler.h index 55ac33cffd..7729e2af27 100644 --- a/src/EventHandler.h +++ b/src/EventHandler.h @@ -4,7 +4,8 @@ #define EVENTHANDLER #include - +#include +#include #include "List.h" #include "BroList.h" @@ -28,6 +29,18 @@ public: void AddRemoteHandler(SourceID peer); void RemoveRemoteHandler(SourceID peer); +#ifdef ENABLE_BROKER + void AutoRemote(std::string topic, int flags) + { + auto_remote_send[std::move(topic)] = flags; + } + + void AutoRemoteStop(const std::string& topic) + { + auto_remote_send.erase(topic); + } +#endif + void Call(val_list* vl, bool no_remote = false); // Returns true if there is at least one local or remote handler. @@ -67,6 +80,10 @@ private: declare(List, SourceID); typedef List(SourceID) receiver_list; receiver_list receivers; + +#ifdef ENABLE_BROKER + std::map auto_remote_send; // topic -> flags +#endif }; // Encapsulates a ptr to an event handler to overload the boolean operator. diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index b4f118706a..d803d64ae7 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -107,6 +107,12 @@ bool comm::Manager::Print(string topic, string msg, const Val* flags) return true; } +bool comm::Manager::Event(std::string topic, broker::message msg, int flags) + { + endpoint->send(move(topic), move(msg), flags); + return true; + } + bool comm::Manager::Event(std::string topic, const RecordVal* args, const Val* flags) { @@ -130,6 +136,65 @@ bool comm::Manager::Event(std::string topic, const RecordVal* args, return true; } +bool comm::Manager::AutoEvent(string topic, const Val* event, const Val* flags) + { + if ( event->Type()->Tag() != TYPE_FUNC ) + { + reporter->Error("Comm::auto_event must operate on an event"); + return false; + } + + auto event_val = event->AsFunc(); + + if ( event_val->Flavor() != FUNC_FLAVOR_EVENT ) + { + reporter->Error("Comm::auto_event must operate on an event"); + return false; + } + + auto handler = event_registry->Lookup(event_val->Name()); + + if ( ! handler ) + { + reporter->Error("Comm::auto_event failed to lookup event '%s'", + event_val->Name()); + return false; + } + + handler->AutoRemote(move(topic), get_flags(flags)); + return true; + } + +bool comm::Manager::AutoEventStop(const string& topic, const Val* event) + { + if ( event->Type()->Tag() != TYPE_FUNC ) + { + reporter->Error("Comm::auto_event_stop must operate on an event"); + return false; + } + + auto event_val = event->AsFunc(); + + if ( event_val->Flavor() != FUNC_FLAVOR_EVENT ) + { + reporter->Error("Comm::auto_event_stop must operate on an event"); + return false; + } + + auto handler = event_registry->Lookup(event_val->Name()); + + if ( ! handler ) + { + reporter->Error("Comm::auto_event_stop failed to lookup event '%s'", + event_val->Name()); + return false; + } + + + handler->AutoRemoteStop(topic); + return true; + } + RecordVal* comm::Manager::MakeEventArgs(const val_list* args) { auto rval = new RecordVal(BifType::Record::Comm::EventArgs); diff --git a/src/comm/Manager.h b/src/comm/Manager.h index 020f78a03b..70bec51ded 100644 --- a/src/comm/Manager.h +++ b/src/comm/Manager.h @@ -31,8 +31,13 @@ public: bool Print(std::string topic, std::string msg, const Val* flags); + bool Event(std::string topic, broker::message msg, int flags); bool Event(std::string topic, const RecordVal* args, const Val* flags); + bool AutoEvent(std::string topic, const Val* event, const Val* flags); + + bool AutoEventStop(const std::string& topic, const Val* event); + RecordVal* MakeEventArgs(const val_list* args); bool SubscribeToPrints(std::string topic_prefix); diff --git a/src/comm/comm.bif b/src/comm/comm.bif index fe405222cc..c185120126 100644 --- a/src/comm/comm.bif +++ b/src/comm/comm.bif @@ -100,6 +100,19 @@ function Comm::event%(topic: string, args: Comm::EventArgs, return new Val(rval, TYPE_BOOL); %} +function Comm::auto_event%(topic: string, ev: any, + flags: SendFlags &default = SendFlags()%): bool + %{ + auto rval = comm_mgr->AutoEvent(topic->CheckString(), ev, flags); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::auto_event_stop%(topic: string, ev: any%): bool + %{ + auto rval = comm_mgr->AutoEventStop(topic->CheckString(), ev); + return new Val(rval, TYPE_BOOL); + %} + function Comm::subscribe_to_events%(topic_prefix: string%): bool %{ auto rval = comm_mgr->SubscribeToEvents(topic_prefix->CheckString()); From d6d5276d769e07a442fd27ed1b075b33972219b5 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Fri, 23 Jan 2015 10:43:28 -0600 Subject: [PATCH 093/299] Update binpac: Fix potential out-of-bounds memory reads in generated code. Field lengths derived from other data in the input could potentially lead to reading from outside the bounds of the input buffer. Reported by John Villamil and Chris Rohlf - Yahoo Paranoids --- aux/binpac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aux/binpac b/aux/binpac index 77a86591dc..8d56b507b8 160000 --- a/aux/binpac +++ b/aux/binpac @@ -1 +1 @@ -Subproject commit 77a86591dcf89d7252d3676d3f1199d6c927d073 +Subproject commit 8d56b507b8b804fa83f6637f3b1f198e696cd603 From 6cedd67c381ff22fde653adf02ee31caf66c81a0 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Fri, 23 Jan 2015 10:49:15 -0600 Subject: [PATCH 094/299] DNP3: fix reachable assertion and buffer over-read/overflow. A DNP3 packet using a link layer header that specifies a zero length can trigger an assertion failure if assertions are enabled. Assertions are enabled unless Bro is compiled with the NDEBUG preprocessor macro defined. The default configuration of Bro will define this macro and so disables assertions, but using the --enable-debug option in the configure script will enable assertions. When assertions are disabled, or also for certain length values, the DNP3 parser may attempt to pass a negative value as the third argument to memcpy (number of bytes to copy) and result in a buffer over-read or overflow. Reported by Travis Emmert. --- CHANGES | 11 ++++++ VERSION | 2 +- src/analyzer/protocol/dnp3/DNP3.cc | 58 ++++++++++++++++++++++++++---- src/analyzer/protocol/dnp3/DNP3.h | 16 ++++++++- 4 files changed, 78 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 4087615fe2..a50dc265cc 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,15 @@ +2.3-396 | 2015-01-23 10:49:15 -0600 + + * DNP3: fix reachable assertion and buffer over-read/overflow. + CVE number pending. (Travis Emmert, Jon Siwek) + + * Update binpac: Fix potential out-of-bounds memory reads in generated + code. CVE-2014-9586. (John Villamil and Chris Rohlf - Yahoo + Paranoids, Jon Siwek) + + * Fixing (harmless) Coverity warning. (Robin Sommer) + 2.3-392 | 2015-01-15 09:44:15 -0800 * Small changes to EC curve names in a newer draft. (Johanna Amann) diff --git a/VERSION b/VERSION index 1cb805162d..081c98cc51 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3-392 +2.3-396 diff --git a/src/analyzer/protocol/dnp3/DNP3.cc b/src/analyzer/protocol/dnp3/DNP3.cc index 135100eb6b..b04dbf64e0 100644 --- a/src/analyzer/protocol/dnp3/DNP3.cc +++ b/src/analyzer/protocol/dnp3/DNP3.cc @@ -138,9 +138,14 @@ bool DNP3_Base::ProcessData(int len, const u_char* data, bool orig) if ( endp->in_hdr ) { // We're parsing the DNP3 header and link layer, get that in full. - if ( ! AddToBuffer(endp, PSEUDO_APP_LAYER_INDEX, &data, &len) ) + int res = AddToBuffer(endp, PSEUDO_APP_LAYER_INDEX, &data, &len); + + if ( res == 0 ) return true; + if ( res < 0 ) + return false; + // The first two bytes must always be 0x0564. if( endp->buffer[0] != 0x05 || endp->buffer[1] != 0x64 ) { @@ -186,7 +191,11 @@ bool DNP3_Base::ProcessData(int len, const u_char* data, bool orig) if ( ! endp->in_hdr ) { - assert(endp->pkt_length); + if ( endp->pkt_length <= 0 ) + { + analyzer->Weird("dnp3_negative_or_zero_length_link_layer"); + return false; + } // We're parsing the DNP3 application layer, get that // in full now as well. We calculate the number of @@ -197,9 +206,14 @@ bool DNP3_Base::ProcessData(int len, const u_char* data, bool orig) int n = PSEUDO_APP_LAYER_INDEX + (endp->pkt_length - 5) + ((endp->pkt_length - 5) / 16) * 2 + 2 * ( ((endp->pkt_length - 5) % 16 == 0) ? 0 : 1) - 1 ; - if ( ! AddToBuffer(endp, n, &data, &len) ) + int res = AddToBuffer(endp, n, &data, &len); + + if ( res == 0 ) return true; + if ( res < 0 ) + return false; + // Parse the the application layer data. if ( ! ParseAppLayer(endp) ) return false; @@ -213,19 +227,42 @@ bool DNP3_Base::ProcessData(int len, const u_char* data, bool orig) return true; } -bool DNP3_Base::AddToBuffer(Endpoint* endp, int target_len, const u_char** data, int* len) +int DNP3_Base::AddToBuffer(Endpoint* endp, int target_len, const u_char** data, int* len) { if ( ! target_len ) - return true; + return 1; + + if ( *len < 0 ) + { + reporter->AnalyzerError(analyzer, "dnp3 negative input length: %d", *len); + return -1; + } + + if ( target_len < endp->buffer_len ) + { + reporter->AnalyzerError(analyzer, "dnp3 invalid target length: %d - %d", + target_len, endp->buffer_len); + return -1; + } int to_copy = min(*len, target_len - endp->buffer_len); + if ( endp->buffer_len + to_copy > MAX_BUFFER_SIZE ) + { + reporter->AnalyzerError(analyzer, "dnp3 buffer length exceeded: %d + %d", + endp->buffer_len, to_copy); + return -1; + } + memcpy(endp->buffer + endp->buffer_len, *data, to_copy); *data += to_copy; *len -= to_copy; endp->buffer_len += to_copy; - return endp->buffer_len == target_len; + if ( endp->buffer_len == target_len ) + return 1; + + return 0; } bool DNP3_Base::ParseAppLayer(Endpoint* endp) @@ -256,8 +293,15 @@ bool DNP3_Base::ParseAppLayer(Endpoint* endp) if ( ! CheckCRC(n, data, data + n, "app_chunk") ) return false; + if ( data + n >= endp->buffer + endp->buffer_len ) + { + reporter->AnalyzerError(analyzer, + "dnp3 app layer parsing overflow %d - %d", + endp->buffer_len, n); + return false; + } + // Pass on to BinPAC. - assert(data + n < endp->buffer + endp->buffer_len); flow->flow_buffer()->BufferData(data + transport, data + n); transport = 0; diff --git a/src/analyzer/protocol/dnp3/DNP3.h b/src/analyzer/protocol/dnp3/DNP3.h index 12c3624cd5..aa4ef78479 100644 --- a/src/analyzer/protocol/dnp3/DNP3.h +++ b/src/analyzer/protocol/dnp3/DNP3.h @@ -31,7 +31,21 @@ protected: bool ProcessData(int len, const u_char* data, bool orig); void ClearEndpointState(bool orig); - bool AddToBuffer(Endpoint* endp, int target_len, const u_char** data, int* len); + + /** + * Buffers packet data until it reaches a specified length. + * @param endp an endpoint speaking DNP3 to which data will be buffered. + * @param target_len the required length of the buffer + * @param data source buffer to copy bytes from. Will be incremented + * by the number of bytes copied by this function. + * @param len the number of bytes available in \a data. Will be decremented + * by the number of bytes copied by this function. + * @return -1 if invalid input parameters were supplied, 0 if the endpoint's + * buffer is not yet \a target_len bytes in size, or 1 the buffer is the + * required size. + */ + int AddToBuffer(Endpoint* endp, int target_len, const u_char** data, int* len); + bool ParseAppLayer(Endpoint* endp); bool CheckCRC(int len, const u_char* data, const u_char* crc16, const char* where); unsigned int CalcCRC(int len, const u_char* data); From 2b598e3d5a582b61ab43d43d52134a95e7e45336 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Mon, 26 Jan 2015 14:24:42 -0600 Subject: [PATCH 095/299] broker integration: add remote logging It now works a bit differently than before: whether to send a remote log write is now a property of the logging stream, not the logging filter and it's now up the the receiver side filters to instantiate the desired writer. i.e. the sender now has no say in what the receiver should use as the log writer backend. Under the new style of remote logging, the "Log::enable_remote_logging" option is repurposed to set the default behavior for new logging streams. There's also "Comm::{enable,disable}_remote_logging()" to explicitly set the desired behavior for a given logging stream. To receive remote logs, one calls "Comm::subscribe_to_logs()", where senders implicitly use topics of the form "bro/log/". --- aux/broker | 2 +- src/comm/Data.cc | 2 +- src/comm/Manager.cc | 137 +++++++++++++++++++++++++++++++++++++++-- src/comm/Manager.h | 21 +++++-- src/comm/comm.bif | 34 ++++++++++ src/logging/Manager.cc | 66 ++++++++++++++++++++ src/logging/Manager.h | 10 +++ 7 files changed, 260 insertions(+), 12 deletions(-) diff --git a/aux/broker b/aux/broker index 1e8d675790..425bab3bf4 160000 --- a/aux/broker +++ b/aux/broker @@ -1 +1 @@ -Subproject commit 1e8d6757909750524c15f8eaf3c297243bc55425 +Subproject commit 425bab3bf420898d8dbd14280f94aee9d420f617 diff --git a/src/comm/Data.cc b/src/comm/Data.cc index 58d5b30085..b279b97529 100644 --- a/src/comm/Data.cc +++ b/src/comm/Data.cc @@ -332,7 +332,7 @@ struct val_converter { rval->Assign(i, item_val); } - return nullptr; + return rval; } }; diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index d803d64ae7..ffe68970a8 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -7,9 +7,16 @@ #include "Var.h" #include "Reporter.h" #include "comm/comm.bif.h" +#include "logging/Manager.h" using namespace std; +VectorType* comm::Manager::vector_of_data_type; +EnumType* comm::Manager::log_id_type; +int comm::Manager::send_flags_self_idx; +int comm::Manager::send_flags_peers_idx; +int comm::Manager::send_flags_unsolicited_idx; + bool comm::Manager::InitPreScript() { return true; @@ -33,6 +40,8 @@ bool comm::Manager::InitPostScript() send_flags_peers_idx = require_field(send_flags_type, "peers"); send_flags_unsolicited_idx = require_field(send_flags_type, "unsolicited"); + log_id_type = internal_type("Log::ID")->AsEnumType(); + comm::opaque_of_data_type = new OpaqueType("Comm::Data"); vector_of_data_type = new VectorType(internal_type("Comm::Data")->Ref()); @@ -103,7 +112,7 @@ bool comm::Manager::Disconnect(const string& addr, uint16_t port) bool comm::Manager::Print(string topic, string msg, const Val* flags) { - endpoint->send(move(topic), broker::message{move(msg)}, get_flags(flags)); + endpoint->send(move(topic), broker::message{move(msg)}, GetFlags(flags)); return true; } @@ -113,6 +122,34 @@ bool comm::Manager::Event(std::string topic, broker::message msg, int flags) return true; } +bool comm::Manager::Log(const EnumVal* stream, const RecordVal* columns, + int flags) + { + auto stream_name = stream->Type()->AsEnumType()->Lookup(stream->AsEnum()); + + if ( ! stream_name ) + { + reporter->Error("Failed to remotely log: stream %d doesn't have name", + stream->AsEnum()); + return false; + } + + auto opt_column_data = val_to_data(columns); + + if ( ! opt_column_data ) + { + reporter->Error("Failed to remotely log stream %s: unsupported types", + stream_name); + return false; + } + + broker::message msg{broker::enum_value{stream_name}, + move(*opt_column_data)}; + std::string topic = std::string("bro/log/") + stream_name; + endpoint->send(move(topic), move(msg), flags); + return true; + } + bool comm::Manager::Event(std::string topic, const RecordVal* args, const Val* flags) { @@ -132,7 +169,7 @@ bool comm::Manager::Event(std::string topic, const RecordVal* args, msg.emplace_back(data_val->data); } - endpoint->send(move(topic), move(msg), get_flags(flags)); + endpoint->send(move(topic), move(msg), GetFlags(flags)); return true; } @@ -161,7 +198,7 @@ bool comm::Manager::AutoEvent(string topic, const Val* event, const Val* flags) return false; } - handler->AutoRemote(move(topic), get_flags(flags)); + handler->AutoRemote(move(topic), GetFlags(flags)); return true; } @@ -294,7 +331,23 @@ bool comm::Manager::UnsubscribeToEvents(const string& topic_prefix) return event_subscriptions.erase(topic_prefix); } -int comm::Manager::get_flags(const Val* flags) +bool comm::Manager::SubscribeToLogs(string topic_prefix) + { + auto& q = log_subscriptions[topic_prefix]; + + if ( q ) + return false; + + q = broker::message_queue(move(topic_prefix), *endpoint); + return true; + } + +bool comm::Manager::UnsubscribeToLogs(const string& topic_prefix) + { + return log_subscriptions.erase(topic_prefix); + } + +int comm::Manager::GetFlags(const Val* flags) { auto r = flags->AsRecordVal(); int rval = 0; @@ -327,6 +380,9 @@ void comm::Manager::GetFds(iosource::FD_Set* read, iosource::FD_Set* write, for ( const auto& ps : event_subscriptions ) read->Insert(ps.second.fd()); + + for ( const auto& ps : log_subscriptions ) + read->Insert(ps.second.fd()); } double comm::Manager::NextTimestamp(double* local_network_time) @@ -493,5 +549,78 @@ void comm::Manager::Process() } } + struct unref_guard { + unref_guard(Val* v) : val(v) {} + ~unref_guard() { Unref(val); } + Val* val; + }; + + for ( const auto& ls : log_subscriptions ) + { + auto log_messages = ls.second.want_pop(); + + if ( log_messages.empty() ) + continue; + + idle = false; + + for ( auto& lm : log_messages ) + { + if ( lm.size() != 2 ) + { + reporter->Warning("got bad remote log size: %zd (expect 2)", + lm.size()); + continue; + } + + if ( ! broker::get(lm[0]) ) + { + reporter->Warning("got remote log w/o stream id: %d", + static_cast(broker::which(lm[0]))); + continue; + } + + if ( ! broker::get(lm[1]) ) + { + reporter->Warning("got remote log w/o columns: %d", + static_cast(broker::which(lm[1]))); + continue; + } + + auto stream_id = data_to_val(move(lm[0]), log_id_type); + + if ( ! stream_id ) + { + reporter->Warning("failed to unpack remote log stream id"); + continue; + } + + unref_guard stream_id_unreffer{stream_id}; + auto columns_type = log_mgr->StreamColumns(stream_id->AsEnumVal()); + + if ( ! columns_type ) + { + reporter->Warning("got remote log for unknown stream: %s", + stream_id->Type()->AsEnumType()->Lookup( + stream_id->AsEnum())); + continue; + } + + auto columns = data_to_val(move(lm[1]), columns_type); + + if ( ! columns ) + { + reporter->Warning("failed to unpack remote log stream columns" + " for stream: %s", + stream_id->Type()->AsEnumType()->Lookup( + stream_id->AsEnum())); + continue; + } + + log_mgr->Write(stream_id->AsEnumVal(), columns->AsRecordVal()); + Unref(columns); + } + } + SetIdle(idle); } diff --git a/src/comm/Manager.h b/src/comm/Manager.h index 70bec51ded..3c1e80827b 100644 --- a/src/comm/Manager.h +++ b/src/comm/Manager.h @@ -8,6 +8,7 @@ #include #include "Reporter.h" #include "iosource/IOSource.h" +#include "Val.h" namespace comm { @@ -34,6 +35,8 @@ public: bool Event(std::string topic, broker::message msg, int flags); bool Event(std::string topic, const RecordVal* args, const Val* flags); + bool Log(const EnumVal* stream_id, const RecordVal* columns, int flags); + bool AutoEvent(std::string topic, const Val* event, const Val* flags); bool AutoEventStop(const std::string& topic, const Val* event); @@ -48,9 +51,13 @@ public: bool UnsubscribeToEvents(const std::string& topic_prefix); -private: + bool SubscribeToLogs(std::string topic_prefix); - int get_flags(const Val* flags); + bool UnsubscribeToLogs(const std::string& topic_prefix); + + static int GetFlags(const Val* flags); + +private: // IOSource interface overrides: void GetFds(iosource::FD_Set* read, iosource::FD_Set* write, @@ -67,12 +74,14 @@ private: std::map, broker::peering> peers; std::map print_subscriptions; std::map event_subscriptions; + std::map log_subscriptions; - int send_flags_self_idx; - int send_flags_peers_idx; - int send_flags_unsolicited_idx; + static VectorType* vector_of_data_type; + static EnumType* log_id_type; + static int send_flags_self_idx; + static int send_flags_peers_idx; + static int send_flags_unsolicited_idx; - VectorType* vector_of_data_type; }; } // namespace comm diff --git a/src/comm/comm.bif b/src/comm/comm.bif index c185120126..e1c2bc533f 100644 --- a/src/comm/comm.bif +++ b/src/comm/comm.bif @@ -2,6 +2,7 @@ %%{ #include "comm/Manager.h" #include "comm/Data.h" +#include "logging/Manager.h" %%} module Comm; @@ -124,3 +125,36 @@ function Comm::unsubscribe_to_events%(topic_prefix: string%): bool auto rval = comm_mgr->UnsubscribeToEvents(topic_prefix->CheckString()); return new Val(rval, TYPE_BOOL); %} + +function +Comm::enable_remote_logs%(id: Log::ID, + flags: SendFlags &default = SendFlags()%): bool + %{ + auto rval = log_mgr->EnableRemoteLogs(id->AsEnumVal(), + comm::Manager::GetFlags(flags)); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::disable_remote_logs%(id: Log::ID%): bool + %{ + auto rval = log_mgr->DisableRemoteLogs(id->AsEnumVal()); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::remote_logs_enabled%(id: Log::ID%): bool + %{ + auto rval = log_mgr->RemoteLogsAreEnabled(id->AsEnumVal()); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::subscribe_to_logs%(topic_prefix: string%): bool + %{ + auto rval = comm_mgr->SubscribeToLogs(topic_prefix->CheckString()); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::unsubscribe_to_logs%(topic_prefix: string%): bool + %{ + auto rval = comm_mgr->UnsubscribeToLogs(topic_prefix->CheckString()); + return new Val(rval, TYPE_BOOL); + %} diff --git a/src/logging/Manager.cc b/src/logging/Manager.cc index 1fe5db3b26..d6d7fbb908 100644 --- a/src/logging/Manager.cc +++ b/src/logging/Manager.cc @@ -16,6 +16,10 @@ #include "WriterBackend.h" #include "logging.bif.h" +#ifdef ENABLE_BROKER +#include "comm/Manager.h" +#endif + using namespace logging; struct Manager::Filter { @@ -69,6 +73,11 @@ struct Manager::Stream { WriterMap writers; // Writers indexed by id/path pair. +#ifdef ENABLE_BROKER + bool enable_remote; + int remote_flags; +#endif + ~Stream(); }; @@ -287,6 +296,11 @@ bool Manager::CreateStream(EnumVal* id, RecordVal* sval) streams[idx]->event = event ? event_registry->Lookup(event->Name()) : 0; streams[idx]->columns = columns->Ref()->AsRecordType(); +#ifdef ENABLE_BROKER + streams[idx]->enable_remote = internal_val("Log::enable_remote_logging")->AsBool(); + streams[idx]->remote_flags = broker::PEERS; +#endif + DBG_LOG(DBG_LOGGING, "Created new logging stream '%s', raising event %s", streams[idx]->name.c_str(), event ? streams[idx]->event->Name() : ""); @@ -828,6 +842,11 @@ bool Manager::Write(EnumVal* id, RecordVal* columns) #endif } +#ifdef ENABLE_BROKER + if ( stream->enable_remote ) + comm_mgr->Log(id, columns, stream->remote_flags); +#endif + Unref(columns); if ( error ) @@ -1206,6 +1225,53 @@ void Manager::Terminate() } } +#ifdef ENABLE_BROKER + +bool Manager::EnableRemoteLogs(EnumVal* stream_id, int flags) + { + auto stream = FindStream(stream_id); + + if ( ! stream ) + return false; + + stream->enable_remote = true; + stream->remote_flags = flags; + return true; + } + +bool Manager::DisableRemoteLogs(EnumVal* stream_id) + { + auto stream = FindStream(stream_id); + + if ( ! stream ) + return false; + + stream->enable_remote = false; + return true; + } + +bool Manager::RemoteLogsAreEnabled(EnumVal* stream_id) + { + auto stream = FindStream(stream_id); + + if ( ! stream ) + return false; + + return stream->enable_remote; + } + +RecordType* Manager::StreamColumns(EnumVal* stream_id) + { + auto stream = FindStream(stream_id); + + if ( ! stream ) + return nullptr; + + return stream->columns; + } + +#endif + // Timer which on dispatching rotates the filter. class RotationTimer : public Timer { public: diff --git a/src/logging/Manager.h b/src/logging/Manager.h index b8264927a3..8130a1ddd4 100644 --- a/src/logging/Manager.h +++ b/src/logging/Manager.h @@ -157,6 +157,16 @@ public: */ void Terminate(); +#ifdef ENABLE_BROKER + bool EnableRemoteLogs(EnumVal* stream_id, int flags); + + bool DisableRemoteLogs(EnumVal* stream_id); + + bool RemoteLogsAreEnabled(EnumVal* stream_id); + + RecordType* StreamColumns(EnumVal* stream_id); +#endif + protected: friend class WriterFrontend; friend class RotationFinishedMessage; From 55275436016f96e0fd77ffd1e921fd7a96fe1e0d Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Mon, 26 Jan 2015 16:53:13 -0600 Subject: [PATCH 096/299] broker integration: add unit tests for remote log/print/event --- aux/broker | 2 +- src/comm/Manager.cc | 4 +- src/comm/Manager.h | 3 +- src/comm/comm.bif | 6 +- src/main.cc | 2 +- .../Baseline/comm.remote_event/recv.recv.out | 6 ++ .../Baseline/comm.remote_event/send.send.out | 13 +++ .../Baseline/comm.remote_log/recv.recv.out | 6 ++ .../Baseline/comm.remote_log/recv.test.log | 15 +++ .../Baseline/comm.remote_log/send.send.out | 1 + .../Baseline/comm.remote_log/send.test.log | 15 +++ .../Baseline/comm.remote_print/recv.recv.out | 6 ++ .../Baseline/comm.remote_print/send.send.out | 7 ++ testing/btest/btest.cfg | 2 +- testing/btest/comm/remote_event.test | 100 ++++++++++++++++++ testing/btest/comm/remote_log.test | 87 +++++++++++++++ testing/btest/comm/remote_print.test | 66 ++++++++++++ 17 files changed, 333 insertions(+), 8 deletions(-) create mode 100644 testing/btest/Baseline/comm.remote_event/recv.recv.out create mode 100644 testing/btest/Baseline/comm.remote_event/send.send.out create mode 100644 testing/btest/Baseline/comm.remote_log/recv.recv.out create mode 100644 testing/btest/Baseline/comm.remote_log/recv.test.log create mode 100644 testing/btest/Baseline/comm.remote_log/send.send.out create mode 100644 testing/btest/Baseline/comm.remote_log/send.test.log create mode 100644 testing/btest/Baseline/comm.remote_print/recv.recv.out create mode 100644 testing/btest/Baseline/comm.remote_print/send.send.out create mode 100644 testing/btest/comm/remote_event.test create mode 100644 testing/btest/comm/remote_log.test create mode 100644 testing/btest/comm/remote_print.test diff --git a/aux/broker b/aux/broker index 425bab3bf4..ebc66f484a 160000 --- a/aux/broker +++ b/aux/broker @@ -1 +1 @@ -Subproject commit 425bab3bf420898d8dbd14280f94aee9d420f617 +Subproject commit ebc66f484af27a32dc5d91b1c985638847e35cf6 diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index ffe68970a8..bc0bc3f8a8 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -72,9 +72,9 @@ bool comm::Manager::InitPostScript() return true; } -bool comm::Manager::Listen(uint16_t port, const char* addr) +bool comm::Manager::Listen(uint16_t port, const char* addr, bool reuse_addr) { - auto rval = endpoint->listen(port, addr); + auto rval = endpoint->listen(port, addr, reuse_addr); if ( ! rval ) { diff --git a/src/comm/Manager.h b/src/comm/Manager.h index 3c1e80827b..5e3ec350b8 100644 --- a/src/comm/Manager.h +++ b/src/comm/Manager.h @@ -23,7 +23,8 @@ public: bool InitPostScript(); - bool Listen(uint16_t port, const char* addr = nullptr); + bool Listen(uint16_t port, const char* addr = nullptr, + bool reuse_addr = true); bool Connect(std::string addr, uint16_t port, std::chrono::duration retry_interval); diff --git a/src/comm/comm.bif b/src/comm/comm.bif index e1c2bc533f..ebe206d266 100644 --- a/src/comm/comm.bif +++ b/src/comm/comm.bif @@ -28,7 +28,8 @@ event Comm::remote_connection_broken%(peer_address: string, event Comm::remote_connection_incompatible%(peer_address: string, peer_port: port%); -function Comm::listen%(p: port, a: string &default = ""%): bool +function Comm::listen%(p: port, a: string &default = "", + reuse: bool &default = T%): bool %{ if ( ! p->IsTCP() ) { @@ -36,7 +37,8 @@ function Comm::listen%(p: port, a: string &default = ""%): bool return new Val(false, TYPE_BOOL); } - auto rval = comm_mgr->Listen(p->Port(), a->Len() ? a->CheckString() : 0); + auto rval = comm_mgr->Listen(p->Port(), a->Len() ? a->CheckString() : 0, + reuse); return new Val(rval, TYPE_BOOL); %} diff --git a/src/main.cc b/src/main.cc index a7099cb90b..5385ca7993 100644 --- a/src/main.cc +++ b/src/main.cc @@ -944,7 +944,7 @@ int main(int argc, char** argv) #ifdef ENABLE_BROKER comm_mgr->InitPostScript(); - iosource_mgr->Register(comm_mgr); + iosource_mgr->Register(comm_mgr, true); #endif #ifdef USE_PERFTOOLS_DEBUG diff --git a/testing/btest/Baseline/comm.remote_event/recv.recv.out b/testing/btest/Baseline/comm.remote_event/recv.recv.out new file mode 100644 index 0000000000..7dab0284ea --- /dev/null +++ b/testing/btest/Baseline/comm.remote_event/recv.recv.out @@ -0,0 +1,6 @@ +got event msg, ping, 0 +got event msg, ping, 1 +got event msg, ping, 2 +got event msg, ping, 3 +got event msg, ping, 4 +got event msg, ping, 5 diff --git a/testing/btest/Baseline/comm.remote_event/send.send.out b/testing/btest/Baseline/comm.remote_event/send.send.out new file mode 100644 index 0000000000..ef1f7bc7e1 --- /dev/null +++ b/testing/btest/Baseline/comm.remote_event/send.send.out @@ -0,0 +1,13 @@ +Comm::remote_connection_established, 127.0.0.1, 9999/tcp +got event msg, pong, 0 +got auto event msg, ping, 0 +got event msg, pong, 1 +got auto event msg, ping, 1 +got event msg, pong, 2 +got auto event msg, ping, 2 +got event msg, pong, 3 +got auto event msg, ping, 3 +got event msg, pong, 4 +got auto event msg, ping, 4 +got event msg, pong, 5 +got auto event msg, ping, 5 diff --git a/testing/btest/Baseline/comm.remote_log/recv.recv.out b/testing/btest/Baseline/comm.remote_log/recv.recv.out new file mode 100644 index 0000000000..3e0957442d --- /dev/null +++ b/testing/btest/Baseline/comm.remote_log/recv.recv.out @@ -0,0 +1,6 @@ +wrote log, [msg=ping, num=0] +wrote log, [msg=ping, num=1] +wrote log, [msg=ping, num=2] +wrote log, [msg=ping, num=3] +wrote log, [msg=ping, num=4] +wrote log, [msg=ping, num=5] diff --git a/testing/btest/Baseline/comm.remote_log/recv.test.log b/testing/btest/Baseline/comm.remote_log/recv.test.log new file mode 100644 index 0000000000..0d6dae756c --- /dev/null +++ b/testing/btest/Baseline/comm.remote_log/recv.test.log @@ -0,0 +1,15 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path test +#open 2015-01-26-22-47-11 +#fields msg num +#types string count +ping 0 +ping 1 +ping 2 +ping 3 +ping 4 +ping 5 +#close 2015-01-26-22-47-11 diff --git a/testing/btest/Baseline/comm.remote_log/send.send.out b/testing/btest/Baseline/comm.remote_log/send.send.out new file mode 100644 index 0000000000..0968e6beb9 --- /dev/null +++ b/testing/btest/Baseline/comm.remote_log/send.send.out @@ -0,0 +1 @@ +Comm::remote_connection_established, 127.0.0.1, 9999/tcp diff --git a/testing/btest/Baseline/comm.remote_log/send.test.log b/testing/btest/Baseline/comm.remote_log/send.test.log new file mode 100644 index 0000000000..0d6dae756c --- /dev/null +++ b/testing/btest/Baseline/comm.remote_log/send.test.log @@ -0,0 +1,15 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path test +#open 2015-01-26-22-47-11 +#fields msg num +#types string count +ping 0 +ping 1 +ping 2 +ping 3 +ping 4 +ping 5 +#close 2015-01-26-22-47-11 diff --git a/testing/btest/Baseline/comm.remote_print/recv.recv.out b/testing/btest/Baseline/comm.remote_print/recv.recv.out new file mode 100644 index 0000000000..6e5a37abbf --- /dev/null +++ b/testing/btest/Baseline/comm.remote_print/recv.recv.out @@ -0,0 +1,6 @@ +got print msg, ping 0 +got print msg, ping 1 +got print msg, ping 2 +got print msg, ping 3 +got print msg, ping 4 +got print msg, ping 5 diff --git a/testing/btest/Baseline/comm.remote_print/send.send.out b/testing/btest/Baseline/comm.remote_print/send.send.out new file mode 100644 index 0000000000..982ee993f6 --- /dev/null +++ b/testing/btest/Baseline/comm.remote_print/send.send.out @@ -0,0 +1,7 @@ +Comm::remote_connection_established, 127.0.0.1, 9999/tcp +got print msg, pong 0 +got print msg, pong 1 +got print msg, pong 2 +got print msg, pong 3 +got print msg, pong 4 +got print msg, pong 5 diff --git a/testing/btest/btest.cfg b/testing/btest/btest.cfg index 43f29d40a1..2eea514357 100644 --- a/testing/btest/btest.cfg +++ b/testing/btest/btest.cfg @@ -1,5 +1,5 @@ [btest] -TestDirs = doc bifs language core scripts istate coverage signatures plugins +TestDirs = doc bifs language core scripts istate coverage signatures plugins comm TmpDir = %(testbase)s/.tmp BaselineDir = %(testbase)s/Baseline IgnoreDirs = .svn CVS .tmp diff --git a/testing/btest/comm/remote_event.test b/testing/btest/comm/remote_event.test new file mode 100644 index 0000000000..9ab9a6b224 --- /dev/null +++ b/testing/btest/comm/remote_event.test @@ -0,0 +1,100 @@ +# @TEST_SERIALIZE: brokercomm +# @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt + +# @TEST-EXEC: btest-bg-run recv "bro -b ../recv.bro >recv.out" +# @TEST-EXEC: btest-bg-run send "bro -b ../send.bro >send.out" + +# @TEST-EXEC: btest-bg-wait 20 +# @TEST-EXEC: btest-diff recv/recv.out +# @TEST-EXEC: btest-diff send/send.out + +@TEST-START-FILE recv.bro + +redef exit_only_after_terminate = T; + +global event_handler: event(msg: string, c: count); +global auto_event_handler: event(msg: string, c: count); + +event bro_init() + { + Comm::listen(9999/tcp, "127.0.0.1"); + Comm::subscribe_to_events("bro/event/"); + Comm::auto_event("bro/event/my_topic", auto_event_handler); + } + +global event_count = 0; + +event event_handler(msg: string, n: count) + { + event auto_event_handler(msg, n); + print "got event msg", msg, n; + local args = Comm::event_args(event_handler, "pong", event_count); + Comm::event("bro/event/my_topic", args); + ++event_count; + + if ( n == 5 ) + terminate(); + } + +@TEST-END-FILE + +@TEST-START-FILE send.bro + +redef exit_only_after_terminate = T; + +global event_handler: event(msg: string, c: count); +global auto_event_handler: event(msg: string, c: count); + +event bro_init() + { + Comm::subscribe_to_events("bro/event/my_topic"); + Comm::connect("127.0.0.1", 9999/tcp, 1secs); + } + +global event_count = 0; + +event Comm::remote_connection_established(peer_address: string, + peer_port: port, + peer_name: string) + { + print "Comm::remote_connection_established", peer_address, peer_port; + local args = Comm::event_args(event_handler, "ping", event_count); + Comm::event("bro/event/hi", args); + ++event_count; + } + +global done = F; +global done_auto = F; + +function check_terminate() + { + if ( done && done_auto ) + terminate(); + } + +event event_handler(msg: string, n: count) + { + print "got event msg", msg, n; + local args = Comm::event_args(event_handler, "ping", event_count); + Comm::event("bro/event/hi", args); + ++event_count; + + if ( n == 5 ) + { + done = T; + check_terminate(); + } + } + +event auto_event_handler(msg: string, n: count) + { + print "got auto event msg", msg, n; + + if ( n == 5 ) + { + done_auto = T; + check_terminate(); + } + } + +@TEST-END-FILE diff --git a/testing/btest/comm/remote_log.test b/testing/btest/comm/remote_log.test new file mode 100644 index 0000000000..aea88cdc25 --- /dev/null +++ b/testing/btest/comm/remote_log.test @@ -0,0 +1,87 @@ +# @TEST_SERIALIZE: brokercomm +# @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt + +# @TEST-EXEC: btest-bg-run recv "bro -b ../common.bro ../recv.bro >recv.out" +# @TEST-EXEC: btest-bg-run send "bro -b ../common.bro ../send.bro >send.out" + +# @TEST-EXEC: btest-bg-wait 20 +# @TEST-EXEC: btest-diff recv/recv.out +# @TEST-EXEC: btest-diff recv/test.log +# @TEST-EXEC: btest-diff send/send.out +# @TEST-EXEC: btest-diff send/test.log + +@TEST-START-FILE common.bro + +module Test; + +export { + redef enum Log::ID += { LOG }; + + type Info: record { + msg: string &log; + num: count &log; + }; + + global log_test: event(rec: Test::Info); + + event bro_init() &priority=5 + { + Log::create_stream(Test::LOG, [$columns=Test::Info, $ev=log_test]); + } +} + +@TEST-END-FILE + +@TEST-START-FILE recv.bro + +redef exit_only_after_terminate = T; + +event bro_init() + { + Comm::listen(9999/tcp, "127.0.0.1"); + Comm::subscribe_to_logs("bro/log/"); + } + +event Test::log_test(rec: Test::Info) + { + print "wrote log", rec; + + if ( rec$num == 5 ) + terminate(); + } + +@TEST-END-FILE + +@TEST-START-FILE send.bro + +redef exit_only_after_terminate = T; + +event bro_init() + { + Comm::enable_remote_logs(Test::LOG); + Comm::connect("127.0.0.1", 9999/tcp, 1secs); + } + +global n = 0; + +event do_write() + { + if ( n == 6 ) + terminate(); + else + { + Log::write(Test::LOG, [$msg = "ping", $num = n]); + ++n; + event do_write(); + } + } + +event Comm::remote_connection_established(peer_address: string, + peer_port: port, + peer_name: string) + { + print "Comm::remote_connection_established", peer_address, peer_port; + event do_write(); + } + +@TEST-END-FILE diff --git a/testing/btest/comm/remote_print.test b/testing/btest/comm/remote_print.test new file mode 100644 index 0000000000..48dfd98bed --- /dev/null +++ b/testing/btest/comm/remote_print.test @@ -0,0 +1,66 @@ +# @TEST_SERIALIZE: brokercomm +# @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt + +# @TEST-EXEC: btest-bg-run recv "bro -b ../recv.bro >recv.out" +# @TEST-EXEC: btest-bg-run send "bro -b ../send.bro >send.out" + +# @TEST-EXEC: btest-bg-wait 20 +# @TEST-EXEC: btest-diff recv/recv.out +# @TEST-EXEC: btest-diff send/send.out + +@TEST-START-FILE recv.bro + +redef exit_only_after_terminate = T; + +event bro_init() + { + Comm::listen(9999/tcp, "127.0.0.1"); + Comm::subscribe_to_prints("bro/print/"); + } + +global n = 0; + +event Comm::print_handler(msg: string) + { + print "got print msg", msg; + Comm::print("bro/print/my_topic", fmt("pong %d", n)); + ++n; + + if ( msg == "ping 5" ) + terminate(); + } + +@TEST-END-FILE + +@TEST-START-FILE send.bro + +redef exit_only_after_terminate = T; + +event bro_init() + { + Comm::subscribe_to_prints("bro/print/my_topic"); + Comm::connect("127.0.0.1", 9999/tcp, 1secs); + } + +global n = 0; + +event Comm::remote_connection_established(peer_address: string, + peer_port: port, + peer_name: string) + { + print "Comm::remote_connection_established", peer_address, peer_port; + Comm::print("bro/print/hi", fmt("ping %d", n)); + ++n; + } + +event Comm::print_handler(msg: string) + { + print "got print msg", msg; + Comm::print("bro/print/hi", fmt("ping %d", n)); + ++n; + + if ( msg == "pong 5" ) + terminate(); + } + +@TEST-END-FILE From 36bc7ba5b5d25cea881db22fb1a5bc2bc5fbc3e4 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 27 Jan 2015 10:13:10 -0600 Subject: [PATCH 097/299] Handle guess_lexer exceptions in pygments reST directive --- CHANGES | 4 ++++ VERSION | 2 +- doc/ext/rst_directive.py | 5 ++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index a50dc265cc..1b084c7f19 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,8 @@ +2.3-397 | 2015-01-27 10:13:10 -0600 + + * Handle guess_lexer exceptions in pygments reST directive (Jon Siwek) + 2.3-396 | 2015-01-23 10:49:15 -0600 * DNP3: fix reachable assertion and buffer over-read/overflow. diff --git a/VERSION b/VERSION index 081c98cc51..9a536ef2e7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3-396 +2.3-397 diff --git a/doc/ext/rst_directive.py b/doc/ext/rst_directive.py index 434eef2c61..43c95abc52 100644 --- a/doc/ext/rst_directive.py +++ b/doc/ext/rst_directive.py @@ -135,7 +135,10 @@ class Pygments(Directive): # lexer not found, use default. lexer = TextLexer() else: - lexer = guess_lexer(content) + try: + lexer = guess_lexer(content) + except: + lexer = TextLexer() # import sys # print >>sys.stderr, self.arguments, lexer.__class__ From 0537711fd4b83ded01e430bd8d6192fcc3619bdd Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 27 Jan 2015 10:48:05 -0600 Subject: [PATCH 098/299] update broker submodule --- aux/broker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aux/broker b/aux/broker index ebc66f484a..177bdfac2c 160000 --- a/aux/broker +++ b/aux/broker @@ -1 +1 @@ -Subproject commit ebc66f484af27a32dc5d91b1c985638847e35cf6 +Subproject commit 177bdfac2c768d9ed8f3edb10e9e2dbd0d6f8723 From d2ea87735a5a9cb34c5df4a4d4de83d399681e53 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Thu, 29 Jan 2015 10:42:48 -0600 Subject: [PATCH 099/299] broker integration: add bifs to inspect/manipulate broker data i.e. script-layer functions to convert between bro values and broker values; mostly for use w/ Bro's data store interface (coming soon). --- scripts/base/frameworks/comm/main.bro | 5 + src/Reporter.cc | 13 + src/Reporter.h | 4 + src/comm/Data.cc | 168 ++++++++- src/comm/Data.h | 155 +++++++- src/comm/Manager.cc | 26 +- src/comm/Manager.h | 15 +- src/comm/comm.bif | 490 ++++++++++++++++++++++++++ testing/btest/Baseline/comm.data/out | 99 ++++++ testing/btest/comm/data.bro | 219 ++++++++++++ 10 files changed, 1154 insertions(+), 40 deletions(-) create mode 100644 testing/btest/Baseline/comm.data/out create mode 100644 testing/btest/comm/data.bro diff --git a/scripts/base/frameworks/comm/main.bro b/scripts/base/frameworks/comm/main.bro index efe3069a1c..974e5e43af 100644 --- a/scripts/base/frameworks/comm/main.bro +++ b/scripts/base/frameworks/comm/main.bro @@ -19,4 +19,9 @@ export { name: string &optional; # nil for invalid event/args. args: vector of Comm::Data; }; + + type Comm::TableItem : record { + key: Comm::Data; + val: Comm::Data; + }; } diff --git a/src/Reporter.cc b/src/Reporter.cc index 9002633b10..cd1aa09d4c 100644 --- a/src/Reporter.cc +++ b/src/Reporter.cc @@ -123,6 +123,19 @@ void Reporter::ExprRuntimeError(const Expr* expr, const char* fmt, ...) throw InterpreterException(); } +void Reporter::RuntimeError(const Location* location, const char* fmt, ...) + { + ++errors; + PushLocation(location); + va_list ap; + va_start(ap, fmt); + FILE* out = errors_to_stderr ? stderr : 0; + DoLog("runtime error", reporter_error, out, 0, 0, true, true, "", fmt, ap); + va_end(ap); + PopLocation(); + throw InterpreterException(); + } + void Reporter::InternalError(const char* fmt, ...) { va_list ap; diff --git a/src/Reporter.h b/src/Reporter.h index e477ad8934..52bcd7d02a 100644 --- a/src/Reporter.h +++ b/src/Reporter.h @@ -73,6 +73,10 @@ public: // function will not return but raise an InterpreterException. void ExprRuntimeError(const Expr* expr, const char* fmt, ...); + // Report a runtime error in evaluating a Bro script expression. This + // function will not return but raise an InterpreterException. + void RuntimeError(const Location* location, const char* fmt, ...); + // Report a traffic weirdness, i.e., an unexpected protocol situation // that may lead to incorrectly processing a connnection. void Weird(const char* name); // Raises net_weird(). diff --git a/src/comm/Data.cc b/src/comm/Data.cc index b279b97529..f32bedd885 100644 --- a/src/comm/Data.cc +++ b/src/comm/Data.cc @@ -4,6 +4,10 @@ using namespace std; OpaqueType* comm::opaque_of_data_type; +OpaqueType* comm::opaque_of_set_iterator; +OpaqueType* comm::opaque_of_table_iterator; +OpaqueType* comm::opaque_of_vector_iterator; +OpaqueType* comm::opaque_of_record_iterator; static broker::port::protocol to_broker_port_proto(TransportProto tp) { @@ -20,7 +24,7 @@ static broker::port::protocol to_broker_port_proto(TransportProto tp) } } -static TransportProto to_bro_port_proto(broker::port::protocol tp) +TransportProto comm::to_bro_port_proto(broker::port::protocol tp) { switch ( tp ) { case broker::port::protocol::tcp: @@ -70,7 +74,7 @@ struct val_converter { return nullptr; } - result_type operator()(const std::string& a) + result_type operator()(std::string& a) { switch ( type->Tag() ) { case TYPE_STRING: @@ -103,7 +107,7 @@ struct val_converter { } } - result_type operator()(const broker::address& a) + result_type operator()(broker::address& a) { if ( type->Tag() == TYPE_ADDR ) { @@ -114,7 +118,7 @@ struct val_converter { return nullptr; } - result_type operator()(const broker::subnet& a) + result_type operator()(broker::subnet& a) { if ( type->Tag() == TYPE_SUBNET ) { @@ -125,15 +129,15 @@ struct val_converter { return nullptr; } - result_type operator()(const broker::port& a) + result_type operator()(broker::port& a) { if ( type->Tag() == TYPE_PORT ) - return new PortVal(a.number(), to_bro_port_proto(a.type())); + return new PortVal(a.number(), comm::to_bro_port_proto(a.type())); return nullptr; } - result_type operator()(const broker::time_point& a) + result_type operator()(broker::time_point& a) { if ( type->Tag() == TYPE_TIME ) return new Val(a.value, TYPE_TIME); @@ -141,7 +145,7 @@ struct val_converter { return nullptr; } - result_type operator()(const broker::time_duration& a) + result_type operator()(broker::time_duration& a) { if ( type->Tag() == TYPE_INTERVAL ) return new Val(a.value, TYPE_INTERVAL); @@ -149,7 +153,7 @@ struct val_converter { return nullptr; } - result_type operator()(const broker::enum_value& a) + result_type operator()(broker::enum_value& a) { if ( type->Tag() == TYPE_ENUM ) { @@ -175,12 +179,13 @@ struct val_converter { for ( auto& item : a ) { + broker::vector composite_key; auto indices = broker::get(item); if ( ! indices ) { - Unref(rval); - return nullptr; + composite_key.emplace_back(move(item)); + indices = &composite_key; } auto expected_index_types = tt->Indices()->Types(); @@ -226,12 +231,13 @@ struct val_converter { for ( auto& item : a ) { + broker::vector composite_key; auto indices = broker::get(item.first); if ( ! indices ) { - Unref(rval); - return nullptr; + composite_key.emplace_back(move(item.first)); + indices = &composite_key; } auto expected_index_types = tt->Indices()->Types(); @@ -341,7 +347,7 @@ Val* comm::data_to_val(broker::data d, BroType* type) return broker::visit(val_converter{type}, d); } -broker::util::optional comm::val_to_data(const Val* v) +broker::util::optional comm::val_to_data(Val* v) { switch ( v->Type()->Tag() ) { case TYPE_BOOL: @@ -388,7 +394,7 @@ broker::util::optional comm::val_to_data(const Val* v) { auto enum_type = v->Type()->AsEnumType(); auto enum_name = enum_type->Lookup(v->AsEnum()); - return {broker::enum_value(enum_name ? "" : enum_name)}; + return {broker::enum_value(enum_name ? enum_name : "")}; } case TYPE_STRING: { @@ -433,7 +439,9 @@ broker::util::optional comm::val_to_data(const Val* v) auto entry = table->NextEntry(k, c); auto vl = table_val->RecoverIndex(k); iter_guard ig(k, vl); - broker::vector key; + + broker::vector composite_key; + composite_key.reserve(vl->Length()); for ( auto k = 0; k < vl->Length(); ++k ) { @@ -442,9 +450,16 @@ broker::util::optional comm::val_to_data(const Val* v) if ( ! key_part ) return {}; - key.emplace_back(move(*key_part)); + composite_key.emplace_back(move(*key_part)); } + broker::data key; + + if ( composite_key.size() == 1 ) + key = move(composite_key[0]); + else + key = move(composite_key); + if ( is_set ) broker::get(rval)->emplace(move(key)); else @@ -521,7 +536,7 @@ broker::util::optional comm::val_to_data(const Val* v) return {}; } -RecordVal* comm::make_data_val(const Val* v) +RecordVal* comm::make_data_val(Val* v) { auto rval = new RecordVal(BifType::Record::Comm::Data); auto data = val_to_data(v); @@ -531,3 +546,120 @@ RecordVal* comm::make_data_val(const Val* v) return rval; } + +RecordVal* comm::make_data_val(broker::data d) + { + auto rval = new RecordVal(BifType::Record::Comm::Data); + rval->Assign(0, new DataVal(move(d))); + return rval; + } + +struct data_type_getter { + using result_type = EnumVal*; + + result_type operator()(bool a) + { + return new EnumVal(BifEnum::Comm::BOOL, + BifType::Enum::Comm::DataType); + } + + result_type operator()(uint64_t a) + { + return new EnumVal(BifEnum::Comm::COUNT, + BifType::Enum::Comm::DataType); + } + + result_type operator()(int64_t a) + { + return new EnumVal(BifEnum::Comm::INT, + BifType::Enum::Comm::DataType); + } + + result_type operator()(double a) + { + return new EnumVal(BifEnum::Comm::DOUBLE, + BifType::Enum::Comm::DataType); + } + + result_type operator()(const std::string& a) + { + return new EnumVal(BifEnum::Comm::STRING, + BifType::Enum::Comm::DataType); + } + + result_type operator()(const broker::address& a) + { + return new EnumVal(BifEnum::Comm::ADDR, + BifType::Enum::Comm::DataType); + } + + result_type operator()(const broker::subnet& a) + { + return new EnumVal(BifEnum::Comm::SUBNET, + BifType::Enum::Comm::DataType); + } + + result_type operator()(const broker::port& a) + { + return new EnumVal(BifEnum::Comm::PORT, + BifType::Enum::Comm::DataType); + } + + result_type operator()(const broker::time_point& a) + { + return new EnumVal(BifEnum::Comm::TIME, + BifType::Enum::Comm::DataType); + } + + result_type operator()(const broker::time_duration& a) + { + return new EnumVal(BifEnum::Comm::INTERVAL, + BifType::Enum::Comm::DataType); + } + + result_type operator()(const broker::enum_value& a) + { + return new EnumVal(BifEnum::Comm::ENUM, + BifType::Enum::Comm::DataType); + } + + result_type operator()(const broker::set& a) + { + return new EnumVal(BifEnum::Comm::SET, + BifType::Enum::Comm::DataType); + } + + result_type operator()(const broker::table& a) + { + return new EnumVal(BifEnum::Comm::TABLE, + BifType::Enum::Comm::DataType); + } + + result_type operator()(const broker::vector& a) + { + return new EnumVal(BifEnum::Comm::VECTOR, + BifType::Enum::Comm::DataType); + } + + result_type operator()(const broker::record& a) + { + return new EnumVal(BifEnum::Comm::RECORD, + BifType::Enum::Comm::DataType); + } +}; + +EnumVal* comm::get_data_type(RecordVal* v, Frame* frame) + { + return broker::visit(data_type_getter{}, opaque_field_to_data(v, frame)); + } + +broker::data& comm::opaque_field_to_data(RecordVal* v, Frame* f) + { + Val* d = v->Lookup(0); + + if ( ! d ) + reporter->RuntimeError(f->GetCall()->GetLocationInfo(), + "Comm::Data's opaque field is not set"); + + return static_cast(d)->data; + } diff --git a/src/comm/Data.h b/src/comm/Data.h index e3197b61da..c720dcda71 100644 --- a/src/comm/Data.h +++ b/src/comm/Data.h @@ -3,14 +3,27 @@ #include #include "Val.h" +#include "Reporter.h" +#include "Frame.h" +#include "Expr.h" namespace comm { extern OpaqueType* opaque_of_data_type; +extern OpaqueType* opaque_of_set_iterator; +extern OpaqueType* opaque_of_table_iterator; +extern OpaqueType* opaque_of_vector_iterator; +extern OpaqueType* opaque_of_record_iterator; -RecordVal* make_data_val(const Val* v); +TransportProto to_bro_port_proto(broker::port::protocol tp); -broker::util::optional val_to_data(const Val* v); +RecordVal* make_data_val(Val* v); + +RecordVal* make_data_val(broker::data d); + +EnumVal* get_data_type(RecordVal* v, Frame* frame); + +broker::util::optional val_to_data(Val* v); Val* data_to_val(broker::data d, BroType* type); @@ -21,9 +34,147 @@ public: : OpaqueVal(comm::opaque_of_data_type), data(std::move(arg_data)) {} + void ValDescribe(ODesc* d) const override + { + d->Add("broker::data{"); + d->Add(broker::to_string(data)); + d->Add("}"); + } + broker::data data; }; +struct type_name_getter { + using result_type = const char*; + + result_type operator()(bool a) + { return "bool"; } + + result_type operator()(uint64_t a) + { return "uint64_t"; } + + result_type operator()(int64_t a) + { return "int64_t"; } + + result_type operator()(double a) + { return "double"; } + + result_type operator()(const std::string& a) + { return "string"; } + + result_type operator()(const broker::address& a) + { return "address"; } + + result_type operator()(const broker::subnet& a) + { return "subnet"; } + + result_type operator()(const broker::port& a) + { return "port"; } + + result_type operator()(const broker::time_point& a) + { return "time"; } + + result_type operator()(const broker::time_duration& a) + { return "interval"; } + + result_type operator()(const broker::enum_value& a) + { return "enum"; } + + result_type operator()(const broker::set& a) + { return "set"; } + + result_type operator()(const broker::table& a) + { return "table"; } + + result_type operator()(const broker::vector& a) + { return "vector"; } + + result_type operator()(const broker::record& a) + { return "record"; } +}; + +broker::data& opaque_field_to_data(RecordVal* v, Frame* f); + +template +T& require_data_type(broker::data& d, TypeTag tag, Frame* f) + { + auto ptr = broker::get(d); + + if ( ! ptr ) + reporter->RuntimeError(f->GetCall()->GetLocationInfo(), + "data is of type '%s' not of type '%s'", + broker::visit(type_name_getter{}, d), + type_name(tag)); + + return *ptr; + } + +template +inline T& require_data_type(RecordVal* v, TypeTag tag, Frame* f) + { + return require_data_type(opaque_field_to_data(v, f), tag, f); + } + +template +inline Val* refine(RecordVal* v, TypeTag tag, Frame* f) + { + return new Val(require_data_type(v, tag, f), tag); + } + +// Copying data in to iterator vals is not the fastest approach, but safer... + +class SetIterator : public OpaqueVal { +public: + + SetIterator(RecordVal* v, TypeTag tag, Frame* f) + : OpaqueVal(comm::opaque_of_set_iterator), + dat(require_data_type(v, TYPE_TABLE, f)), + it(dat.begin()) + {} + + broker::set dat; + broker::set::iterator it; +}; + +class TableIterator : public OpaqueVal { +public: + + TableIterator(RecordVal* v, TypeTag tag, Frame* f) + : OpaqueVal(comm::opaque_of_table_iterator), + dat(require_data_type(v, TYPE_TABLE, f)), + it(dat.begin()) + {} + + broker::table dat; + broker::table::iterator it; +}; + +class VectorIterator : public OpaqueVal { +public: + + VectorIterator(RecordVal* v, TypeTag tag, Frame* f) + : OpaqueVal(comm::opaque_of_vector_iterator), + dat(require_data_type(v, TYPE_VECTOR, f)), + it(dat.begin()) + {} + + broker::vector dat; + broker::vector::iterator it; +}; + +class RecordIterator : public OpaqueVal { +public: + + RecordIterator(RecordVal* v, TypeTag tag, Frame* f) + : OpaqueVal(comm::opaque_of_record_iterator), + dat(require_data_type(v, TYPE_VECTOR, f)), + it(dat.fields.begin()) + {} + + broker::record dat; + decltype(broker::record::fields)::iterator it; +}; + } // namespace comm #endif // BRO_COMM_DATA_H diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index bc0bc3f8a8..e64c74c377 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -22,7 +22,7 @@ bool comm::Manager::InitPreScript() return true; } -static int require_field(const RecordType* rt, const char* name) +static int require_field(RecordType* rt, const char* name) { auto rval = rt->FieldOffset(name); @@ -43,6 +43,10 @@ bool comm::Manager::InitPostScript() log_id_type = internal_type("Log::ID")->AsEnumType(); comm::opaque_of_data_type = new OpaqueType("Comm::Data"); + comm::opaque_of_set_iterator = new OpaqueType("Comm::SetIterator"); + comm::opaque_of_table_iterator = new OpaqueType("Comm::TableIterator"); + comm::opaque_of_vector_iterator = new OpaqueType("Comm::VectorIterator"); + comm::opaque_of_record_iterator = new OpaqueType("Comm::RecordIterator"); vector_of_data_type = new VectorType(internal_type("Comm::Data")->Ref()); auto res = broker::init(); @@ -110,7 +114,7 @@ bool comm::Manager::Disconnect(const string& addr, uint16_t port) return rval; } -bool comm::Manager::Print(string topic, string msg, const Val* flags) +bool comm::Manager::Print(string topic, string msg, Val* flags) { endpoint->send(move(topic), broker::message{move(msg)}, GetFlags(flags)); return true; @@ -122,8 +126,7 @@ bool comm::Manager::Event(std::string topic, broker::message msg, int flags) return true; } -bool comm::Manager::Log(const EnumVal* stream, const RecordVal* columns, - int flags) +bool comm::Manager::Log(EnumVal* stream, RecordVal* columns, int flags) { auto stream_name = stream->Type()->AsEnumType()->Lookup(stream->AsEnum()); @@ -150,8 +153,7 @@ bool comm::Manager::Log(const EnumVal* stream, const RecordVal* columns, return true; } -bool comm::Manager::Event(std::string topic, const RecordVal* args, - const Val* flags) +bool comm::Manager::Event(std::string topic, RecordVal* args, Val* flags) { if ( ! args->Lookup(0) ) return false; @@ -165,7 +167,7 @@ bool comm::Manager::Event(std::string topic, const RecordVal* args, for ( auto i = 0u; i < vv->Size(); ++i ) { auto val = vv->Lookup(i)->AsRecordVal()->Lookup(0); - auto data_val = dynamic_cast(val); + auto data_val = static_cast(val); msg.emplace_back(data_val->data); } @@ -173,7 +175,7 @@ bool comm::Manager::Event(std::string topic, const RecordVal* args, return true; } -bool comm::Manager::AutoEvent(string topic, const Val* event, const Val* flags) +bool comm::Manager::AutoEvent(string topic, Val* event, Val* flags) { if ( event->Type()->Tag() != TYPE_FUNC ) { @@ -202,7 +204,7 @@ bool comm::Manager::AutoEvent(string topic, const Val* event, const Val* flags) return true; } -bool comm::Manager::AutoEventStop(const string& topic, const Val* event) +bool comm::Manager::AutoEventStop(const string& topic, Val* event) { if ( event->Type()->Tag() != TYPE_FUNC ) { @@ -232,12 +234,12 @@ bool comm::Manager::AutoEventStop(const string& topic, const Val* event) return true; } -RecordVal* comm::Manager::MakeEventArgs(const val_list* args) +RecordVal* comm::Manager::MakeEventArgs(val_list* args) { auto rval = new RecordVal(BifType::Record::Comm::EventArgs); auto arg_vec = new VectorVal(vector_of_data_type); rval->Assign(1, arg_vec); - const Func* func; + Func* func; for ( auto i = 0u; i < args->length(); ++i ) { @@ -347,7 +349,7 @@ bool comm::Manager::UnsubscribeToLogs(const string& topic_prefix) return log_subscriptions.erase(topic_prefix); } -int comm::Manager::GetFlags(const Val* flags) +int comm::Manager::GetFlags(Val* flags) { auto r = flags->AsRecordVal(); int rval = 0; diff --git a/src/comm/Manager.h b/src/comm/Manager.h index 5e3ec350b8..44f5eb0f2b 100644 --- a/src/comm/Manager.h +++ b/src/comm/Manager.h @@ -31,18 +31,18 @@ public: bool Disconnect(const std::string& addr, uint16_t port); - bool Print(std::string topic, std::string msg, const Val* flags); + bool Print(std::string topic, std::string msg, Val* flags); bool Event(std::string topic, broker::message msg, int flags); - bool Event(std::string topic, const RecordVal* args, const Val* flags); + bool Event(std::string topic, RecordVal* args, Val* flags); - bool Log(const EnumVal* stream_id, const RecordVal* columns, int flags); + bool Log(EnumVal* stream_id, RecordVal* columns, int flags); - bool AutoEvent(std::string topic, const Val* event, const Val* flags); + bool AutoEvent(std::string topic, Val* event, Val* flags); - bool AutoEventStop(const std::string& topic, const Val* event); + bool AutoEventStop(const std::string& topic, Val* event); - RecordVal* MakeEventArgs(const val_list* args); + RecordVal* MakeEventArgs(val_list* args); bool SubscribeToPrints(std::string topic_prefix); @@ -56,7 +56,7 @@ public: bool UnsubscribeToLogs(const std::string& topic_prefix); - static int GetFlags(const Val* flags); + static int GetFlags(Val* flags); private: @@ -82,7 +82,6 @@ private: static int send_flags_self_idx; static int send_flags_peers_idx; static int send_flags_unsolicited_idx; - }; } // namespace comm diff --git a/src/comm/comm.bif b/src/comm/comm.bif index ebe206d266..b2ce0fb415 100644 --- a/src/comm/comm.bif +++ b/src/comm/comm.bif @@ -7,17 +7,507 @@ module Comm; +enum DataType %{ + BOOL, + INT, + COUNT, + DOUBLE, + STRING, + ADDR, + SUBNET, + PORT, + TIME, + INTERVAL, + ENUM, + SET, + TABLE, + VECTOR, + RECORD, +%} + type Comm::SendFlags: record; type Comm::Data: record; type Comm::EventArgs: record; +type Comm::TableItem: record; + function Comm::data%(d: any%): Comm::Data %{ return comm::make_data_val(d); %} +function Comm::data_type%(d: Comm::Data%): Comm::DataType + %{ + return comm::get_data_type(d->AsRecordVal(), frame); + %} + +function Comm::refine_to_bool%(d: Comm::Data%): bool + %{ + return comm::refine(d->AsRecordVal(), TYPE_BOOL, frame); + %} + +function Comm::refine_to_int%(d: Comm::Data%): int + %{ + return comm::refine(d->AsRecordVal(), TYPE_INT, frame); + %} + +function Comm::refine_to_count%(d: Comm::Data%): count + %{ + return comm::refine(d->AsRecordVal(), TYPE_COUNT, frame); + %} + +function Comm::refine_to_double%(d: Comm::Data%): double + %{ + return comm::refine(d->AsRecordVal(), TYPE_DOUBLE, frame); + %} + +function Comm::refine_to_string%(d: Comm::Data%): string + %{ + return new StringVal(comm::require_data_type(d->AsRecordVal(), + TYPE_STRING, + frame)); + %} + +function Comm::refine_to_addr%(d: Comm::Data%): addr + %{ + auto& a = comm::require_data_type(d->AsRecordVal(), + TYPE_ADDR, frame); + auto bits = reinterpret_cast(&a.bytes()); + return new AddrVal(IPAddr(*bits)); + %} + +function Comm::refine_to_subnet%(d: Comm::Data%): subnet + %{ + auto& a = comm::require_data_type(d->AsRecordVal(), + TYPE_SUBNET, frame); + auto bits = reinterpret_cast(&a.network().bytes()); + return new SubNetVal(IPPrefix(IPAddr(*bits), a.length())); + %} + +function Comm::refine_to_port%(d: Comm::Data%): port + %{ + auto& a = comm::require_data_type(d->AsRecordVal(), + TYPE_SUBNET, frame); + return new PortVal(a.number(), comm::to_bro_port_proto(a.type())); + %} + +function Comm::refine_to_time%(d: Comm::Data%): time + %{ + auto v = comm::require_data_type(d->AsRecordVal(), + TYPE_TIME, frame).value; + return new Val(v, TYPE_TIME); + %} + +function Comm::refine_to_interval%(d: Comm::Data%): interval + %{ + auto v = comm::require_data_type(d->AsRecordVal(), + TYPE_TIME, frame).value; + return new Val(v, TYPE_INTERVAL); + %} + +function Comm::refine_to_enum_name%(d: Comm::Data%): string + %{ + auto& v = comm::require_data_type(d->AsRecordVal(), + TYPE_ENUM, frame).name; + return new StringVal(v); + %} + +function Comm::set_create%(%): Comm::Data + %{ + return comm::make_data_val(broker::set()); + %} + +function Comm::set_clear%(s: Comm::Data%): bool + %{ + auto& v = comm::require_data_type(s->AsRecordVal(), TYPE_TABLE, + frame); + v.clear(); + return new Val(true, TYPE_BOOL); + %} + +function Comm::set_size%(s: Comm::Data%): count + %{ + auto& v = comm::require_data_type(s->AsRecordVal(), TYPE_TABLE, + frame); + return new Val(static_cast(v.size()), TYPE_COUNT); + %} + +function Comm::set_contains%(s: Comm::Data, key: Comm::Data%): bool + %{ + auto& v = comm::require_data_type(s->AsRecordVal(), TYPE_TABLE, + frame); + auto& k = comm::opaque_field_to_data(key->AsRecordVal(), frame); + return new Val(v.find(k) != v.end(), TYPE_BOOL); + %} + +function Comm::set_insert%(s: Comm::Data, key: Comm::Data%): bool + %{ + auto& v = comm::require_data_type(s->AsRecordVal(), TYPE_TABLE, + frame); + auto& k = comm::opaque_field_to_data(key->AsRecordVal(), frame); + return new Val(v.insert(k).second, TYPE_BOOL); + %} + +function Comm::set_remove%(s: Comm::Data, key: Comm::Data%): bool + %{ + auto& v = comm::require_data_type(s->AsRecordVal(), TYPE_TABLE, + frame); + auto& k = comm::opaque_field_to_data(key->AsRecordVal(), frame); + return new Val(v.erase(k) > 0, TYPE_BOOL); + %} + +function Comm::set_iterator%(s: Comm::Data%): opaque of Comm::SetIterator + %{ + return new comm::SetIterator(s->AsRecordVal(), TYPE_TABLE, frame); + %} + +function Comm::set_iterator_last%(it: opaque of Comm::SetIterator%): bool + %{ + auto set_it = static_cast(it); + return new Val(set_it->it == set_it->dat.end(), TYPE_BOOL); + %} + +function Comm::set_iterator_next%(it: opaque of Comm::SetIterator%): bool + %{ + auto set_it = static_cast(it); + + if ( set_it->it == set_it->dat.end() ) + return new Val(false, TYPE_BOOL); + + ++set_it->it; + return new Val(set_it->it != set_it->dat.end(), TYPE_BOOL); + %} + +function Comm::set_iterator_value%(it: opaque of Comm::SetIterator%): Comm::Data + %{ + auto set_it = static_cast(it); + auto rval = new RecordVal(BifType::Record::Comm::Data); + + if ( set_it->it == set_it->dat.end() ) + { + reporter->PushLocation(frame->GetCall()->GetLocationInfo()); + reporter->Warning("attempt to retrieve value of invalid set iterator"); + reporter->PopLocation(); + return rval; + } + + rval->Assign(0, new comm::DataVal(*set_it->it)); + return rval; + %} + +function Comm::table_create%(%): Comm::Data + %{ + return comm::make_data_val(broker::table()); + %} + +function Comm::table_clear%(t: Comm::Data%): bool + %{ + auto& v = comm::require_data_type(t->AsRecordVal(), + TYPE_TABLE, frame); + v.clear(); + return new Val(true, TYPE_BOOL); + %} + +function Comm::table_size%(t: Comm::Data%): count + %{ + auto& v = comm::require_data_type(t->AsRecordVal(), + TYPE_TABLE, frame); + return new Val(static_cast(v.size()), TYPE_COUNT); + %} + +function Comm::table_contains%(t: Comm::Data, key: Comm::Data%): bool + %{ + auto& v = comm::require_data_type(t->AsRecordVal(), + TYPE_TABLE, frame); + auto& k = comm::opaque_field_to_data(key->AsRecordVal(), frame); + return new Val(v.find(k) != v.end(), TYPE_BOOL); + %} + +function Comm::table_insert%(t: Comm::Data, key: Comm::Data, val: Comm::Data%): Comm::Data + %{ + auto& table = comm::require_data_type(t->AsRecordVal(), + TYPE_TABLE, frame); + auto& k = comm::opaque_field_to_data(key->AsRecordVal(), frame); + auto& v = comm::opaque_field_to_data(val->AsRecordVal(), frame); + + try + { + auto& prev = table.at(k); + auto rval = comm::make_data_val(move(prev)); + prev = v; + return rval; + } + catch (const std::out_of_range&) + { + table[k] = v; + return new RecordVal(BifType::Record::Comm::Data); + } + %} + +function Comm::table_remove%(t: Comm::Data, key: Comm::Data%): Comm::Data + %{ + auto& table = comm::require_data_type(t->AsRecordVal(), + TYPE_TABLE, frame); + auto& k = comm::opaque_field_to_data(key->AsRecordVal(), frame); + auto it = table.find(k); + + if ( it == table.end() ) + return new RecordVal(BifType::Record::Comm::Data); + else + { + auto rval = comm::make_data_val(move(it->second)); + table.erase(it); + return rval; + } + %} + +function Comm::table_lookup%(t: Comm::Data, key: Comm::Data%): Comm::Data + %{ + auto& table = comm::require_data_type(t->AsRecordVal(), + TYPE_TABLE, frame); + auto& k = comm::opaque_field_to_data(key->AsRecordVal(), frame); + auto it = table.find(k); + + if ( it == table.end() ) + return new RecordVal(BifType::Record::Comm::Data); + else + return comm::make_data_val(it->second); + %} + +function Comm::table_iterator%(t: Comm::Data%): opaque of Comm::TableIterator + %{ + return new comm::TableIterator(t->AsRecordVal(), TYPE_TABLE, frame); + %} + +function Comm::table_iterator_last%(it: opaque of Comm::TableIterator%): bool + %{ + auto ti = static_cast(it); + return new Val(ti->it == ti->dat.end(), TYPE_BOOL); + %} + +function Comm::table_iterator_next%(it: opaque of Comm::TableIterator%): bool + %{ + auto ti = static_cast(it); + + if ( ti->it == ti->dat.end() ) + return new Val(false, TYPE_BOOL); + + ++ti->it; + return new Val(ti->it != ti->dat.end(), TYPE_BOOL); + %} + +function Comm::table_iterator_value%(it: opaque of Comm::TableIterator%): Comm::TableItem + %{ + auto ti = static_cast(it); + auto rval = new RecordVal(BifType::Record::Comm::TableItem); + auto key_val = new RecordVal(BifType::Record::Comm::Data); + auto val_val = new RecordVal(BifType::Record::Comm::Data); + rval->Assign(0, key_val); + rval->Assign(1, val_val); + + if ( ti->it == ti->dat.end() ) + { + reporter->PushLocation(frame->GetCall()->GetLocationInfo()); + reporter->Warning("attempt to retrieve value of invalid table iterator"); + reporter->PopLocation(); + return rval; + } + + key_val->Assign(0, new comm::DataVal(ti->it->first)); + val_val->Assign(0, new comm::DataVal(ti->it->second)); + return rval; + %} + +function Comm::vector_create%(%): Comm::Data + %{ + return comm::make_data_val(broker::vector()); + %} + +function Comm::vector_clear%(v: Comm::Data%): bool + %{ + auto& vec = comm::require_data_type(v->AsRecordVal(), + TYPE_VECTOR, frame); + vec.clear(); + return new Val(true, TYPE_BOOL); + %} + +function Comm::vector_size%(v: Comm::Data%): count + %{ + auto& vec = comm::require_data_type(v->AsRecordVal(), + TYPE_VECTOR, frame); + return new Val(static_cast(vec.size()), TYPE_COUNT); + %} + +function Comm::vector_insert%(v: Comm::Data, d: Comm::Data, idx: count%): bool + %{ + auto& vec = comm::require_data_type(v->AsRecordVal(), + TYPE_VECTOR, frame); + auto& item = comm::opaque_field_to_data(d->AsRecordVal(), frame); + idx = min(idx, static_cast(vec.size())); + vec.insert(vec.begin() + idx, item); + return new Val(true, TYPE_BOOL); + %} + +function Comm::vector_replace%(v: Comm::Data, d: Comm::Data, idx: count%): Comm::Data + %{ + auto& vec = comm::require_data_type(v->AsRecordVal(), + TYPE_VECTOR, frame); + auto& item = comm::opaque_field_to_data(d->AsRecordVal(), frame); + + if ( idx >= vec.size() ) + return new RecordVal(BifType::Record::Comm::Data); + + auto rval = comm::make_data_val(move(vec[idx])); + vec[idx] = item; + return rval; + %} + +function Comm::vector_remove%(v: Comm::Data, idx: count%): Comm::Data + %{ + auto& vec = comm::require_data_type(v->AsRecordVal(), + TYPE_VECTOR, frame); + + if ( idx >= vec.size() ) + return new RecordVal(BifType::Record::Comm::Data); + + auto rval = comm::make_data_val(move(vec[idx])); + vec.erase(vec.begin() + idx); + return rval; + %} + +function Comm::vector_lookup%(v: Comm::Data, idx: count%): Comm::Data + %{ + auto& vec = comm::require_data_type(v->AsRecordVal(), + TYPE_VECTOR, frame); + + if ( idx >= vec.size() ) + return new RecordVal(BifType::Record::Comm::Data); + + return comm::make_data_val(vec[idx]); + %} + +function Comm::vector_iterator%(v: Comm::Data%): opaque of Comm::VectorIterator + %{ + return new comm::VectorIterator(v->AsRecordVal(), TYPE_VECTOR, frame); + %} + +function Comm::vector_iterator_last%(it: opaque of Comm::VectorIterator%): bool + %{ + auto vi = static_cast(it); + return new Val(vi->it == vi->dat.end(), TYPE_BOOL); + %} + +function Comm::vector_iterator_next%(it: opaque of Comm::VectorIterator%): bool + %{ + auto vi = static_cast(it); + + if ( vi->it == vi->dat.end() ) + return new Val(false, TYPE_BOOL); + + ++vi->it; + return new Val(vi->it != vi->dat.end(), TYPE_BOOL); + %} + +function Comm::vector_iterator_value%(it: opaque of Comm::VectorIterator%): Comm::Data + %{ + auto vi = static_cast(it); + auto rval = new RecordVal(BifType::Record::Comm::Data); + + if ( vi->it == vi->dat.end() ) + { + reporter->PushLocation(frame->GetCall()->GetLocationInfo()); + reporter->Warning("attempt to retrieve value of invalid table iterator"); + reporter->PopLocation(); + return rval; + } + + rval->Assign(0, new comm::DataVal(*vi->it)); + return rval; + %} + +function Comm::record_create%(sz: count%): Comm::Data + %{ + return comm::make_data_val(broker::record(std::vector(sz))); + %} + +function Comm::record_size%(r: Comm::Data%): count + %{ + auto& v = comm::require_data_type(r->AsRecordVal(), + TYPE_RECORD, frame); + return new Val(static_cast(v.fields.size()), TYPE_COUNT); + %} + +function Comm::record_assign%(r: Comm::Data, d: Comm::Data, idx: count%): bool + %{ + auto& v = comm::require_data_type(r->AsRecordVal(), + TYPE_RECORD, frame); + auto& item = comm::opaque_field_to_data(d->AsRecordVal(), frame); + + if ( idx >= v.fields.size() ) + return new Val(false, TYPE_BOOL); + + v.fields[idx] = item; + return new Val(true, TYPE_BOOL); + %} + +function Comm::record_lookup%(r: Comm::Data, idx: count%): Comm::Data + %{ + auto& v = comm::require_data_type(r->AsRecordVal(), + TYPE_RECORD, frame); + + if ( idx >= v.size() ) + return new RecordVal(BifType::Record::Comm::Data); + + if ( ! v.fields[idx] ) + return new RecordVal(BifType::Record::Comm::Data); + + return comm::make_data_val(*v.fields[idx]); + %} + +function Comm::record_iterator%(r: Comm::Data%): opaque of Comm::RecordIterator + %{ + return new comm::RecordIterator(r->AsRecordVal(), TYPE_RECORD, frame); + %} + +function Comm::record_iterator_last%(it: opaque of Comm::RecordIterator%): bool + %{ + auto ri = static_cast(it); + return new Val(ri->it == ri->dat.fields.end(), TYPE_BOOL); + %} + +function Comm::record_iterator_next%(it: opaque of Comm::RecordIterator%): bool + %{ + auto ri = static_cast(it); + + if ( ri->it == ri->dat.fields.end() ) + return new Val(false, TYPE_BOOL); + + ++ri->it; + return new Val(ri->it != ri->dat.fields.end(), TYPE_BOOL); + %} + +function Comm::record_iterator_value%(it: opaque of Comm::RecordIterator%): Comm::Data + %{ + auto ri = static_cast(it); + auto rval = new RecordVal(BifType::Record::Comm::Data); + + if ( ri->it == ri->dat.fields.end() ) + { + reporter->PushLocation(frame->GetCall()->GetLocationInfo()); + reporter->Warning("attempt to retrieve value of invalid record iterator"); + reporter->PopLocation(); + return rval; + } + + if ( ! *ri->it ) + return rval; // field isn't set + + rval->Assign(0, new comm::DataVal(**ri->it)); + return rval; + %} + event Comm::remote_connection_established%(peer_address: string, peer_port: port, peer_name: string%); diff --git a/testing/btest/Baseline/comm.data/out b/testing/btest/Baseline/comm.data/out new file mode 100644 index 0000000000..eea78d39a2 --- /dev/null +++ b/testing/btest/Baseline/comm.data/out @@ -0,0 +1,99 @@ +Comm::BOOL +Comm::INT +Comm::COUNT +Comm::DOUBLE +Comm::STRING +Comm::ADDR +Comm::SUBNET +Comm::PORT +Comm::TIME +Comm::INTERVAL +Comm::ENUM +Comm::SET +Comm::TABLE +Comm::VECTOR +Comm::RECORD +*************************** +T +F +1 +0 +-1 +1 +0 +1.1 +-11.1 +hello +1.2.3.4 +192.168.0.0/16 +22/tcp +42.0 +180.0 +Comm::BOOL +*************************** +{ +two, +one, +three +} +0 +T +1 +T +F +T +2 +T +1 +F +{ +bye +} +0 +*************************** +{ +[two] = 2, +[one] = 1, +[three] = 3 +} +0 +[d=] +1 +T +42 +F +[d=] +2 +[d=broker::data{7}] +2 +37 +[d=broker::data{42}] +1 +*************************** +[zero, one, two] +0 +T +T +T +T +[hi, salutations, hello, greetings] +4 +[d=broker::data{hello}] +[d=broker::data{bah}] +[d=broker::data{hi}] +[hi, salutations, bah, greetings] +[d=broker::data{bah}] +[hi, salutations, greetings] +3 +*************************** +[a=, b=bee, c=1] +[a=test, b=bee, c=1] +[a=test, b=testagain, c=1] +3 +T +T +T +[d=broker::data{hi}] +[d=broker::data{hello}] +[d=broker::data{37}] +3 diff --git a/testing/btest/comm/data.bro b/testing/btest/comm/data.bro new file mode 100644 index 0000000000..3fb9dcd86e --- /dev/null +++ b/testing/btest/comm/data.bro @@ -0,0 +1,219 @@ +# @TEST-EXEC: bro -b %INPUT >out +# @TEST-EXEC: btest-diff out + +type bro_set: set[string]; +type bro_table: table[string] of count; +type bro_vector: vector of string; + +type bro_record : record { + a: string &optional; + b: string &default = "bee"; + c: count; +}; + +function comm_record_to_bro_record_recurse(it: opaque of Comm::RecordIterator, + rval: bro_record, + idx: count): bro_record + { + if ( Comm::record_iterator_last(it) ) + return rval; + + local field_value = Comm::record_iterator_value(it); + + if ( field_value?$d ) + switch ( idx ) { + case 0: + rval$a = Comm::refine_to_string(field_value); + break; + case 1: + rval$b = Comm::refine_to_string(field_value); + break; + case 2: + rval$c = Comm::refine_to_count(field_value); + break; + }; + + ++idx; + Comm::record_iterator_next(it); + return comm_record_to_bro_record_recurse(it, rval, idx); + } + +function comm_record_to_bro_record(d: Comm::Data): bro_record + { + return comm_record_to_bro_record_recurse(Comm::record_iterator(d), + bro_record($c = 0), 0); + } + +function +comm_set_to_bro_set_recurse(it: opaque of Comm::SetIterator, + rval: bro_set): bro_set + { + if ( Comm::set_iterator_last(it) ) + return rval; + + add rval[Comm::refine_to_string(Comm::set_iterator_value(it))]; + Comm::set_iterator_next(it); + return comm_set_to_bro_set_recurse(it, rval); + } + + +function comm_set_to_bro_set(d: Comm::Data): bro_set + { + return comm_set_to_bro_set_recurse(Comm::set_iterator(d), bro_set()); + } + +function +comm_table_to_bro_table_recurse(it: opaque of Comm::TableIterator, + rval: bro_table): bro_table + { + if ( Comm::table_iterator_last(it) ) + return rval; + + local item = Comm::table_iterator_value(it); + rval[Comm::refine_to_string(item$key)] = Comm::refine_to_count(item$val); + Comm::table_iterator_next(it); + return comm_table_to_bro_table_recurse(it, rval); + } + +function comm_table_to_bro_table(d: Comm::Data): bro_table + { + return comm_table_to_bro_table_recurse(Comm::table_iterator(d), + bro_table()); + } + +function comm_vector_to_bro_vector_recurse(it: opaque of Comm::VectorIterator, + rval: bro_vector): bro_vector + { + if ( Comm::vector_iterator_last(it) ) + return rval; + + rval[|rval|] = Comm::refine_to_string(Comm::vector_iterator_value(it)); + Comm::vector_iterator_next(it); + return comm_vector_to_bro_vector_recurse(it, rval); + } + +function comm_vector_to_bro_vector(d: Comm::Data): bro_vector + { + return comm_vector_to_bro_vector_recurse(Comm::vector_iterator(d), + bro_vector()); + } + +event bro_init() +{ +print Comm::data_type(Comm::data(T)); +print Comm::data_type(Comm::data(+1)); +print Comm::data_type(Comm::data(1)); +print Comm::data_type(Comm::data(1.1)); +print Comm::data_type(Comm::data("1 (how creative)")); +print Comm::data_type(Comm::data(1.1.1.1)); +print Comm::data_type(Comm::data(1.1.1.1/1)); +print Comm::data_type(Comm::data(1/udp)); +print Comm::data_type(Comm::data(double_to_time(1))); +print Comm::data_type(Comm::data(1sec)); +print Comm::data_type(Comm::data(Comm::BOOL)); +local s: bro_set = bro_set("one", "two", "three"); +local t: bro_table = bro_table(["one"] = 1, ["two"] = 2, ["three"] = 3); +local v: bro_vector = bro_vector("zero", "one", "two"); +local r: bro_record = bro_record($c = 1); +print Comm::data_type(Comm::data(s)); +print Comm::data_type(Comm::data(t)); +print Comm::data_type(Comm::data(v)); +print Comm::data_type(Comm::data(r)); + +print "***************************"; + +print Comm::refine_to_bool(Comm::data(T)); +print Comm::refine_to_bool(Comm::data(F)); +print Comm::refine_to_int(Comm::data(+1)); +print Comm::refine_to_int(Comm::data(+0)); +print Comm::refine_to_int(Comm::data(-1)); +print Comm::refine_to_count(Comm::data(1)); +print Comm::refine_to_count(Comm::data(0)); +print Comm::refine_to_double(Comm::data(1.1)); +print Comm::refine_to_double(Comm::data(-11.1)); +print Comm::refine_to_string(Comm::data("hello")); +print Comm::refine_to_addr(Comm::data(1.2.3.4)); +print Comm::refine_to_subnet(Comm::data(192.168.1.1/16)); +print Comm::refine_to_port(Comm::data(22/tcp)); +print Comm::refine_to_time(Comm::data(double_to_time(42))); +print Comm::refine_to_interval(Comm::data(3min)); +print Comm::refine_to_enum_name(Comm::data(Comm::BOOL)); + +print "***************************"; + +local cs = Comm::data(s); +print comm_set_to_bro_set(cs); +cs = Comm::set_create(); +print Comm::set_size(cs); +print Comm::set_insert(cs, Comm::data("hi")); +print Comm::set_size(cs); +print Comm::set_contains(cs, Comm::data("hi")); +print Comm::set_contains(cs, Comm::data("bye")); +print Comm::set_insert(cs, Comm::data("bye")); +print Comm::set_size(cs); +print Comm::set_remove(cs, Comm::data("hi")); +print Comm::set_size(cs); +print Comm::set_remove(cs, Comm::data("hi")); +print comm_set_to_bro_set(cs); +Comm::set_clear(cs); +print Comm::set_size(cs); + +print "***************************"; + +local ct = Comm::data(t); +print comm_table_to_bro_table(ct); +ct = Comm::table_create(); +print Comm::table_size(ct); +print Comm::table_insert(ct, Comm::data("hi"), Comm::data(42)); +print Comm::table_size(ct); +print Comm::table_contains(ct, Comm::data("hi")); +print Comm::refine_to_count(Comm::table_lookup(ct, Comm::data("hi"))); +print Comm::table_contains(ct, Comm::data("bye")); +print Comm::table_insert(ct, Comm::data("bye"), Comm::data(7)); +print Comm::table_size(ct); +print Comm::table_insert(ct, Comm::data("bye"), Comm::data(37)); +print Comm::table_size(ct); +print Comm::refine_to_count(Comm::table_lookup(ct, Comm::data("bye"))); +print Comm::table_remove(ct, Comm::data("hi")); +print Comm::table_size(ct); + +print "***************************"; + +local cv = Comm::data(v); +print comm_vector_to_bro_vector(cv); +cv = Comm::vector_create(); +print Comm::vector_size(cv); +print Comm::vector_insert(cv, Comm::data("hi"), 0); +print Comm::vector_insert(cv, Comm::data("hello"), 1); +print Comm::vector_insert(cv, Comm::data("greetings"), 2); +print Comm::vector_insert(cv, Comm::data("salutations"), 1); +print comm_vector_to_bro_vector(cv); +print Comm::vector_size(cv); +print Comm::vector_replace(cv, Comm::data("bah"), 2); +print Comm::vector_lookup(cv, 2); +print Comm::vector_lookup(cv, 0); +print comm_vector_to_bro_vector(cv); +print Comm::vector_remove(cv, 2); +print comm_vector_to_bro_vector(cv); +print Comm::vector_size(cv); + +print "***************************"; + +local cr = Comm::data(r); +print comm_record_to_bro_record(cr); +r$a = "test"; +cr = Comm::data(r); +print comm_record_to_bro_record(cr); +r$b = "testagain"; +cr = Comm::data(r); +print comm_record_to_bro_record(cr); +cr = Comm::record_create(3); +print Comm::record_size(cr); +print Comm::record_assign(cr, Comm::data("hi"), 0); +print Comm::record_assign(cr, Comm::data("hello"), 1); +print Comm::record_assign(cr, Comm::data(37), 2); +print Comm::record_lookup(cr, 0); +print Comm::record_lookup(cr, 1); +print Comm::record_lookup(cr, 2); +print Comm::record_size(cr); +} From 9875f5d3eba7e19506c6b2970d8c8ea444927007 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Fri, 30 Jan 2015 14:39:16 -0600 Subject: [PATCH 100/299] broker integration: add distributed data store api But haven't done the full gamut of testing on it yet. --- scripts/base/frameworks/comm/main.bro | 38 ++- src/Trigger.cc | 12 +- src/Trigger.h | 5 + src/comm/CMakeLists.txt | 10 +- src/comm/Data.cc | 2 +- src/comm/Manager.cc | 145 +++++++++- src/comm/Manager.h | 17 ++ src/comm/Store.cc | 141 ++++++++++ src/comm/Store.h | 113 ++++++++ src/comm/{comm.bif => data.bif} | 151 +--------- src/comm/messaging.bif | 156 +++++++++++ src/comm/store.bif | 378 ++++++++++++++++++++++++++ 12 files changed, 1012 insertions(+), 156 deletions(-) create mode 100644 src/comm/Store.cc create mode 100644 src/comm/Store.h rename src/comm/{comm.bif => data.bif} (76%) create mode 100644 src/comm/messaging.bif create mode 100644 src/comm/store.bif diff --git a/scripts/base/frameworks/comm/main.bro b/scripts/base/frameworks/comm/main.bro index 974e5e43af..a2cd1f6ac0 100644 --- a/scripts/base/frameworks/comm/main.bro +++ b/scripts/base/frameworks/comm/main.bro @@ -15,9 +15,11 @@ export { d: opaque of Comm::Data &optional; }; + type DataVector: vector of Comm::Data; + type EventArgs: record { name: string &optional; # nil for invalid event/args. - args: vector of Comm::Data; + args: DataVector; }; type Comm::TableItem : record { @@ -25,3 +27,37 @@ export { val: Comm::Data; }; } + +module Store; + +export { + + type QueryStatus: enum { + SUCCESS, + FAILURE, + }; + + type ExpiryTime: record { + absolute: time &optional; + since_last_modification: interval &optional; + }; + + type QueryResult: record { + status: Store::QueryStatus; + result: Comm::Data; + }; + + type SQLiteOptions: record { + path: string &default = "store.sqlite"; + }; + + type RocksDBOptions: record { + path: string &default = "store.rocksdb"; + use_merge_operator: bool &default = F; + }; + + type BackendOptions: record { + sqlite: SQLiteOptions &default = SQLiteOptions(); + rocksdb: RocksDBOptions &default = RocksDBOptions(); + }; +} diff --git a/src/Trigger.cc b/src/Trigger.cc index 099027f4e0..772a991791 100644 --- a/src/Trigger.cc +++ b/src/Trigger.cc @@ -112,6 +112,7 @@ Trigger::Trigger(Expr* arg_cond, Stmt* arg_body, Stmt* arg_timeout_stmts, attached = 0; is_return = arg_is_return; location = arg_location; + timeout_value = -1; ++total_triggers; @@ -133,17 +134,22 @@ Trigger::Trigger(Expr* arg_cond, Stmt* arg_body, Stmt* arg_timeout_stmts, Val* timeout_val = arg_timeout ? arg_timeout->Eval(arg_frame) : 0; + if ( timeout_val ) + { + Unref(timeout_val); + timeout_value = timeout_val->AsInterval(); + } + // Make sure we don't get deleted if somebody calls a method like // Timeout() while evaluating the trigger. Ref(this); - if ( ! Eval() && timeout_val ) + if ( ! Eval() && timeout_value >= 0 ) { - timer = new TriggerTimer(timeout_val->AsInterval(), this); + timer = new TriggerTimer(timeout_value, this); timer_mgr->Add(timer); } - Unref(timeout_val); Unref(this); } diff --git a/src/Trigger.h b/src/Trigger.h index b752ea8ada..7662901dc5 100644 --- a/src/Trigger.h +++ b/src/Trigger.h @@ -32,6 +32,10 @@ public: // Executes timeout code and deletes the object. void Timeout(); + // Return the timeout interval (negative if none was specified). + double TimeoutValue() const + { return timeout_value; } + // Called if another entity needs to complete its operations first // in any case before this trigger can proceed. void Hold() { delayed = true; } @@ -87,6 +91,7 @@ private: Stmt* body; Stmt* timeout_stmts; Expr* timeout; + double timeout_value; Frame* frame; bool is_return; const Location* location; diff --git a/src/comm/CMakeLists.txt b/src/comm/CMakeLists.txt index 95ad701d71..da726e54d6 100644 --- a/src/comm/CMakeLists.txt +++ b/src/comm/CMakeLists.txt @@ -5,12 +5,20 @@ include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR} ) +if ( ROCKSDB_INCLUDE_DIR ) + add_definitions(-DHAVE_ROCKSDB) + include_directories(BEFORE ${ROCKSDB_INCLUDE_DIR}) +endif () + set(comm_SRCS Data.cc Manager.cc + Store.cc ) -bif_target(comm.bif) +bif_target(data.bif) +bif_target(messaging.bif) +bif_target(store.bif) bro_add_subdir_library(comm ${comm_SRCS} ${BIF_OUTPUT_CC}) add_dependencies(bro_comm generate_outputs) diff --git a/src/comm/Data.cc b/src/comm/Data.cc index f32bedd885..3b1a240988 100644 --- a/src/comm/Data.cc +++ b/src/comm/Data.cc @@ -1,5 +1,5 @@ #include "Data.h" -#include "comm/comm.bif.h" +#include "comm/data.bif.h" using namespace std; diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index e64c74c377..9f17878cf6 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -1,12 +1,15 @@ #include "Manager.h" #include "Data.h" +#include "Store.h" #include #include #include #include "util.h" #include "Var.h" #include "Reporter.h" -#include "comm/comm.bif.h" +#include "comm/data.bif.h" +#include "comm/messaging.bif.h" +#include "comm/store.bif.h" #include "logging/Manager.h" using namespace std; @@ -17,6 +20,12 @@ int comm::Manager::send_flags_self_idx; int comm::Manager::send_flags_peers_idx; int comm::Manager::send_flags_unsolicited_idx; +comm::Manager::~Manager() + { + for ( auto& s : data_stores ) + CloseStore(s.first); + } + bool comm::Manager::InitPreScript() { return true; @@ -47,6 +56,7 @@ bool comm::Manager::InitPostScript() comm::opaque_of_table_iterator = new OpaqueType("Comm::TableIterator"); comm::opaque_of_vector_iterator = new OpaqueType("Comm::VectorIterator"); comm::opaque_of_record_iterator = new OpaqueType("Comm::RecordIterator"); + comm::opaque_of_store_handle = new OpaqueType("Store::Handle"); vector_of_data_type = new VectorType(internal_type("Comm::Data")->Ref()); auto res = broker::init(); @@ -385,6 +395,9 @@ void comm::Manager::GetFds(iosource::FD_Set* read, iosource::FD_Set* write, for ( const auto& ps : log_subscriptions ) read->Insert(ps.second.fd()); + + for ( const auto& s : data_stores ) + read->Insert(s.second->store->responses().fd()); } double comm::Manager::NextTimestamp(double* local_network_time) @@ -393,6 +406,49 @@ double comm::Manager::NextTimestamp(double* local_network_time) return timer_mgr->Time(); } +struct response_converter { + using result_type = RecordVal*; + + result_type operator()(bool d) + { + return comm::make_data_val(broker::data{d}); + } + + result_type operator()(uint64_t d) + { + return comm::make_data_val(broker::data{d}); + } + + result_type operator()(broker::data& d) + { + return comm::make_data_val(move(d)); + } + + result_type operator()(std::vector& d) + { + return comm::make_data_val(broker::data{move(d)}); + } + + result_type operator()(broker::store::snapshot& d) + { + broker::table table; + + for ( auto& item : d.entries ) + { + auto& key = item.first; + auto& val = item.second.item; + table[move(key)] = move(val); + } + + return comm::make_data_val(broker::data{move(table)}); + } +}; + +static RecordVal* response_to_val(broker::store::response r) + { + return broker::visit(response_converter{}, r.reply.value); + } + void comm::Manager::Process() { bool idle = true; @@ -624,5 +680,92 @@ void comm::Manager::Process() } } + for ( const auto& s : data_stores ) + { + auto responses = s.second->store->responses().want_pop(); + + if ( responses.empty() ) + continue; + + idle = false; + + for ( auto& response : responses ) + { + auto ck = static_cast(response.cookie); + auto it = pending_queries.find(ck); + + if ( it == pending_queries.end() ) + { + reporter->Warning("unmatched response to query on store %s", + s.second->store->id().data()); + continue; + } + + auto query = *it; + + switch ( response.reply.stat ) { + case broker::store::result::status::timeout: + // Fine, trigger's timeout takes care of things. + break; + case broker::store::result::status::failure: + query->Result(query_result()); + break; + case broker::store::result::status::success: + query->Result(query_result(response_to_val(move(response)))); + break; + default: + reporter->InternalWarning("unknown store response status: %d", + static_cast(response.reply.stat)); + break; + } + + pending_queries.erase(it); + } + } + SetIdle(idle); } + +bool comm::Manager::AddStore(StoreHandleVal* handle) + { + if ( ! handle->store ) + return false; + + if ( data_stores.find(handle->store->id()) != data_stores.end() ) + return false; + + data_stores[handle->store->id()] = handle; + Ref(handle); + return true; + } + +bool comm::Manager::CloseStore(const broker::store::identifier& id) + { + auto it = data_stores.find(id); + + if ( it == data_stores.end() ) + return false; + + for ( auto it = pending_queries.begin(); it != pending_queries.end(); ) + { + auto query = *it; + + if ( query->StoreID() == id ) + { + it = pending_queries.erase(it); + query->Abort(); + delete query; + } + else + ++it; + } + + it->second->store = nullptr; + Unref(it->second); + return true; + } + +bool comm::Manager::TrackStoreQuery(StoreQueryCallback* cb) + { + return pending_queries.insert(cb).second; + } diff --git a/src/comm/Manager.h b/src/comm/Manager.h index 44f5eb0f2b..c9cc2c8464 100644 --- a/src/comm/Manager.h +++ b/src/comm/Manager.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include "comm/Store.h" #include "Reporter.h" #include "iosource/IOSource.h" #include "Val.h" @@ -17,8 +19,11 @@ namespace comm { // Manages various forms of communication between peer Bro processes // or possibly between different parts of a single Bro process. class Manager : public iosource::IOSource { +friend class StoreHandleVal; public: + ~Manager(); + bool InitPreScript(); bool InitPostScript(); @@ -56,6 +61,12 @@ public: bool UnsubscribeToLogs(const std::string& topic_prefix); + bool AddStore(StoreHandleVal* handle); + + bool CloseStore(const broker::store::identifier& id); + + bool TrackStoreQuery(StoreQueryCallback* cb); + static int GetFlags(Val* flags); private: @@ -71,12 +82,18 @@ private: const char* Tag() override { return "Comm::Manager"; } + broker::endpoint& Endpoint() + { return *endpoint; } + std::unique_ptr endpoint; std::map, broker::peering> peers; std::map print_subscriptions; std::map event_subscriptions; std::map log_subscriptions; + std::map data_stores; + std::unordered_set pending_queries; + static VectorType* vector_of_data_type; static EnumType* log_id_type; static int send_flags_self_idx; diff --git a/src/comm/Store.cc b/src/comm/Store.cc new file mode 100644 index 0000000000..0d94795ce8 --- /dev/null +++ b/src/comm/Store.cc @@ -0,0 +1,141 @@ +#include "Store.h" +#include "comm/Manager.h" + +#include +#include +#include + +#ifdef HAVE_ROCKSDB +#include +#include +#endif + +OpaqueType* comm::opaque_of_store_handle; + +comm::StoreHandleVal::StoreHandleVal(broker::store::identifier id, + comm::StoreType arg_type, + broker::util::optional arg_back, + RecordVal* backend_options, std::chrono::duration resync) + : OpaqueVal(opaque_of_store_handle), + store(), store_type(arg_type), backend_type(arg_back) + { + using BifEnum::Store::BackendType; + std::unique_ptr backend; + + if ( backend_type ) + switch ( *backend_type ) { + case BackendType::MEMORY: + backend.reset(new broker::store::memory_backend); + break; + case BackendType::SQLITE: + { + auto sqlite = new broker::store::sqlite_backend; + std::string path = backend_options->Lookup(0)->AsRecordVal() + ->Lookup(0)->AsStringVal()->CheckString(); + + if ( sqlite->open(path) ) + backend.reset(sqlite); + else + { + reporter->Error("failed to open sqlite backend at path %s: %s", + path.data(), sqlite->last_error().data()); + delete sqlite; + } + } + break; + case BackendType::ROCKSDB: + { +#ifdef HAVE_ROCKSDB + std::string path = backend_options->Lookup(1)->AsRecordVal() + ->Lookup(0)->AsStringVal()->CheckString(); + bool use_merge_op = backend_options->Lookup(1)->AsRecordVal() + ->Lookup(1)->AsBool(); + rocksdb::Options rock_op; + rock_op.create_if_missing = true; + + if ( use_merge_op ) + options.merge_operator.reset(new rocksdb_merge_operator); + + auto rocksdb = new broker::store::rocksdb_backend; + + if ( rocksdb->open(path, options).ok() ) + backend.reset(rocksdb); + else + { + reporter->Error("failed to open rocksdb backend at path %s: %s", + path.data(), rocksdb->last_error().data()); + delete rocksdb; + } +#else + reporter->Error("rocksdb backend support is not enabled"); +#endif + } + break; + default: + reporter->FatalError("unknown data store backend: %d", + static_cast(*backend_type)); + } + + switch ( store_type ) { + case StoreType::FRONTEND: + store.reset(new broker::store::frontend(comm_mgr->Endpoint(), + move(id))); + break; + case StoreType::MASTER: + store.reset(new broker::store::master(comm_mgr->Endpoint(), + move(id), move(backend))); + break; + case StoreType::CLONE: + store.reset(new broker::store::clone(comm_mgr->Endpoint(), + move(id), resync, + move(backend))); + break; + default: + reporter->FatalError("unknown data store type: %d", + static_cast(store_type)); + } + } + +void comm::StoreHandleVal::ValDescribe(ODesc* d) const + { + using BifEnum::Store::BackendType; + d->Add("broker::store::"); + + switch ( store_type ) { + case StoreType::FRONTEND: + d->Add("frontend"); + break; + case StoreType::MASTER: + d->Add("master"); + break; + case StoreType::CLONE: + d->Add("clone"); + break; + default: + d->Add("unknown"); + } + + d->Add("{"); + d->Add(store->id()); + + if ( backend_type ) + { + d->Add(", "); + + switch ( *backend_type ) { + case BackendType::MEMORY: + d->Add("memory"); + break; + case BackendType::SQLITE: + d->Add("sqlite"); + break; + case BackendType::ROCKSDB: + d->Add("rocksdb"); + break; + default: + d->Add("unknown"); + } + } + + d->Add("}"); + } diff --git a/src/comm/Store.h b/src/comm/Store.h new file mode 100644 index 0000000000..b3a8ccb339 --- /dev/null +++ b/src/comm/Store.h @@ -0,0 +1,113 @@ +#ifndef BRO_COMM_STORE_H +#define BRO_COMM_STORE_H + +#include "comm/store.bif.h" +#include "comm/data.bif.h" +#include "Reporter.h" +#include "Type.h" +#include "Val.h" +#include "Trigger.h" + +#include + +namespace comm { + +extern OpaqueType* opaque_of_store_handle; + +enum StoreType { + FRONTEND, + MASTER, + CLONE, +}; + +inline EnumVal* query_status(bool success) + { + static EnumType* store_query_status = nullptr; + static int success_val; + static int failure_val; + + if ( ! store_query_status ) + { + store_query_status = internal_type("Store::QueryStatus")->AsEnumType(); + success_val = store_query_status->Lookup("Store", "SUCCESS"); + failure_val = store_query_status->Lookup("Store", "FAILURE"); + } + + return new EnumVal(success ? success_val : failure_val, store_query_status); + } + +inline RecordVal* query_result() + { + auto rval = new RecordVal(BifType::Record::Store::QueryResult); + rval->Assign(0, query_status(false)); + rval->Assign(1, new RecordVal(BifType::Record::Comm::Data)); + return rval; + } + +inline RecordVal* query_result(RecordVal* data) + { + auto rval = new RecordVal(BifType::Record::Store::QueryResult); + rval->Assign(0, query_status(true)); + rval->Assign(1, data); + return rval; + } + +class StoreQueryCallback { +public: + + StoreQueryCallback(Trigger* arg_trigger, const CallExpr* arg_call, + broker::store::identifier arg_store_id) + : trigger(arg_trigger), call(arg_call), store_id(move(arg_store_id)) + { + Ref(trigger); + } + + ~StoreQueryCallback() + { + Unref(trigger); + } + + void Result(RecordVal* result) + { + trigger->Cache(call, result); + trigger->Release(); + Unref(result); + } + + void Abort() + { + auto result = query_result(); + trigger->Cache(call, result); + trigger->Release(); + Unref(result); + } + + const broker::store::identifier& StoreID() const + { return store_id; } + +private: + + Trigger* trigger; + const CallExpr* call; + broker::store::identifier store_id; +}; + +class StoreHandleVal : public OpaqueVal { +public: + + StoreHandleVal(broker::store::identifier id, + comm::StoreType arg_type, + broker::util::optional arg_back, + RecordVal* backend_options, + std::chrono::duration resync = std::chrono::seconds(1)); + + void ValDescribe(ODesc* d) const override; + + std::unique_ptr store; + comm::StoreType store_type; + broker::util::optional backend_type; +}; + +} // namespace comm + +#endif // BRO_COMM_STORE_H diff --git a/src/comm/comm.bif b/src/comm/data.bif similarity index 76% rename from src/comm/comm.bif rename to src/comm/data.bif index b2ce0fb415..2a78a9229a 100644 --- a/src/comm/comm.bif +++ b/src/comm/data.bif @@ -1,8 +1,8 @@ +##! Functions for inspecting and manipulating broker data. + %%{ -#include "comm/Manager.h" #include "comm/Data.h" -#include "logging/Manager.h" %%} module Comm; @@ -25,12 +25,8 @@ enum DataType %{ RECORD, %} -type Comm::SendFlags: record; - type Comm::Data: record; -type Comm::EventArgs: record; - type Comm::TableItem: record; function Comm::data%(d: any%): Comm::Data @@ -507,146 +503,3 @@ function Comm::record_iterator_value%(it: opaque of Comm::RecordIterator%): Comm rval->Assign(0, new comm::DataVal(**ri->it)); return rval; %} - -event Comm::remote_connection_established%(peer_address: string, - peer_port: port, - peer_name: string%); - -event Comm::remote_connection_broken%(peer_address: string, - peer_port: port%); - -event Comm::remote_connection_incompatible%(peer_address: string, - peer_port: port%); - -function Comm::listen%(p: port, a: string &default = "", - reuse: bool &default = T%): bool - %{ - if ( ! p->IsTCP() ) - { - reporter->Error("listen port must use tcp"); - return new Val(false, TYPE_BOOL); - } - - auto rval = comm_mgr->Listen(p->Port(), a->Len() ? a->CheckString() : 0, - reuse); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::connect%(a: string, p: port, retry: interval%): bool - %{ - if ( ! p->IsTCP() ) - { - reporter->Error("remote connection port must use tcp"); - return new Val(false, TYPE_BOOL); - } - - auto rval = comm_mgr->Connect(a->CheckString(), p->Port(), - std::chrono::duration(retry)); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::disconnect%(a: string, p: port%): bool - %{ - if ( ! p->IsTCP() ) - { - reporter->Error("remote connection port must use tcp"); - return new Val(false, TYPE_BOOL); - } - - auto rval = comm_mgr->Disconnect(a->CheckString(), p->Port()); - return new Val(rval, TYPE_BOOL); - %} - -event Comm::print_handler%(msg: string%); - -function Comm::print%(topic: string, msg: string, - flags: SendFlags &default = SendFlags()%): bool - %{ - auto rval = comm_mgr->Print(topic->CheckString(), msg->CheckString(), - flags); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::subscribe_to_prints%(topic_prefix: string%): bool - %{ - auto rval = comm_mgr->SubscribeToPrints(topic_prefix->CheckString()); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::unsubscribe_to_prints%(topic_prefix: string%): bool - %{ - auto rval = comm_mgr->UnsubscribeToPrints(topic_prefix->CheckString()); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::event_args%(...%): Comm::EventArgs - %{ - auto rval = comm_mgr->MakeEventArgs(@ARGS@); - return rval; - %} - -function Comm::event%(topic: string, args: Comm::EventArgs, - flags: SendFlags &default = SendFlags()%): bool - %{ - auto rval = comm_mgr->Event(topic->CheckString(), args->AsRecordVal(), - flags); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::auto_event%(topic: string, ev: any, - flags: SendFlags &default = SendFlags()%): bool - %{ - auto rval = comm_mgr->AutoEvent(topic->CheckString(), ev, flags); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::auto_event_stop%(topic: string, ev: any%): bool - %{ - auto rval = comm_mgr->AutoEventStop(topic->CheckString(), ev); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::subscribe_to_events%(topic_prefix: string%): bool - %{ - auto rval = comm_mgr->SubscribeToEvents(topic_prefix->CheckString()); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::unsubscribe_to_events%(topic_prefix: string%): bool - %{ - auto rval = comm_mgr->UnsubscribeToEvents(topic_prefix->CheckString()); - return new Val(rval, TYPE_BOOL); - %} - -function -Comm::enable_remote_logs%(id: Log::ID, - flags: SendFlags &default = SendFlags()%): bool - %{ - auto rval = log_mgr->EnableRemoteLogs(id->AsEnumVal(), - comm::Manager::GetFlags(flags)); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::disable_remote_logs%(id: Log::ID%): bool - %{ - auto rval = log_mgr->DisableRemoteLogs(id->AsEnumVal()); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::remote_logs_enabled%(id: Log::ID%): bool - %{ - auto rval = log_mgr->RemoteLogsAreEnabled(id->AsEnumVal()); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::subscribe_to_logs%(topic_prefix: string%): bool - %{ - auto rval = comm_mgr->SubscribeToLogs(topic_prefix->CheckString()); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::unsubscribe_to_logs%(topic_prefix: string%): bool - %{ - auto rval = comm_mgr->UnsubscribeToLogs(topic_prefix->CheckString()); - return new Val(rval, TYPE_BOOL); - %} diff --git a/src/comm/messaging.bif b/src/comm/messaging.bif new file mode 100644 index 0000000000..f5034f842f --- /dev/null +++ b/src/comm/messaging.bif @@ -0,0 +1,156 @@ + +##! Functions for peering and various messaging patterns (e.g. print/log/event). + +%%{ +#include "comm/Manager.h" +#include "logging/Manager.h" +%%} + +module Comm; + +type Comm::SendFlags: record; + +type Comm::EventArgs: record; + +event Comm::remote_connection_established%(peer_address: string, + peer_port: port, + peer_name: string%); + +event Comm::remote_connection_broken%(peer_address: string, + peer_port: port%); + +event Comm::remote_connection_incompatible%(peer_address: string, + peer_port: port%); + +function Comm::listen%(p: port, a: string &default = "", + reuse: bool &default = T%): bool + %{ + if ( ! p->IsTCP() ) + { + reporter->Error("listen port must use tcp"); + return new Val(false, TYPE_BOOL); + } + + auto rval = comm_mgr->Listen(p->Port(), a->Len() ? a->CheckString() : 0, + reuse); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::connect%(a: string, p: port, retry: interval%): bool + %{ + if ( ! p->IsTCP() ) + { + reporter->Error("remote connection port must use tcp"); + return new Val(false, TYPE_BOOL); + } + + auto rval = comm_mgr->Connect(a->CheckString(), p->Port(), + std::chrono::duration(retry)); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::disconnect%(a: string, p: port%): bool + %{ + if ( ! p->IsTCP() ) + { + reporter->Error("remote connection port must use tcp"); + return new Val(false, TYPE_BOOL); + } + + auto rval = comm_mgr->Disconnect(a->CheckString(), p->Port()); + return new Val(rval, TYPE_BOOL); + %} + +event Comm::print_handler%(msg: string%); + +function Comm::print%(topic: string, msg: string, + flags: SendFlags &default = SendFlags()%): bool + %{ + auto rval = comm_mgr->Print(topic->CheckString(), msg->CheckString(), + flags); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::subscribe_to_prints%(topic_prefix: string%): bool + %{ + auto rval = comm_mgr->SubscribeToPrints(topic_prefix->CheckString()); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::unsubscribe_to_prints%(topic_prefix: string%): bool + %{ + auto rval = comm_mgr->UnsubscribeToPrints(topic_prefix->CheckString()); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::event_args%(...%): Comm::EventArgs + %{ + auto rval = comm_mgr->MakeEventArgs(@ARGS@); + return rval; + %} + +function Comm::event%(topic: string, args: Comm::EventArgs, + flags: SendFlags &default = SendFlags()%): bool + %{ + auto rval = comm_mgr->Event(topic->CheckString(), args->AsRecordVal(), + flags); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::auto_event%(topic: string, ev: any, + flags: SendFlags &default = SendFlags()%): bool + %{ + auto rval = comm_mgr->AutoEvent(topic->CheckString(), ev, flags); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::auto_event_stop%(topic: string, ev: any%): bool + %{ + auto rval = comm_mgr->AutoEventStop(topic->CheckString(), ev); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::subscribe_to_events%(topic_prefix: string%): bool + %{ + auto rval = comm_mgr->SubscribeToEvents(topic_prefix->CheckString()); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::unsubscribe_to_events%(topic_prefix: string%): bool + %{ + auto rval = comm_mgr->UnsubscribeToEvents(topic_prefix->CheckString()); + return new Val(rval, TYPE_BOOL); + %} + +function +Comm::enable_remote_logs%(id: Log::ID, + flags: SendFlags &default = SendFlags()%): bool + %{ + auto rval = log_mgr->EnableRemoteLogs(id->AsEnumVal(), + comm::Manager::GetFlags(flags)); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::disable_remote_logs%(id: Log::ID%): bool + %{ + auto rval = log_mgr->DisableRemoteLogs(id->AsEnumVal()); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::remote_logs_enabled%(id: Log::ID%): bool + %{ + auto rval = log_mgr->RemoteLogsAreEnabled(id->AsEnumVal()); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::subscribe_to_logs%(topic_prefix: string%): bool + %{ + auto rval = comm_mgr->SubscribeToLogs(topic_prefix->CheckString()); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::unsubscribe_to_logs%(topic_prefix: string%): bool + %{ + auto rval = comm_mgr->UnsubscribeToLogs(topic_prefix->CheckString()); + return new Val(rval, TYPE_BOOL); + %} diff --git a/src/comm/store.bif b/src/comm/store.bif new file mode 100644 index 0000000000..fb4c8d57ce --- /dev/null +++ b/src/comm/store.bif @@ -0,0 +1,378 @@ + +##! Functions to interface with broker's distributed data store. + +%%{ +#include "comm/Manager.h" +#include "comm/Store.h" +#include "comm/Data.h" +#include "Trigger.h" +%%} + +module Store; + +type Store::ExpiryTime: record; + +type Store::QueryResult: record; + +type Store::BackendOptions: record; + +enum BackendType %{ + MEMORY, + SQLITE, + ROCKSDB, +%} + +function Store::create_master%(id: string, b: BackendType &default = MEMORY, + options: BackendOptions &default = BackendOptions()%): opaque of Store::Handle + %{ + auto rval = new comm::StoreHandleVal(id->CheckString(), comm::StoreType::MASTER, + static_cast(b->AsEnum()), + options->AsRecordVal()); + comm_mgr->AddStore(rval); + return rval; + %} + +function Store::create_clone%(id: string, b: BackendType &default = MEMORY, + options: BackendOptions &default = BackendOptions(), + resync: interval &default = 1sec%): opaque of Store::Handle + %{ + auto rval = new comm::StoreHandleVal(id->CheckString(), comm::StoreType::CLONE, + static_cast(b->AsEnum()), + options->AsRecordVal(), + std::chrono::duration(resync)); + comm_mgr->AddStore(rval); + return rval; + %} + +function Store::create_frontend%(id: string%): opaque of Store::Handle + %{ + auto rval = new comm::StoreHandleVal(id->CheckString(), comm::StoreType::FRONTEND, + {}, nullptr); + comm_mgr->AddStore(rval); + return rval; + %} + +function Store::close_by_name%(id: string%): bool + %{ + return new Val(comm_mgr->CloseStore(id->CheckString()), TYPE_BOOL); + %} + +function Store::close_by_handle%(h: opaque of Store::Handle%): bool + %{ + auto handle = static_cast(h); + + if ( ! handle->store ) + return new Val(false, TYPE_BOOL); + + return new Val(comm_mgr->CloseStore(handle->store->id()), TYPE_BOOL); + %} + +########################### +# non-blocking update API # +########################### + +function Store::insert%(h: opaque of Store::Handle, + k: Comm::Data, v: Comm::Data, + e: Store::ExpiryTime &default = Store::ExpiryTime()%): bool + %{ + auto handle = static_cast(h); + + if ( ! handle->store ) + return new Val(false, TYPE_BOOL); + + auto& key = comm::opaque_field_to_data(k->AsRecordVal(), frame); + auto& val = comm::opaque_field_to_data(v->AsRecordVal(), frame); + + broker::util::optional expiry; + + auto abs_expiry_val = e->AsRecordVal()->Lookup(0); + auto rel_expiry_val = e->AsRecordVal()->Lookup(1); + + if ( abs_expiry_val ) + { + auto tag = broker::store::expiration_time::tag::absolute; + expiry = broker::store::expiration_time(abs_expiry_val->AsTime(), tag); + } + else if ( rel_expiry_val ) + { + auto tag = broker::store::expiration_time::tag::since_last_modification; + expiry = broker::store::expiration_time(rel_expiry_val->AsInterval(), tag); + } + + handle->store->insert(key, val, expiry); + return new Val(true, TYPE_BOOL); + %} + +function Store::erase%(h: opaque of Store::Handle, k: Comm::Data%): bool + %{ + auto handle = static_cast(h); + + if ( ! handle->store ) + return new Val(false, TYPE_BOOL); + + auto& key = comm::opaque_field_to_data(k->AsRecordVal(), frame); + handle->store->erase(key); + return new Val(true, TYPE_BOOL); + %} + +function Store::clear%(h: opaque of Store::Handle%): bool + %{ + auto handle = static_cast(h); + + if ( ! handle->store ) + return new Val(false, TYPE_BOOL); + + handle->store->clear(); + return new Val(true, TYPE_BOOL); + %} + +function Store::increment%(h: opaque of Store::Handle, + k: Comm::Data, by: int%): bool + %{ + auto handle = static_cast(h); + + if ( ! handle->store ) + return new Val(false, TYPE_BOOL); + + auto& key = comm::opaque_field_to_data(k->AsRecordVal(), frame); + handle->store->increment(key, by); + return new Val(true, TYPE_BOOL); + %} + +function Store::decrement%(h: opaque of Store::Handle, + k: Comm::Data, by: int%): bool + %{ + auto handle = static_cast(h); + + if ( ! handle->store ) + return new Val(false, TYPE_BOOL); + + auto& key = comm::opaque_field_to_data(k->AsRecordVal(), frame); + handle->store->decrement(key, by); + return new Val(true, TYPE_BOOL); + %} + +function Store::add_to_set%(h: opaque of Store::Handle, + k: Comm::Data, element: Comm::Data%): bool + %{ + auto handle = static_cast(h); + + if ( ! handle->store ) + return new Val(false, TYPE_BOOL); + + auto& key = comm::opaque_field_to_data(k->AsRecordVal(), frame); + auto& ele = comm::opaque_field_to_data(element->AsRecordVal(), frame); + handle->store->add_to_set(key, ele); + return new Val(true, TYPE_BOOL); + %} + +function Store::remove_from_set%(h: opaque of Store::Handle, + k: Comm::Data, element: Comm::Data%): bool + %{ + auto handle = static_cast(h); + + if ( ! handle->store ) + return new Val(false, TYPE_BOOL); + + auto& key = comm::opaque_field_to_data(k->AsRecordVal(), frame); + auto& ele = comm::opaque_field_to_data(element->AsRecordVal(), frame); + handle->store->remove_from_set(key, ele); + return new Val(true, TYPE_BOOL); + %} + +function Store::push_left%(h: opaque of Store::Handle, k: Comm::Data, + items: Comm::DataVector%): bool + %{ + auto handle = static_cast(h); + + if ( ! handle->store ) + return new Val(false, TYPE_BOOL); + + auto& key = comm::opaque_field_to_data(k->AsRecordVal(), frame); + broker::vector items_vector; + auto items_vv = items->AsVector(); + + for ( auto i = 0u; i < items_vv->size(); ++i ) + { + auto& item = comm::opaque_field_to_data((*items_vv)[i]->AsRecordVal(), + frame); + items_vector.emplace_back(item); + } + + handle->store->push_left(key, move(items_vector)); + return new Val(true, TYPE_BOOL); + %} + +function Store::push_right%(h: opaque of Store::Handle, k: Comm::Data, + items: Comm::DataVector%): bool + %{ + auto handle = static_cast(h); + + if ( ! handle->store ) + return new Val(false, TYPE_BOOL); + + auto& key = comm::opaque_field_to_data(k->AsRecordVal(), frame); + broker::vector items_vector; + auto items_vv = items->AsVector(); + + for ( auto i = 0u; i < items_vv->size(); ++i ) + { + auto& item = comm::opaque_field_to_data((*items_vv)[i]->AsRecordVal(), + frame); + items_vector.emplace_back(item); + } + + handle->store->push_right(key, move(items_vector)); + return new Val(true, TYPE_BOOL); + %} + +########################## +# non-blocking query API # +########################## + +%%{ +static bool prepare_for_query(Val* opaque, Frame* frame, + comm::StoreHandleVal** handle, + double* timeout, + comm::StoreQueryCallback** cb) + { + *handle = static_cast(opaque); + + if ( ! (*handle)->store ) + return false; + + Trigger* trigger = frame->GetTrigger(); + + if ( ! trigger ) + { + reporter->PushLocation(frame->GetCall()->GetLocationInfo()); + reporter->Error("Store queries can only be called inside when-condition"); + reporter->PopLocation(); + return false; + } + + *timeout = trigger->TimeoutValue(); + + if ( *timeout < 0 ) + { + reporter->PushLocation(frame->GetCall()->GetLocationInfo()); + reporter->Error("Store queries must specify a timeout block"); + reporter->PopLocation(); + return false; + } + + frame->SetDelayed(); + trigger->Hold(); + *cb = new comm::StoreQueryCallback(trigger, frame->GetCall(), + (*handle)->store->id()); + comm_mgr->TrackStoreQuery(*cb); + return true; + } + +%%} + +function Store::pop_left%(h: opaque of Store::Handle, + k: Comm::Data%): Store::QueryResult + %{ + double timeout; + comm::StoreQueryCallback* cb; + comm::StoreHandleVal* handle; + + if ( ! prepare_for_query(h, frame, &handle, &timeout, &cb) ) + return comm::query_result(); + + Val* key = k->AsRecordVal()->Lookup(0); + + if ( ! key ) + return comm::query_result(); + + handle->store->pop_left(static_cast(key)->data, + std::chrono::duration(timeout), cb); + return 0; + %} + +function Store::pop_right%(h: opaque of Store::Handle, + k: Comm::Data%): Store::QueryResult + %{ + double timeout; + comm::StoreQueryCallback* cb; + comm::StoreHandleVal* handle; + + if ( ! prepare_for_query(h, frame, &handle, &timeout, &cb) ) + return comm::query_result(); + + Val* key = k->AsRecordVal()->Lookup(0); + + if ( ! key ) + return comm::query_result(); + + handle->store->pop_right(static_cast(key)->data, + std::chrono::duration(timeout), cb); + return 0; + %} + +function Store::lookup%(h: opaque of Store::Handle, + k: Comm::Data%): Store::QueryResult + %{ + double timeout; + comm::StoreQueryCallback* cb; + comm::StoreHandleVal* handle; + + if ( ! prepare_for_query(h, frame, &handle, &timeout, &cb) ) + return comm::query_result(); + + Val* key = k->AsRecordVal()->Lookup(0); + + if ( ! key ) + return comm::query_result(); + + handle->store->lookup(static_cast(key)->data, + std::chrono::duration(timeout), cb); + return 0; + %} + +function Store::exists%(h: opaque of Store::Handle, + k: Comm::Data%): Store::QueryResult + %{ + double timeout; + comm::StoreQueryCallback* cb; + comm::StoreHandleVal* handle; + + if ( ! prepare_for_query(h, frame, &handle, &timeout, &cb) ) + return comm::query_result(); + + Val* key = k->AsRecordVal()->Lookup(0); + + if ( ! key ) + return comm::query_result(); + + handle->store->exists(static_cast(key)->data, + std::chrono::duration(timeout), cb); + return 0; + %} + +function Store::keys%(h: opaque of Store::Handle%): Store::QueryResult + %{ + double timeout; + comm::StoreQueryCallback* cb; + comm::StoreHandleVal* handle; + + if ( ! prepare_for_query(h, frame, &handle, &timeout, &cb) ) + return comm::query_result(); + + handle->store->keys(std::chrono::duration(timeout), cb); + return 0; + %} + +function Store::size%(h: opaque of Store::Handle%): Store::QueryResult + %{ + double timeout; + comm::StoreQueryCallback* cb; + comm::StoreHandleVal* handle; + + if ( ! prepare_for_query(h, frame, &handle, &timeout, &cb) ) + return comm::query_result(); + + handle->store->size(std::chrono::duration(timeout), cb); + return 0; + %} From 21c7642f6215960e1e9faf65d581e41dacb8de7c Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Mon, 2 Feb 2015 11:14:24 -0600 Subject: [PATCH 101/299] Fix memory leak in new split_string* functions. --- CHANGES | 4 ++++ VERSION | 2 +- src/strings.bif | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index f7f564b290..2d4ce98c31 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,8 @@ +2.3-405 | 2015-02-02 11:14:24 -0600 + + * Fix memory leak in new split_string* functions. (Jon Siwek) + 2.3-404 | 2015-01-30 14:23:27 -0800 * Update documentation (broken links, outdated tests). (Jon Siwek) diff --git a/VERSION b/VERSION index a47da723dd..57d75d10d0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3-404 +2.3-405 diff --git a/src/strings.bif b/src/strings.bif index 84d6014cff..b8d21cb04a 100644 --- a/src/strings.bif +++ b/src/strings.bif @@ -341,7 +341,7 @@ static int match_prefix(int s_len, const char* s, int t_len, const char* t) VectorVal* do_split_string(StringVal* str_val, RE_Matcher* re, int incl_sep, int max_num_sep) { - VectorVal* rval = new VectorVal(new VectorType(base_type(TYPE_STRING))); + VectorVal* rval = new VectorVal(string_vec); const u_char* s = str_val->Bytes(); int n = str_val->Len(); const u_char* end_of_s = s + n; From 05a865a907d79b6617dcbb72da3d423636426b82 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Mon, 2 Feb 2015 14:56:28 -0600 Subject: [PATCH 102/299] broker integration: add master data store unti test And fix bug w/ looking up nonexistent keys -- the resulting value data should be "null" not "false". --- src/comm/Manager.cc | 14 +- src/comm/store.bif | 4 +- testing/btest/Baseline/comm.master_store/out | 14 ++ testing/btest/comm/master_store.bro | 141 +++++++++++++++++++ 4 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 testing/btest/Baseline/comm.master_store/out create mode 100644 testing/btest/comm/master_store.bro diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index 9f17878cf6..0b887d4f37 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -408,10 +408,20 @@ double comm::Manager::NextTimestamp(double* local_network_time) struct response_converter { using result_type = RecordVal*; + broker::store::query::tag query_tag; result_type operator()(bool d) { - return comm::make_data_val(broker::data{d}); + switch ( query_tag ) { + case broker::store::query::tag::pop_left: + case broker::store::query::tag::pop_right: + case broker::store::query::tag::lookup: + // A boolean result means the key doesn't exist (if it did, then + // the result would contain the broker::data value, not a bool). + return new RecordVal(BifType::Record::Comm::Data); + default: + return comm::make_data_val(broker::data{d}); + } } result_type operator()(uint64_t d) @@ -446,7 +456,7 @@ struct response_converter { static RecordVal* response_to_val(broker::store::response r) { - return broker::visit(response_converter{}, r.reply.value); + return broker::visit(response_converter{r.request.type}, r.reply.value); } void comm::Manager::Process() diff --git a/src/comm/store.bif b/src/comm/store.bif index fb4c8d57ce..176e55268e 100644 --- a/src/comm/store.bif +++ b/src/comm/store.bif @@ -127,7 +127,7 @@ function Store::clear%(h: opaque of Store::Handle%): bool %} function Store::increment%(h: opaque of Store::Handle, - k: Comm::Data, by: int%): bool + k: Comm::Data, by: int &default = +1%): bool %{ auto handle = static_cast(h); @@ -140,7 +140,7 @@ function Store::increment%(h: opaque of Store::Handle, %} function Store::decrement%(h: opaque of Store::Handle, - k: Comm::Data, by: int%): bool + k: Comm::Data, by: int &default = +1%): bool %{ auto handle = static_cast(h); diff --git a/testing/btest/Baseline/comm.master_store/out b/testing/btest/Baseline/comm.master_store/out new file mode 100644 index 0000000000..defdc9a3e1 --- /dev/null +++ b/testing/btest/Baseline/comm.master_store/out @@ -0,0 +1,14 @@ +lookup(two): [status=Store::SUCCESS, result=[d=broker::data{222}]] +lookup(four): [status=Store::SUCCESS, result=[d=]] +lookup(myset): [status=Store::SUCCESS, result=[d=broker::data{{a, c, d}}]] +lookup(one): [status=Store::SUCCESS, result=[d=broker::data{111}]] +lookup(myvec): [status=Store::SUCCESS, result=[d=broker::data{[delta, alpha, beta, gamma, omega]}]] +exists(one): [status=Store::SUCCESS, result=[d=broker::data{1}]] +exists(two): [status=Store::SUCCESS, result=[d=broker::data{0}]] +exists(myset): [status=Store::SUCCESS, result=[d=broker::data{1}]] +exists(four): [status=Store::SUCCESS, result=[d=broker::data{0}]] +pop_right(myvec): [status=Store::SUCCESS, result=[d=broker::data{omega}]] +pop_left(myvec): [status=Store::SUCCESS, result=[d=broker::data{delta}]] +keys: [status=Store::SUCCESS, result=[d=broker::data{[myvec, myset, one]}]] +size: [status=Store::SUCCESS, result=[d=broker::data{3}]] +size (after clear): [status=Store::SUCCESS, result=[d=broker::data{0}]] diff --git a/testing/btest/comm/master_store.bro b/testing/btest/comm/master_store.bro new file mode 100644 index 0000000000..84b4ee07a1 --- /dev/null +++ b/testing/btest/comm/master_store.bro @@ -0,0 +1,141 @@ +# @TEST-EXEC: bro -b %INPUT >out +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-sort btest-diff out + +redef exit_only_after_terminate = T; + +global h: opaque of Store::Handle; +global lookup_count = 0; +const lookup_expect_count = 5; +global exists_count = 0; +const exists_expect_count = 4; +global pop_count = 0; +const pop_expect_count = 2; + +global test_size: event(where: string &default = ""); + +event test_clear() + { + Store::clear(h); + event test_size("after clear"); + } + +event test_size(where: string) + { + when ( local res = Store::size(h) ) + { + if ( where == "" ) + { + print fmt("size: %s", res); + event test_clear(); + } + else + { + print fmt("size (%s): %s", where, res); + terminate(); + } + } + timeout 10sec + { print "timeout"; } + } + +event test_keys() + { + when ( local res = Store::keys(h) ) + { + print fmt("keys: %s", res); + event test_size(); + } + timeout 10sec + { print "timeout"; } + } + +event test_pop(key: string) + { + when ( local lres = Store::pop_left(h, Comm::data(key)) ) + { + print fmt("pop_left(%s): %s", key, lres); + ++pop_count; + + if ( pop_count == pop_expect_count ) + event test_keys(); + } + timeout 10sec + { print "timeout"; } + + when ( local rres = Store::pop_right(h, Comm::data(key)) ) + { + print fmt("pop_right(%s): %s", key, rres); + ++pop_count; + + if ( pop_count == pop_expect_count ) + event test_keys(); + } + timeout 10sec + { print "timeout"; } + } + +function do_exists(key: string) + { + when ( local res = Store::exists(h, Comm::data(key)) ) + { + print fmt("exists(%s): %s", key, res); + ++exists_count; + + if ( exists_count == exists_expect_count ) + event test_pop("myvec"); + } + timeout 10sec + { print "timeout"; } + } + +event test_erase() + { + Store::erase(h, Comm::data("two")); + do_exists("one"); + do_exists("two"); + do_exists("myset"); + do_exists("four"); + } + +function do_lookup(key: string) + { + when ( local res = Store::lookup(h, Comm::data(key)) ) + { + print fmt("lookup(%s): %s", key, res); + ++lookup_count; + + if ( lookup_count == lookup_expect_count ) + event test_erase(); + } + timeout 10sec + { print "timeout"; } + } + +function dv(d: Comm::Data): Comm::DataVector + { + local rval: Comm::DataVector; + rval[0] = d; + return rval; + } + +event bro_init() + { + local myset: set[string] = {"a", "b", "c"}; + local myvec: vector of string = {"alpha", "beta", "gamma"}; + h = Store::create_master("master"); + Store::insert(h, Comm::data("one"), Comm::data(110)); + Store::insert(h, Comm::data("two"), Comm::data(223)); + Store::insert(h, Comm::data("myset"), Comm::data(myset)); + Store::insert(h, Comm::data("myvec"), Comm::data(myvec)); + Store::increment(h, Comm::data("one")); + Store::decrement(h, Comm::data("two")); + Store::add_to_set(h, Comm::data("myset"), Comm::data("d")); + Store::remove_from_set(h, Comm::data("myset"), Comm::data("b")); + Store::push_left(h, Comm::data("myvec"), dv(Comm::data("delta"))); + Store::push_right(h, Comm::data("myvec"), dv(Comm::data("omega"))); + do_lookup("one"); + do_lookup("two"); + do_lookup("myset"); + do_lookup("four"); + do_lookup("myvec"); + } From 441c46df76e3a7e0e50877d66d5c9f7dee081ce9 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 3 Feb 2015 11:09:39 -0600 Subject: [PATCH 103/299] broker integration: add unit test for store clones --- aux/broker | 2 +- src/comm/Data.h | 1 + src/comm/Manager.cc | 29 ++++- src/comm/Manager.h | 7 +- src/comm/Store.h | 13 +- src/comm/store.bif | 64 +++++++--- .../Baseline/comm.clone_store/clone.clone.out | 5 + .../comm.clone_store/master.master.out | 0 testing/btest/comm/clone_store.bro | 114 ++++++++++++++++++ 9 files changed, 205 insertions(+), 30 deletions(-) create mode 100644 testing/btest/Baseline/comm.clone_store/clone.clone.out create mode 100644 testing/btest/Baseline/comm.clone_store/master.master.out create mode 100644 testing/btest/comm/clone_store.bro diff --git a/aux/broker b/aux/broker index 177bdfac2c..c217119d9a 160000 --- a/aux/broker +++ b/aux/broker @@ -1 +1 @@ -Subproject commit 177bdfac2c768d9ed8f3edb10e9e2dbd0d6f8723 +Subproject commit c217119d9a484da941161d182cdc0a1f86a0d40f diff --git a/src/comm/Data.h b/src/comm/Data.h index c720dcda71..da10853127 100644 --- a/src/comm/Data.h +++ b/src/comm/Data.h @@ -27,6 +27,7 @@ broker::util::optional val_to_data(Val* v); Val* data_to_val(broker::data d, BroType* type); +// TODO: actually need to implement Bro's serialization to support copying vals class DataVal : public OpaqueVal { public: diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index 0b887d4f37..3d6aad4d1e 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -23,7 +23,7 @@ int comm::Manager::send_flags_unsolicited_idx; comm::Manager::~Manager() { for ( auto& s : data_stores ) - CloseStore(s.first); + CloseStore(s.first.first, s.first.second); } bool comm::Manager::InitPreScript() @@ -741,17 +741,34 @@ bool comm::Manager::AddStore(StoreHandleVal* handle) if ( ! handle->store ) return false; - if ( data_stores.find(handle->store->id()) != data_stores.end() ) + auto key = make_pair(handle->store->id(), handle->store_type); + + if ( data_stores.find(key) != data_stores.end() ) return false; - data_stores[handle->store->id()] = handle; + data_stores[key] = handle; Ref(handle); return true; } -bool comm::Manager::CloseStore(const broker::store::identifier& id) +comm::StoreHandleVal* +comm::Manager::LookupStore(const broker::store::identifier& id, + comm::StoreType type) { - auto it = data_stores.find(id); + auto key = make_pair(id, type); + auto it = data_stores.find(key); + + if ( it == data_stores.end() ) + return nullptr; + + return it->second; + } + +bool comm::Manager::CloseStore(const broker::store::identifier& id, + StoreType type) + { + auto key = make_pair(id, type); + auto it = data_stores.find(key); if ( it == data_stores.end() ) return false; @@ -760,7 +777,7 @@ bool comm::Manager::CloseStore(const broker::store::identifier& id) { auto query = *it; - if ( query->StoreID() == id ) + if ( query->GetStoreType() == type && query->StoreID() == id ) { it = pending_queries.erase(it); query->Abort(); diff --git a/src/comm/Manager.h b/src/comm/Manager.h index c9cc2c8464..31fdfa56c1 100644 --- a/src/comm/Manager.h +++ b/src/comm/Manager.h @@ -63,7 +63,9 @@ public: bool AddStore(StoreHandleVal* handle); - bool CloseStore(const broker::store::identifier& id); + StoreHandleVal* LookupStore(const broker::store::identifier& id, StoreType type); + + bool CloseStore(const broker::store::identifier& id, StoreType type); bool TrackStoreQuery(StoreQueryCallback* cb); @@ -91,7 +93,8 @@ private: std::map event_subscriptions; std::map log_subscriptions; - std::map data_stores; + std::map, + StoreHandleVal*> data_stores; std::unordered_set pending_queries; static VectorType* vector_of_data_type; diff --git a/src/comm/Store.h b/src/comm/Store.h index b3a8ccb339..3183afbbb3 100644 --- a/src/comm/Store.h +++ b/src/comm/Store.h @@ -56,8 +56,10 @@ class StoreQueryCallback { public: StoreQueryCallback(Trigger* arg_trigger, const CallExpr* arg_call, - broker::store::identifier arg_store_id) - : trigger(arg_trigger), call(arg_call), store_id(move(arg_store_id)) + broker::store::identifier arg_store_id, + StoreType arg_store_type) + : trigger(arg_trigger), call(arg_call), store_id(move(arg_store_id)), + store_type(arg_store_type) { Ref(trigger); } @@ -85,13 +87,20 @@ public: const broker::store::identifier& StoreID() const { return store_id; } + StoreType GetStoreType() const + { return store_type; } + private: Trigger* trigger; const CallExpr* call; broker::store::identifier store_id; + StoreType store_type; }; +// TODO: actually need to implement Bro's serialization to support copying vals +// but doesn't make sense to "copy" a master data store, so assert we can +// lookup a store by pair locally (i.e. shouldn't send handles remotely). class StoreHandleVal : public OpaqueVal { public: diff --git a/src/comm/store.bif b/src/comm/store.bif index 176e55268e..7d09704d31 100644 --- a/src/comm/store.bif +++ b/src/comm/store.bif @@ -25,10 +25,20 @@ enum BackendType %{ function Store::create_master%(id: string, b: BackendType &default = MEMORY, options: BackendOptions &default = BackendOptions()%): opaque of Store::Handle %{ - auto rval = new comm::StoreHandleVal(id->CheckString(), comm::StoreType::MASTER, - static_cast(b->AsEnum()), - options->AsRecordVal()); - comm_mgr->AddStore(rval); + auto id_str = id->CheckString(); + auto type = comm::StoreType::MASTER; + auto rval = comm_mgr->LookupStore(id_str, type); + + if ( rval ) + { + Ref(rval); + return rval; + } + + rval = new comm::StoreHandleVal(id_str, type, + static_cast(b->AsEnum()), + options->AsRecordVal()); + assert(comm_mgr->AddStore(rval)); return rval; %} @@ -36,25 +46,39 @@ function Store::create_clone%(id: string, b: BackendType &default = MEMORY, options: BackendOptions &default = BackendOptions(), resync: interval &default = 1sec%): opaque of Store::Handle %{ - auto rval = new comm::StoreHandleVal(id->CheckString(), comm::StoreType::CLONE, - static_cast(b->AsEnum()), - options->AsRecordVal(), - std::chrono::duration(resync)); - comm_mgr->AddStore(rval); + auto id_str = id->CheckString(); + auto type = comm::StoreType::CLONE; + auto rval = comm_mgr->LookupStore(id_str, type); + + if ( rval ) + { + Ref(rval); + return rval; + } + + rval = new comm::StoreHandleVal(id_str, type, + static_cast(b->AsEnum()), + options->AsRecordVal(), + std::chrono::duration(resync)); + assert(comm_mgr->AddStore(rval)); return rval; %} function Store::create_frontend%(id: string%): opaque of Store::Handle %{ - auto rval = new comm::StoreHandleVal(id->CheckString(), comm::StoreType::FRONTEND, - {}, nullptr); - comm_mgr->AddStore(rval); - return rval; - %} + auto id_str = id->CheckString(); + auto type = comm::StoreType::FRONTEND; + auto rval = comm_mgr->LookupStore(id_str, type); -function Store::close_by_name%(id: string%): bool - %{ - return new Val(comm_mgr->CloseStore(id->CheckString()), TYPE_BOOL); + if ( rval ) + { + Ref(rval); + return rval; + } + + rval = new comm::StoreHandleVal(id_str, type, {}, nullptr); + assert(comm_mgr->AddStore(rval)); + return rval; %} function Store::close_by_handle%(h: opaque of Store::Handle%): bool @@ -64,7 +88,8 @@ function Store::close_by_handle%(h: opaque of Store::Handle%): bool if ( ! handle->store ) return new Val(false, TYPE_BOOL); - return new Val(comm_mgr->CloseStore(handle->store->id()), TYPE_BOOL); + return new Val(comm_mgr->CloseStore(handle->store->id(), + handle->store_type), TYPE_BOOL); %} ########################### @@ -264,7 +289,8 @@ static bool prepare_for_query(Val* opaque, Frame* frame, frame->SetDelayed(); trigger->Hold(); *cb = new comm::StoreQueryCallback(trigger, frame->GetCall(), - (*handle)->store->id()); + (*handle)->store->id(), + (*handle)->store_type); comm_mgr->TrackStoreQuery(*cb); return true; } diff --git a/testing/btest/Baseline/comm.clone_store/clone.clone.out b/testing/btest/Baseline/comm.clone_store/clone.clone.out new file mode 100644 index 0000000000..8a7c89a19b --- /dev/null +++ b/testing/btest/Baseline/comm.clone_store/clone.clone.out @@ -0,0 +1,5 @@ +clone keys, [status=Store::SUCCESS, result=[d=broker::data{[one, two, myset, myvec]}]] +lookup, one, [status=Store::SUCCESS, result=[d=broker::data{111}]] +lookup, two, [status=Store::SUCCESS, result=[d=broker::data{222}]] +lookup, myset, [status=Store::SUCCESS, result=[d=broker::data{{a, c, d}}]] +lookup, myvec, [status=Store::SUCCESS, result=[d=broker::data{[delta, alpha, beta, gamma, omega]}]] diff --git a/testing/btest/Baseline/comm.clone_store/master.master.out b/testing/btest/Baseline/comm.clone_store/master.master.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testing/btest/comm/clone_store.bro b/testing/btest/comm/clone_store.bro new file mode 100644 index 0000000000..03e0fe172f --- /dev/null +++ b/testing/btest/comm/clone_store.bro @@ -0,0 +1,114 @@ +# @TEST_SERIALIZE: brokercomm +# @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt + +# @TEST-EXEC: btest-bg-run clone "bro -b ../clone.bro >clone.out" +# @TEST-EXEC: btest-bg-run master "bro -b ../master.bro >master.out" + +# @TEST-EXEC: btest-bg-wait 20 +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-sort btest-diff clone/clone.out +# @TEST-EXEC: btest-diff master/master.out + +@TEST-START-FILE clone.bro + +redef exit_only_after_terminate = T; + +global h: opaque of Store::Handle; +global expected_key_count = 4; +global key_count = 0; + +event done() + { + terminate(); + } + +function do_lookup(key: string) + { + when ( local res = Store::lookup(h, Comm::data(key)) ) + { + ++key_count; + print "lookup", key, res; + + if ( key_count == expected_key_count ) + event done(); + } + timeout 10sec + { print "timeout"; } + } + +event ready() + { + h = Store::create_clone("mystore"); + + when ( local res = Store::keys(h) ) + { + print "clone keys", res; + do_lookup(Comm::refine_to_string(Comm::vector_lookup(res$result, 0))); + do_lookup(Comm::refine_to_string(Comm::vector_lookup(res$result, 1))); + do_lookup(Comm::refine_to_string(Comm::vector_lookup(res$result, 2))); + do_lookup(Comm::refine_to_string(Comm::vector_lookup(res$result, 3))); + } + timeout 10sec + { print "timeout"; } + } + +event bro_init() + { + Comm::listen(9999/tcp, "127.0.0.1"); + Comm::subscribe_to_events("bro/event/ready"); + Comm::auto_event("bro/event/done", done); + } + +@TEST-END-FILE + +@TEST-START-FILE master.bro + +redef exit_only_after_terminate = T; + +global h: opaque of Store::Handle; + +function dv(d: Comm::Data): Comm::DataVector + { + local rval: Comm::DataVector; + rval[0] = d; + return rval; + } + +global ready: event(); + +event done() + { + terminate(); + } + +event Comm::remote_connection_established(peer_address: string, + peer_port: port, + peer_name: string) + { + local myset: set[string] = {"a", "b", "c"}; + local myvec: vector of string = {"alpha", "beta", "gamma"}; + h = Store::create_master("mystore"); + Store::insert(h, Comm::data("one"), Comm::data(110)); + Store::insert(h, Comm::data("two"), Comm::data(223)); + Store::insert(h, Comm::data("myset"), Comm::data(myset)); + Store::insert(h, Comm::data("myvec"), Comm::data(myvec)); + Store::increment(h, Comm::data("one")); + Store::decrement(h, Comm::data("two")); + Store::add_to_set(h, Comm::data("myset"), Comm::data("d")); + Store::remove_from_set(h, Comm::data("myset"), Comm::data("b")); + Store::push_left(h, Comm::data("myvec"), dv(Comm::data("delta"))); + Store::push_right(h, Comm::data("myvec"), dv(Comm::data("omega"))); + + when ( local res = Store::size(h) ) + { event ready(); } + timeout 10sec + { print "timeout"; } + } + +event bro_init() + { + Comm::connect("127.0.0.1", 9999/tcp, 1secs); + Comm::auto_event("bro/event/ready", ready); + Comm::subscribe_to_events("bro/event/done"); + } + +@TEST-END-FILE From 51203d71934436c7f30922dfc6e0f8d4800a9f62 Mon Sep 17 00:00:00 2001 From: Mike Smiley Date: Tue, 3 Feb 2015 14:29:34 -0500 Subject: [PATCH 104/299] "id" not defined for debug code "id" not defined for debug code when using -DPROFILE_BRO_FUNCTIONS --- src/Func.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Func.cc b/src/Func.cc index d66e9c71fa..693a4535d4 100644 --- a/src/Func.cc +++ b/src/Func.cc @@ -323,7 +323,7 @@ int BroFunc::IsPure() const Val* BroFunc::Call(val_list* args, Frame* parent) const { #ifdef PROFILE_BRO_FUNCTIONS - DEBUG_MSG("Function: %s\n", id->Name()); + DEBUG_MSG("Function: %s\n", Name()); #endif SegmentProfiler(segment_logger, location); From bb9e6583e0b113aa82553c4f55341034d61d87a2 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 3 Feb 2015 13:54:40 -0600 Subject: [PATCH 105/299] broker integration: Comm::Data/Store::Handle opaque serialization For now, this is needed when locally cloning Vals. E.g. "when" statements will clone an entire frame and data store queries use "when" statements, so it's likely there will be locals of these opaque types that get cloned. --- src/SerialTypes.h | 2 + src/comm/CMakeLists.txt | 3 ++ src/comm/Data.cc | 34 +++++++++++++++++ src/comm/Data.h | 8 +++- src/comm/Manager.cc | 1 + src/comm/Store.cc | 82 +++++++++++++++++++++++++++++++++++++---- src/comm/Store.h | 12 ++++-- 7 files changed, 130 insertions(+), 12 deletions(-) diff --git a/src/SerialTypes.h b/src/SerialTypes.h index d2f227838c..4e6bbb11ac 100644 --- a/src/SerialTypes.h +++ b/src/SerialTypes.h @@ -113,6 +113,8 @@ SERIAL_VAL(TOPK_VAL, 20) SERIAL_VAL(BLOOMFILTER_VAL, 21) SERIAL_VAL(CARDINALITY_VAL, 22) SERIAL_VAL(X509_VAL, 23) +SERIAL_VAL(COMM_STORE_HANDLE_VAL, 24) +SERIAL_VAL(COMM_DATA_VAL, 25) #define SERIAL_EXPR(name, val) SERIAL_CONST(name, val, EXPR) SERIAL_EXPR(EXPR, 1) diff --git a/src/comm/CMakeLists.txt b/src/comm/CMakeLists.txt index da726e54d6..6453e006bf 100644 --- a/src/comm/CMakeLists.txt +++ b/src/comm/CMakeLists.txt @@ -10,6 +10,9 @@ if ( ROCKSDB_INCLUDE_DIR ) include_directories(BEFORE ${ROCKSDB_INCLUDE_DIR}) endif () +include_directories(BEFORE ${LIBCAF_INCLUDE_DIR_CORE}) +include_directories(BEFORE ${LIBCAF_INCLUDE_DIR_IO}) + set(comm_SRCS Data.cc Manager.cc diff --git a/src/comm/Data.cc b/src/comm/Data.cc index 3b1a240988..0ea7666f9e 100644 --- a/src/comm/Data.cc +++ b/src/comm/Data.cc @@ -1,5 +1,7 @@ #include "Data.h" #include "comm/data.bif.h" +#include +#include using namespace std; @@ -663,3 +665,35 @@ broker::data& comm::opaque_field_to_data(RecordVal* v, Frame* f) return static_cast(d)->data; } + +IMPLEMENT_SERIAL(comm::DataVal, SER_COMM_DATA_VAL); + +bool comm::DataVal::DoSerialize(SerialInfo* info) const + { + DO_SERIALIZE(SER_COMM_DATA_VAL, OpaqueVal); + + std::string serial; + caf::binary_serializer bs(std::back_inserter(serial)); + bs << data; + + if ( ! SERIALIZE_STR(serial.data(), serial.size()) ) + return false; + + return true; + } + +bool comm::DataVal::DoUnserialize(UnserialInfo* info) + { + DO_UNSERIALIZE(OpaqueVal); + + const char* serial; + int len; + + if ( ! UNSERIALIZE_STR(&serial, &len) ) + return false; + + caf::binary_deserializer bd(serial, len); + caf::uniform_typeid()->deserialize(&data, &bd); + delete [] serial; + return true; + } diff --git a/src/comm/Data.h b/src/comm/Data.h index da10853127..ed3c16f677 100644 --- a/src/comm/Data.h +++ b/src/comm/Data.h @@ -27,7 +27,6 @@ broker::util::optional val_to_data(Val* v); Val* data_to_val(broker::data d, BroType* type); -// TODO: actually need to implement Bro's serialization to support copying vals class DataVal : public OpaqueVal { public: @@ -42,7 +41,14 @@ public: d->Add("}"); } + DECLARE_SERIAL(DataVal); + broker::data data; + +protected: + + DataVal() + {} }; struct type_name_getter { diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index 3d6aad4d1e..cfce84a1c9 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -787,6 +787,7 @@ bool comm::Manager::CloseStore(const broker::store::identifier& id, ++it; } + delete it->second->store; it->second->store = nullptr; Unref(it->second); return true; diff --git a/src/comm/Store.cc b/src/comm/Store.cc index 0d94795ce8..8c55c31785 100644 --- a/src/comm/Store.cc +++ b/src/comm/Store.cc @@ -78,17 +78,15 @@ comm::StoreHandleVal::StoreHandleVal(broker::store::identifier id, switch ( store_type ) { case StoreType::FRONTEND: - store.reset(new broker::store::frontend(comm_mgr->Endpoint(), - move(id))); + store = new broker::store::frontend(comm_mgr->Endpoint(), move(id)); break; case StoreType::MASTER: - store.reset(new broker::store::master(comm_mgr->Endpoint(), - move(id), move(backend))); + store = new broker::store::master(comm_mgr->Endpoint(), move(id), + move(backend)); break; case StoreType::CLONE: - store.reset(new broker::store::clone(comm_mgr->Endpoint(), - move(id), resync, - move(backend))); + store = new broker::store::clone(comm_mgr->Endpoint(), move(id), resync, + move(backend)); break; default: reporter->FatalError("unknown data store type: %d", @@ -139,3 +137,73 @@ void comm::StoreHandleVal::ValDescribe(ODesc* d) const d->Add("}"); } + +IMPLEMENT_SERIAL(comm::StoreHandleVal, SER_COMM_STORE_HANDLE_VAL); + +bool comm::StoreHandleVal::DoSerialize(SerialInfo* info) const + { + DO_SERIALIZE(SER_COMM_STORE_HANDLE_VAL, OpaqueVal); + + bool have_store = store != nullptr; + + if ( ! SERIALIZE(have_store) ) + return false; + + if ( ! have_store ) + return true; + + if ( ! SERIALIZE(static_cast(store_type)) ) + return false; + + if ( ! SERIALIZE_STR(store->id().data(), store->id().size()) ) + return false; + + return true; + } + +bool comm::StoreHandleVal::DoUnserialize(UnserialInfo* info) + { + DO_UNSERIALIZE(OpaqueVal); + + bool have_store; + + if ( ! UNSERIALIZE(&have_store) ) + return false; + + if ( ! have_store ) + { + store = nullptr; + return true; + } + + int type; + + if ( ! UNSERIALIZE(&type) ) + return false; + + const char* id_str; + int len; + + if ( ! UNSERIALIZE_STR(&id_str, &len) ) + return false; + + broker::store::identifier id(id_str, len); + delete [] id_str; + + auto handle = comm_mgr->LookupStore(id, static_cast(type)); + + if ( ! handle ) + { + // Passing serialized version of store handles to other Bro processes + // doesn't make sense, only allow local clones of the handle val. + reporter->Error("failed to look up unserialized store handle %s, %d", + id.data(), type); + store = nullptr; + return false; + } + + store = handle->store; + store_type = handle->store_type; + backend_type = handle->backend_type; + return true; + } diff --git a/src/comm/Store.h b/src/comm/Store.h index 3183afbbb3..b02c5b4f5b 100644 --- a/src/comm/Store.h +++ b/src/comm/Store.h @@ -98,9 +98,6 @@ private: StoreType store_type; }; -// TODO: actually need to implement Bro's serialization to support copying vals -// but doesn't make sense to "copy" a master data store, so assert we can -// lookup a store by pair locally (i.e. shouldn't send handles remotely). class StoreHandleVal : public OpaqueVal { public: @@ -112,9 +109,16 @@ public: void ValDescribe(ODesc* d) const override; - std::unique_ptr store; + DECLARE_SERIAL(StoreHandleVal); + + broker::store::frontend* store; comm::StoreType store_type; broker::util::optional backend_type; + +protected: + + StoreHandleVal() + {} }; } // namespace comm From 0cf982f1d1f9d4db458b32fdba5d068bb358c115 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 3 Feb 2015 15:11:16 -0600 Subject: [PATCH 106/299] broker integration: process debug/diagnostic reports from broker --- aux/broker | 2 +- src/DebugLogger.cc | 2 +- src/DebugLogger.h | 1 + src/comm/Manager.cc | 59 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/aux/broker b/aux/broker index c217119d9a..0760c6808c 160000 --- a/aux/broker +++ b/aux/broker @@ -1 +1 @@ -Subproject commit c217119d9a484da941161d182cdc0a1f86a0d40f +Subproject commit 0760c6808c1d035b7e9f484daefe8ba0a3d6ee13 diff --git a/src/DebugLogger.cc b/src/DebugLogger.cc index 6f025e3c2b..3ce5d92888 100644 --- a/src/DebugLogger.cc +++ b/src/DebugLogger.cc @@ -19,7 +19,7 @@ DebugLogger::Stream DebugLogger::streams[NUM_DBGS] = { { "logging", 0, false }, {"input", 0, false }, { "threading", 0, false }, { "file_analysis", 0, false }, { "plugins", 0, false }, { "broxygen", 0, false }, - { "pktio", 0, false} + { "pktio", 0, false }, { "broker", 0, false } }; DebugLogger::DebugLogger(const char* filename) diff --git a/src/DebugLogger.h b/src/DebugLogger.h index 9cd09dada1..13124657e7 100644 --- a/src/DebugLogger.h +++ b/src/DebugLogger.h @@ -32,6 +32,7 @@ enum DebugStream { DBG_PLUGINS, // Plugin system DBG_BROXYGEN, // Broxygen DBG_PKTIO, // Packet sources and dumpers. + DBG_BROKER, // Broker communication NUM_DBGS // Has to be last }; diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index cfce84a1c9..443c5f90da 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -2,6 +2,7 @@ #include "Data.h" #include "Store.h" #include +#include #include #include #include "util.h" @@ -11,6 +12,7 @@ #include "comm/messaging.bif.h" #include "comm/store.bif.h" #include "logging/Manager.h" +#include "DebugLogger.h" using namespace std; @@ -67,6 +69,15 @@ bool comm::Manager::InitPostScript() return false; } + res = broker::report::init(true); + + if ( res ) + { + fprintf(stderr, "broker::report::init failed: %s\n", + broker::strerror(res)); + return false; + } + const char* name; auto name_from_script = internal_val("Comm::endpoint_name")->AsString(); @@ -398,6 +409,8 @@ void comm::Manager::GetFds(iosource::FD_Set* read, iosource::FD_Set* write, for ( const auto& s : data_stores ) read->Insert(s.second->store->responses().fd()); + + read->Insert(broker::report::default_queue->fd()); } double comm::Manager::NextTimestamp(double* local_network_time) @@ -733,6 +746,52 @@ void comm::Manager::Process() } } + auto reports = broker::report::default_queue->want_pop(); + + if ( ! reports.empty() ) + { + idle = false; + + for ( auto& report : reports ) + { + if ( report.size() < 2 ) + { + reporter->Warning("got broker report msg of size %zu, expect 4", + report.size()); + continue; + } + + uint64_t* level = broker::get(report[1]); + + if ( ! level ) + { + reporter->Warning("got broker report msg w/ bad level type: %d", + static_cast(broker::which(report[1]))); + continue; + } + + auto lvl = static_cast(*level); + + switch ( lvl ) { + case broker::report::level::debug: + DBG_LOG(DBG_BROKER, broker::to_string(report).data()); + break; + case broker::report::level::info: + reporter->Info("broker info: %s", + broker::to_string(report).data()); + break; + case broker::report::level::warn: + reporter->Warning("broker warning: %s", + broker::to_string(report).data()); + break; + case broker::report::level::error: + reporter->Error("broker error: %s", + broker::to_string(report).data()); + break; + } + } + } + SetIdle(idle); } From 4dfec041352298b32d857f715b04a8be722bf89b Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 3 Feb 2015 16:38:56 -0600 Subject: [PATCH 107/299] broker integration: add Comm::enable function Works like old enable_communication(), but for new broker communication mechanism. Scripts have to explicitly call this if they want to use the broker communication functionality. Saves a decent chunk of Bros' initialization time when one doesn't need communication features. --- src/Net.cc | 13 ++++- src/comm/CMakeLists.txt | 1 + src/comm/Manager.cc | 72 +++++++++++++++++++++++++--- src/comm/Manager.h | 5 +- src/comm/comm.bif | 13 +++++ src/main.cc | 11 ----- testing/btest/comm/clone_store.bro | 2 + testing/btest/comm/data.bro | 1 + testing/btest/comm/master_store.bro | 1 + testing/btest/comm/remote_event.test | 2 + testing/btest/comm/remote_log.test | 11 +++-- testing/btest/comm/remote_print.test | 2 + 12 files changed, 108 insertions(+), 26 deletions(-) create mode 100644 src/comm/comm.bif diff --git a/src/Net.cc b/src/Net.cc index adac9c02fd..3acd4bce9d 100644 --- a/src/Net.cc +++ b/src/Net.cc @@ -34,6 +34,10 @@ #include "iosource/PktDumper.h" #include "plugin/Manager.h" +#ifdef ENABLE_BROKER +#include "comm/Manager.h" +#endif + extern "C" { #include "setsignal.h" }; @@ -315,6 +319,11 @@ void net_run() } #endif current_iosrc = src; + bool communication_enabled = using_communication; + +#ifdef ENABLE_BROKER + communication_enabled |= comm_mgr->Enabled(); +#endif if ( src ) src->Process(); // which will call net_packet_dispatch() @@ -332,7 +341,7 @@ void net_run() } } - else if ( (have_pending_timers || using_communication) && + else if ( (have_pending_timers || communication_enabled) && ! pseudo_realtime ) { // Take advantage of the lull to get up to @@ -347,7 +356,7 @@ void net_run() // us a lot of idle time, but doesn't delay near-term // timers too much. (Delaying them somewhat is okay, // since Bro timers are not high-precision anyway.) - if ( ! using_communication ) + if ( ! communication_enabled ) usleep(100000); else usleep(1000); diff --git a/src/comm/CMakeLists.txt b/src/comm/CMakeLists.txt index 6453e006bf..ef41c605c7 100644 --- a/src/comm/CMakeLists.txt +++ b/src/comm/CMakeLists.txt @@ -19,6 +19,7 @@ set(comm_SRCS Store.cc ) +bif_target(comm.bif) bif_target(data.bif) bif_target(messaging.bif) bif_target(store.bif) diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index 443c5f90da..7db80ebb40 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -13,6 +13,7 @@ #include "comm/store.bif.h" #include "logging/Manager.h" #include "DebugLogger.h" +#include "iosource/Manager.h" using namespace std; @@ -28,11 +29,6 @@ comm::Manager::~Manager() CloseStore(s.first.first, s.first.second); } -bool comm::Manager::InitPreScript() - { - return true; - } - static int require_field(RecordType* rt, const char* name) { auto rval = rt->FieldOffset(name); @@ -44,8 +40,11 @@ static int require_field(RecordType* rt, const char* name) return rval; } -bool comm::Manager::InitPostScript() +bool comm::Manager::Enable() { + if ( endpoint != nullptr ) + return true; + auto send_flags_type = internal_type("Comm::SendFlags")->AsRecordType(); send_flags_self_idx = require_field(send_flags_type, "self"); send_flags_peers_idx = require_field(send_flags_type, "peers"); @@ -94,11 +93,15 @@ bool comm::Manager::InitPostScript() } endpoint = unique_ptr(new broker::endpoint(name)); + iosource_mgr->Register(this, true); return true; } bool comm::Manager::Listen(uint16_t port, const char* addr, bool reuse_addr) { + if ( ! Enabled() ) + return false; + auto rval = endpoint->listen(port, addr, reuse_addr); if ( ! rval ) @@ -114,6 +117,9 @@ bool comm::Manager::Listen(uint16_t port, const char* addr, bool reuse_addr) bool comm::Manager::Connect(string addr, uint16_t port, chrono::duration retry_interval) { + if ( ! Enabled() ) + return false; + auto& peer = peers[make_pair(addr, port)]; if ( peer ) @@ -125,6 +131,9 @@ bool comm::Manager::Connect(string addr, uint16_t port, bool comm::Manager::Disconnect(const string& addr, uint16_t port) { + if ( ! Enabled() ) + return false; + auto it = peers.find(make_pair(addr, port)); if ( it == peers.end() ) @@ -137,18 +146,27 @@ bool comm::Manager::Disconnect(const string& addr, uint16_t port) bool comm::Manager::Print(string topic, string msg, Val* flags) { + if ( ! Enabled() ) + return false; + endpoint->send(move(topic), broker::message{move(msg)}, GetFlags(flags)); return true; } bool comm::Manager::Event(std::string topic, broker::message msg, int flags) { + if ( ! Enabled() ) + return false; + endpoint->send(move(topic), move(msg), flags); return true; } bool comm::Manager::Log(EnumVal* stream, RecordVal* columns, int flags) { + if ( ! Enabled() ) + return false; + auto stream_name = stream->Type()->AsEnumType()->Lookup(stream->AsEnum()); if ( ! stream_name ) @@ -176,6 +194,9 @@ bool comm::Manager::Log(EnumVal* stream, RecordVal* columns, int flags) bool comm::Manager::Event(std::string topic, RecordVal* args, Val* flags) { + if ( ! Enabled() ) + return false; + if ( ! args->Lookup(0) ) return false; @@ -198,6 +219,9 @@ bool comm::Manager::Event(std::string topic, RecordVal* args, Val* flags) bool comm::Manager::AutoEvent(string topic, Val* event, Val* flags) { + if ( ! Enabled() ) + return false; + if ( event->Type()->Tag() != TYPE_FUNC ) { reporter->Error("Comm::auto_event must operate on an event"); @@ -227,6 +251,9 @@ bool comm::Manager::AutoEvent(string topic, Val* event, Val* flags) bool comm::Manager::AutoEventStop(const string& topic, Val* event) { + if ( ! Enabled() ) + return false; + if ( event->Type()->Tag() != TYPE_FUNC ) { reporter->Error("Comm::auto_event_stop must operate on an event"); @@ -257,6 +284,9 @@ bool comm::Manager::AutoEventStop(const string& topic, Val* event) RecordVal* comm::Manager::MakeEventArgs(val_list* args) { + if ( ! Enabled() ) + return nullptr; + auto rval = new RecordVal(BifType::Record::Comm::EventArgs); auto arg_vec = new VectorVal(vector_of_data_type); rval->Assign(1, arg_vec); @@ -324,6 +354,9 @@ RecordVal* comm::Manager::MakeEventArgs(val_list* args) bool comm::Manager::SubscribeToPrints(string topic_prefix) { + if ( ! Enabled() ) + return false; + auto& q = print_subscriptions[topic_prefix]; if ( q ) @@ -335,11 +368,17 @@ bool comm::Manager::SubscribeToPrints(string topic_prefix) bool comm::Manager::UnsubscribeToPrints(const string& topic_prefix) { + if ( ! Enabled() ) + return false; + return print_subscriptions.erase(topic_prefix); } bool comm::Manager::SubscribeToEvents(string topic_prefix) { + if ( ! Enabled() ) + return false; + auto& q = event_subscriptions[topic_prefix]; if ( q ) @@ -351,11 +390,17 @@ bool comm::Manager::SubscribeToEvents(string topic_prefix) bool comm::Manager::UnsubscribeToEvents(const string& topic_prefix) { + if ( ! Enabled() ) + return false; + return event_subscriptions.erase(topic_prefix); } bool comm::Manager::SubscribeToLogs(string topic_prefix) { + if ( ! Enabled() ) + return false; + auto& q = log_subscriptions[topic_prefix]; if ( q ) @@ -367,6 +412,9 @@ bool comm::Manager::SubscribeToLogs(string topic_prefix) bool comm::Manager::UnsubscribeToLogs(const string& topic_prefix) { + if ( ! Enabled() ) + return false; + return log_subscriptions.erase(topic_prefix); } @@ -797,6 +845,9 @@ void comm::Manager::Process() bool comm::Manager::AddStore(StoreHandleVal* handle) { + if ( ! Enabled() ) + return false; + if ( ! handle->store ) return false; @@ -814,6 +865,9 @@ comm::StoreHandleVal* comm::Manager::LookupStore(const broker::store::identifier& id, comm::StoreType type) { + if ( ! Enabled() ) + return nullptr; + auto key = make_pair(id, type); auto it = data_stores.find(key); @@ -826,6 +880,9 @@ comm::Manager::LookupStore(const broker::store::identifier& id, bool comm::Manager::CloseStore(const broker::store::identifier& id, StoreType type) { + if ( ! Enabled() ) + return false; + auto key = make_pair(id, type); auto it = data_stores.find(key); @@ -854,5 +911,8 @@ bool comm::Manager::CloseStore(const broker::store::identifier& id, bool comm::Manager::TrackStoreQuery(StoreQueryCallback* cb) { + if ( ! Enabled() ) + return false; + return pending_queries.insert(cb).second; } diff --git a/src/comm/Manager.h b/src/comm/Manager.h index 31fdfa56c1..2317ecea2c 100644 --- a/src/comm/Manager.h +++ b/src/comm/Manager.h @@ -24,9 +24,10 @@ public: ~Manager(); - bool InitPreScript(); + bool Enable(); - bool InitPostScript(); + bool Enabled() + { return endpoint != nullptr; } bool Listen(uint16_t port, const char* addr = nullptr, bool reuse_addr = true); diff --git a/src/comm/comm.bif b/src/comm/comm.bif new file mode 100644 index 0000000000..7f8d85b720 --- /dev/null +++ b/src/comm/comm.bif @@ -0,0 +1,13 @@ + +##! General functions regarding Bro's broker communication mechanisms. + +%%{ +#include "comm/Manager.h" +%%} + +module Comm; + +function Comm::enable%(%): bool + %{ + return new Val(comm_mgr->Enable(), TYPE_BOOL); + %} diff --git a/src/main.cc b/src/main.cc index 5385ca7993..3d80833009 100644 --- a/src/main.cc +++ b/src/main.cc @@ -860,12 +860,6 @@ int main(int argc, char** argv) #ifdef ENABLE_BROKER comm_mgr = new comm::Manager(); - - if ( ! comm_mgr->InitPreScript() ) - { - fprintf(stderr, "Failed to initialize communication manager."); - exit(1); - } #endif plugin_mgr->InitPreScript(); @@ -942,11 +936,6 @@ int main(int argc, char** argv) exit(rc); } -#ifdef ENABLE_BROKER - comm_mgr->InitPostScript(); - iosource_mgr->Register(comm_mgr, true); -#endif - #ifdef USE_PERFTOOLS_DEBUG } #endif diff --git a/testing/btest/comm/clone_store.bro b/testing/btest/comm/clone_store.bro index 03e0fe172f..3ea0347024 100644 --- a/testing/btest/comm/clone_store.bro +++ b/testing/btest/comm/clone_store.bro @@ -53,6 +53,7 @@ event ready() event bro_init() { + Comm::enable(); Comm::listen(9999/tcp, "127.0.0.1"); Comm::subscribe_to_events("bro/event/ready"); Comm::auto_event("bro/event/done", done); @@ -106,6 +107,7 @@ event Comm::remote_connection_established(peer_address: string, event bro_init() { + Comm::enable(); Comm::connect("127.0.0.1", 9999/tcp, 1secs); Comm::auto_event("bro/event/ready", ready); Comm::subscribe_to_events("bro/event/done"); diff --git a/testing/btest/comm/data.bro b/testing/btest/comm/data.bro index 3fb9dcd86e..dfbb8fc1d7 100644 --- a/testing/btest/comm/data.bro +++ b/testing/btest/comm/data.bro @@ -100,6 +100,7 @@ function comm_vector_to_bro_vector(d: Comm::Data): bro_vector event bro_init() { +Comm::enable(); print Comm::data_type(Comm::data(T)); print Comm::data_type(Comm::data(+1)); print Comm::data_type(Comm::data(1)); diff --git a/testing/btest/comm/master_store.bro b/testing/btest/comm/master_store.bro index 84b4ee07a1..a1cc6a8c95 100644 --- a/testing/btest/comm/master_store.bro +++ b/testing/btest/comm/master_store.bro @@ -120,6 +120,7 @@ function dv(d: Comm::Data): Comm::DataVector event bro_init() { + Comm::enable(); local myset: set[string] = {"a", "b", "c"}; local myvec: vector of string = {"alpha", "beta", "gamma"}; h = Store::create_master("master"); diff --git a/testing/btest/comm/remote_event.test b/testing/btest/comm/remote_event.test index 9ab9a6b224..f44ed0df10 100644 --- a/testing/btest/comm/remote_event.test +++ b/testing/btest/comm/remote_event.test @@ -17,6 +17,7 @@ global auto_event_handler: event(msg: string, c: count); event bro_init() { + Comm::enable(); Comm::listen(9999/tcp, "127.0.0.1"); Comm::subscribe_to_events("bro/event/"); Comm::auto_event("bro/event/my_topic", auto_event_handler); @@ -47,6 +48,7 @@ global auto_event_handler: event(msg: string, c: count); event bro_init() { + Comm::enable(); Comm::subscribe_to_events("bro/event/my_topic"); Comm::connect("127.0.0.1", 9999/tcp, 1secs); } diff --git a/testing/btest/comm/remote_log.test b/testing/btest/comm/remote_log.test index aea88cdc25..7cdc2ab97d 100644 --- a/testing/btest/comm/remote_log.test +++ b/testing/btest/comm/remote_log.test @@ -23,13 +23,14 @@ export { }; global log_test: event(rec: Test::Info); - - event bro_init() &priority=5 - { - Log::create_stream(Test::LOG, [$columns=Test::Info, $ev=log_test]); - } } +event bro_init() &priority=5 + { + Comm::enable(); + Log::create_stream(Test::LOG, [$columns=Test::Info, $ev=log_test]); + } + @TEST-END-FILE @TEST-START-FILE recv.bro diff --git a/testing/btest/comm/remote_print.test b/testing/btest/comm/remote_print.test index 48dfd98bed..03e7517f20 100644 --- a/testing/btest/comm/remote_print.test +++ b/testing/btest/comm/remote_print.test @@ -14,6 +14,7 @@ redef exit_only_after_terminate = T; event bro_init() { + Comm::enable(); Comm::listen(9999/tcp, "127.0.0.1"); Comm::subscribe_to_prints("bro/print/"); } @@ -38,6 +39,7 @@ redef exit_only_after_terminate = T; event bro_init() { + Comm::enable(); Comm::subscribe_to_prints("bro/print/my_topic"); Comm::connect("127.0.0.1", 9999/tcp, 1secs); } From 565ad360c6f0afa0583b472eadde841b7521e9d1 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 3 Feb 2015 17:02:45 -0600 Subject: [PATCH 108/299] Add x509 canonifier to a unit test. --- CHANGES | 4 ++++ VERSION | 2 +- .../btest/scripts/policy/protocols/ssl/validate-ocsp.bro | 6 +++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 2d4ce98c31..76e09d27c1 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,8 @@ +2.3-406 | 2015-02-03 17:02:45 -0600 + + * Add x509 canonifier to a unit test. (Jon Siwek) + 2.3-405 | 2015-02-02 11:14:24 -0600 * Fix memory leak in new split_string* functions. (Jon Siwek) diff --git a/VERSION b/VERSION index 57d75d10d0..15530c9394 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3-405 +2.3-406 diff --git a/testing/btest/scripts/policy/protocols/ssl/validate-ocsp.bro b/testing/btest/scripts/policy/protocols/ssl/validate-ocsp.bro index e7e3c3ff8e..3f88638ee3 100644 --- a/testing/btest/scripts/policy/protocols/ssl/validate-ocsp.bro +++ b/testing/btest/scripts/policy/protocols/ssl/validate-ocsp.bro @@ -1,10 +1,10 @@ # @TEST-EXEC: bro -C -r $TRACES/tls/ocsp-stapling.trace %INPUT -# @TEST-EXEC: btest-diff ssl.log +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-x509-names btest-diff ssl.log # @TEST-EXEC: bro -C -r $TRACES/tls/ocsp-stapling-twimg.trace %INPUT # @TEST-EXEC: mv ssl.log ssl-twimg.log -# @TEST-EXEC: btest-diff ssl-twimg.log +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-x509-names btest-diff ssl-twimg.log # @TEST-EXEC: bro -C -r $TRACES/tls/ocsp-stapling-digicert.trace %INPUT # @TEST-EXEC: mv ssl.log ssl-digicert.log -# @TEST-EXEC: btest-diff ssl-digicert.log +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-x509-names btest-diff ssl-digicert.log @load protocols/ssl/validate-ocsp From 67271ea897e470f6cf2b3484bcc7a6aa4f0622bf Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 3 Feb 2015 17:05:54 -0600 Subject: [PATCH 109/299] Update coverage unit test baselines. --- .../canonified_loaded_scripts.log | 10 +++++-- .../canonified_loaded_scripts.log | 10 +++++-- testing/btest/Baseline/plugins.hooks/output | 28 +++++++++++++------ 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log b/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log index b94df659b4..7b144198ee 100644 --- a/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log +++ b/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log @@ -3,7 +3,7 @@ #empty_field (empty) #unset_field - #path loaded_scripts -#open 2014-10-31-20-38-14 +#open 2015-02-03-22-47-13 #fields name #types string scripts/base/init-bare.bro @@ -14,6 +14,8 @@ scripts/base/init-bare.bro build/scripts/base/bif/reporter.bif.bro build/scripts/base/bif/plugins/Bro_SNMP.types.bif.bro build/scripts/base/bif/event.bif.bro + scripts/base/frameworks/comm/__load__.bro + scripts/base/frameworks/comm/main.bro scripts/base/frameworks/logging/__load__.bro scripts/base/frameworks/logging/main.bro build/scripts/base/bif/logging.bif.bro @@ -47,6 +49,10 @@ scripts/base/init-bare.bro build/scripts/base/bif/bloom-filter.bif.bro build/scripts/base/bif/cardinality-counter.bif.bro build/scripts/base/bif/top-k.bif.bro + build/scripts/base/bif/comm.bif.bro + build/scripts/base/bif/data.bif.bro + build/scripts/base/bif/messaging.bif.bro + build/scripts/base/bif/store.bif.bro build/scripts/base/bif/plugins/__load__.bro build/scripts/base/bif/plugins/Bro_ARP.events.bif.bro build/scripts/base/bif/plugins/Bro_AYIYA.events.bif.bro @@ -115,4 +121,4 @@ scripts/base/init-bare.bro build/scripts/base/bif/plugins/Bro_SQLiteWriter.sqlite.bif.bro scripts/policy/misc/loaded-scripts.bro scripts/base/utils/paths.bro -#close 2014-10-31-20-38-14 +#close 2015-02-03-22-47-13 diff --git a/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log b/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log index 67de0fc1dc..b102ad26a5 100644 --- a/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log +++ b/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log @@ -3,7 +3,7 @@ #empty_field (empty) #unset_field - #path loaded_scripts -#open 2014-10-31-20-38-48 +#open 2015-02-03-22-47-15 #fields name #types string scripts/base/init-bare.bro @@ -14,6 +14,8 @@ scripts/base/init-bare.bro build/scripts/base/bif/reporter.bif.bro build/scripts/base/bif/plugins/Bro_SNMP.types.bif.bro build/scripts/base/bif/event.bif.bro + scripts/base/frameworks/comm/__load__.bro + scripts/base/frameworks/comm/main.bro scripts/base/frameworks/logging/__load__.bro scripts/base/frameworks/logging/main.bro build/scripts/base/bif/logging.bif.bro @@ -47,6 +49,10 @@ scripts/base/init-bare.bro build/scripts/base/bif/bloom-filter.bif.bro build/scripts/base/bif/cardinality-counter.bif.bro build/scripts/base/bif/top-k.bif.bro + build/scripts/base/bif/comm.bif.bro + build/scripts/base/bif/data.bif.bro + build/scripts/base/bif/messaging.bif.bro + build/scripts/base/bif/store.bif.bro build/scripts/base/bif/plugins/__load__.bro build/scripts/base/bif/plugins/Bro_ARP.events.bif.bro build/scripts/base/bif/plugins/Bro_AYIYA.events.bif.bro @@ -247,4 +253,4 @@ scripts/base/init-default.bro scripts/base/misc/find-checksum-offloading.bro scripts/base/misc/find-filtered-trace.bro scripts/policy/misc/loaded-scripts.bro -#close 2014-10-31-20-38-48 +#close 2015-02-03-22-47-15 diff --git a/testing/btest/Baseline/plugins.hooks/output b/testing/btest/Baseline/plugins.hooks/output index 927a64692f..e198d94048 100644 --- a/testing/btest/Baseline/plugins.hooks/output +++ b/testing/btest/Baseline/plugins.hooks/output @@ -191,7 +191,7 @@ 0.000000 MetaHookPost CallFunction(Log::__create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -> -0.000000 MetaHookPost CallFunction(Log::__write, (PacketFilter::LOG, [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::__write, (PacketFilter::LOG, [ts=1423003752.294979, node=bro, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, (Cluster::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, (Communication::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, (Conn::LOG)) -> @@ -285,8 +285,8 @@ 0.000000 MetaHookPost CallFunction(Log::create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -> -0.000000 MetaHookPost CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T])) -> -0.000000 MetaHookPost CallFunction(Log::write, (PacketFilter::LOG, [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1423003752.294979, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::write, (PacketFilter::LOG, [ts=1423003752.294979, node=bro, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(Notice::want_pp, ()) -> 0.000000 MetaHookPost CallFunction(PacketFilter::build, ()) -> 0.000000 MetaHookPost CallFunction(PacketFilter::combine_filters, (ip or not ip, and, )) -> @@ -401,10 +401,12 @@ 0.000000 MetaHookPost LoadFile(./bro.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(./broxygen.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(./cardinality-counter.bif.bro) -> -1 +0.000000 MetaHookPost LoadFile(./comm.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(./const.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(./consts) -> -1 0.000000 MetaHookPost LoadFile(./consts.bro) -> -1 0.000000 MetaHookPost LoadFile(./contents) -> -1 +0.000000 MetaHookPost LoadFile(./data.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(./dcc-send) -> -1 0.000000 MetaHookPost LoadFile(./entities) -> -1 0.000000 MetaHookPost LoadFile(./event.bif.bro) -> -1 @@ -425,6 +427,7 @@ 0.000000 MetaHookPost LoadFile(./main) -> -1 0.000000 MetaHookPost LoadFile(./main.bro) -> -1 0.000000 MetaHookPost LoadFile(./max) -> -1 +0.000000 MetaHookPost LoadFile(./messaging.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(./min) -> -1 0.000000 MetaHookPost LoadFile(./mozilla-ca-list) -> -1 0.000000 MetaHookPost LoadFile(./netstats) -> -1 @@ -440,6 +443,7 @@ 0.000000 MetaHookPost LoadFile(./sftp) -> -1 0.000000 MetaHookPost LoadFile(./site) -> -1 0.000000 MetaHookPost LoadFile(./std-dev) -> -1 +0.000000 MetaHookPost LoadFile(./store.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(./strings.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(./sum) -> -1 0.000000 MetaHookPost LoadFile(./top-k.bif.bro) -> -1 @@ -474,6 +478,7 @@ 0.000000 MetaHookPost LoadFile(base<...>/analyzer.bif) -> -1 0.000000 MetaHookPost LoadFile(base<...>/bro.bif) -> -1 0.000000 MetaHookPost LoadFile(base<...>/cluster) -> -1 +0.000000 MetaHookPost LoadFile(base<...>/comm) -> -1 0.000000 MetaHookPost LoadFile(base<...>/communication) -> -1 0.000000 MetaHookPost LoadFile(base<...>/conn) -> -1 0.000000 MetaHookPost LoadFile(base<...>/conn-ids) -> -1 @@ -730,7 +735,7 @@ 0.000000 MetaHookPre CallFunction(Log::__create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -0.000000 MetaHookPre CallFunction(Log::__write, (PacketFilter::LOG, [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::__write, (PacketFilter::LOG, [ts=1423003752.294979, node=bro, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, (Cluster::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, (Communication::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, (Conn::LOG)) @@ -824,8 +829,8 @@ 0.000000 MetaHookPre CallFunction(Log::create_stream, (Weird::LOG, [columns=, ev=Weird::log_weird])) 0.000000 MetaHookPre CallFunction(Log::create_stream, (X509::LOG, [columns=, ev=X509::log_x509])) 0.000000 MetaHookPre CallFunction(Log::create_stream, (mysql::LOG, [columns=, ev=MySQL::log_mysql])) -0.000000 MetaHookPre CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T])) -0.000000 MetaHookPre CallFunction(Log::write, (PacketFilter::LOG, [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::default_path_func, (PacketFilter::LOG, , [ts=1423003752.294979, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::write, (PacketFilter::LOG, [ts=1423003752.294979, node=bro, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(Notice::want_pp, ()) 0.000000 MetaHookPre CallFunction(PacketFilter::build, ()) 0.000000 MetaHookPre CallFunction(PacketFilter::combine_filters, (ip or not ip, and, )) @@ -940,10 +945,12 @@ 0.000000 MetaHookPre LoadFile(./bro.bif.bro) 0.000000 MetaHookPre LoadFile(./broxygen.bif.bro) 0.000000 MetaHookPre LoadFile(./cardinality-counter.bif.bro) +0.000000 MetaHookPre LoadFile(./comm.bif.bro) 0.000000 MetaHookPre LoadFile(./const.bif.bro) 0.000000 MetaHookPre LoadFile(./consts) 0.000000 MetaHookPre LoadFile(./consts.bro) 0.000000 MetaHookPre LoadFile(./contents) +0.000000 MetaHookPre LoadFile(./data.bif.bro) 0.000000 MetaHookPre LoadFile(./dcc-send) 0.000000 MetaHookPre LoadFile(./entities) 0.000000 MetaHookPre LoadFile(./event.bif.bro) @@ -964,6 +971,7 @@ 0.000000 MetaHookPre LoadFile(./main) 0.000000 MetaHookPre LoadFile(./main.bro) 0.000000 MetaHookPre LoadFile(./max) +0.000000 MetaHookPre LoadFile(./messaging.bif.bro) 0.000000 MetaHookPre LoadFile(./min) 0.000000 MetaHookPre LoadFile(./mozilla-ca-list) 0.000000 MetaHookPre LoadFile(./netstats) @@ -979,6 +987,7 @@ 0.000000 MetaHookPre LoadFile(./sftp) 0.000000 MetaHookPre LoadFile(./site) 0.000000 MetaHookPre LoadFile(./std-dev) +0.000000 MetaHookPre LoadFile(./store.bif.bro) 0.000000 MetaHookPre LoadFile(./strings.bif.bro) 0.000000 MetaHookPre LoadFile(./sum) 0.000000 MetaHookPre LoadFile(./top-k.bif.bro) @@ -1013,6 +1022,7 @@ 0.000000 MetaHookPre LoadFile(base<...>/analyzer.bif) 0.000000 MetaHookPre LoadFile(base<...>/bro.bif) 0.000000 MetaHookPre LoadFile(base<...>/cluster) +0.000000 MetaHookPre LoadFile(base<...>/comm) 0.000000 MetaHookPre LoadFile(base<...>/communication) 0.000000 MetaHookPre LoadFile(base<...>/conn) 0.000000 MetaHookPre LoadFile(base<...>/conn-ids) @@ -1269,7 +1279,7 @@ 0.000000 | HookCallFunction Log::__create_stream(Weird::LOG, [columns=, ev=Weird::log_weird]) 0.000000 | HookCallFunction Log::__create_stream(X509::LOG, [columns=, ev=X509::log_x509]) 0.000000 | HookCallFunction Log::__create_stream(mysql::LOG, [columns=, ev=MySQL::log_mysql]) -0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1423003752.294979, node=bro, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction Log::add_default_filter(Cluster::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Communication::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Conn::LOG) @@ -1363,8 +1373,8 @@ 0.000000 | HookCallFunction Log::create_stream(Weird::LOG, [columns=, ev=Weird::log_weird]) 0.000000 | HookCallFunction Log::create_stream(X509::LOG, [columns=, ev=X509::log_x509]) 0.000000 | HookCallFunction Log::create_stream(mysql::LOG, [columns=, ev=MySQL::log_mysql]) -0.000000 | HookCallFunction Log::default_path_func(PacketFilter::LOG, , [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T]) -0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1421870896.278622, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::default_path_func(PacketFilter::LOG, , [ts=1423003752.294979, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1423003752.294979, node=bro, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction Notice::want_pp() 0.000000 | HookCallFunction PacketFilter::build() 0.000000 | HookCallFunction PacketFilter::combine_filters(ip or not ip, and, ) From a97cd1f3a24d93e81190cc28ba283d26f31035c8 Mon Sep 17 00:00:00 2001 From: Seth Hall Date: Thu, 5 Feb 2015 09:09:08 -0500 Subject: [PATCH 110/299] Fix a bug in the core files framework with handling the BOF buffer. - Any files where the total size was below the size of the default bof_buffer size couldn't have stream analyzers successfully attached because the bof_buffer never reached the full size and was never flushed. This branch explicitly marks the buf_buffer as full and flushes it when the file is being removed. --- src/file_analysis/File.cc | 18 +++++++++++------- .../files.log | 10 ++++++++++ .../file-analysis/big-bof-buffer.bro | 6 ++++++ 3 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 testing/btest/Baseline/scripts.base.frameworks.file-analysis.big-bof-buffer/files.log create mode 100644 testing/btest/scripts/base/frameworks/file-analysis/big-bof-buffer.bro diff --git a/src/file_analysis/File.cc b/src/file_analysis/File.cc index 50617f27b6..cc1f86412c 100644 --- a/src/file_analysis/File.cc +++ b/src/file_analysis/File.cc @@ -492,18 +492,22 @@ void File::EndOfFile() if ( done ) return; - if ( ! did_mime_type && - LookupFieldDefaultCount(missing_bytes_idx) == 0 ) - DetectMIME(); - - analyzers.DrainModifications(); - if ( file_reassembler ) { file_reassembler->Flush(); - analyzers.DrainModifications(); } + // Mark the bof_buffer as full in case it isn't yet + // so that the whole thing can be flushed out to + // any stream analyzers. + if ( ! bof_buffer.full ) + { + bof_buffer.full = true; + DeliverStream((const u_char*) "", 0); + } + + analyzers.DrainModifications(); + done = true; file_analysis::Analyzer* a = 0; diff --git a/testing/btest/Baseline/scripts.base.frameworks.file-analysis.big-bof-buffer/files.log b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.big-bof-buffer/files.log new file mode 100644 index 0000000000..cebe140bda --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.file-analysis.big-bof-buffer/files.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path files +#open 2015-02-05-13-55-41 +#fields ts fuid tx_hosts rx_hosts conn_uids source depth analyzers mime_type filename duration local_orig is_orig seen_bytes total_bytes missing_bytes overflow_bytes timedout parent_fuid md5 sha1 sha256 extracted +#types time string set[addr] set[addr] set[string] string count set[string] string string interval bool bool count count count count bool string string string string string +1362692527.009512 FakNcS1Jfe01uljb3 192.150.187.43 141.142.228.5 CXWv6p3arKYeMETxOg HTTP 0 MD5,SHA1 text/plain - 0.000263 - F 4705 4705 0 0 F - 397168fd09991a0e712254df7bc639ac 1dd7ac0398df6cbc0696445a91ec681facf4dc47 - - +#close 2015-02-05-13-55-41 diff --git a/testing/btest/scripts/base/frameworks/file-analysis/big-bof-buffer.bro b/testing/btest/scripts/base/frameworks/file-analysis/big-bof-buffer.bro new file mode 100644 index 0000000000..0f7e23ddcf --- /dev/null +++ b/testing/btest/scripts/base/frameworks/file-analysis/big-bof-buffer.bro @@ -0,0 +1,6 @@ +# @TEST-EXEC: bro -r $TRACES/http/get.trace %INPUT +# @TEST-EXEC: btest-diff files.log + +@load frameworks/files/hash-all-files + +redef default_file_bof_buffer_size=5000; From 8859c73bde5d392be6081bade798e26b79e0e56e Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Thu, 5 Feb 2015 10:04:04 -0600 Subject: [PATCH 111/299] Add/fix log fields in x509 diff canonifier. --- testing/scripts/diff-remove-x509-names | 30 +++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/testing/scripts/diff-remove-x509-names b/testing/scripts/diff-remove-x509-names index 4863efc990..4534cb7d87 100755 --- a/testing/scripts/diff-remove-x509-names +++ b/testing/scripts/diff-remove-x509-names @@ -3,19 +3,25 @@ # A diff canonifier that removes all X.509 Distinguished Name subject fields # because that output can differ depending on installed OpenSSL version. -BEGIN { FS="\t"; OFS="\t"; s_col = -1; i_col = -1; cs_col = -1; ci_col = -1 } +BEGIN { FS="\t"; OFS="\t"; s_col = -1; i_col = -1; is_col = -1; cs_col = -1; ci_col = -1; cert_subj_col = -1; cert_issuer_col = -1 } /^#fields/ { for ( i = 2; i < NF; ++i ) { if ( $i == "subject" ) s_col = i-1; - if ( $i == "issuer_subject" ) + if ( $i == "issuer" ) i_col = i-1; + if ( $i == "issuer_subject" ) + is_col = i-1; if ( $i == "client_subject" ) cs_col = i-1; - if ( $i == "client_issuer_subject" ) + if ( $i == "client_issuer" ) ci_col = i-1; + if ( $i == "certificate.subject" ) + cert_subj_col = i-1; + if ( $i == "certificate.issuer" ) + cert_issuer_col = i-1; } } @@ -31,6 +37,12 @@ i_col >= 0 { $i_col = "+"; } +is_col >= 0 { + if ( $is_col != "-" ) + # Mark that it's set, but ignore content. + $is_col = "+"; +} + cs_col >= 0 { if ( $cs_col != "-" ) # Mark that it's set, but ignore content. @@ -43,6 +55,18 @@ ci_col >= 0 { $ci_col = "+"; } +cert_subj_col >= 0 { + if ( $cert_subj_col != "-" ) + # Mark that it's set, but ignore content. + $cert_subj_col = "+"; +} + +cert_issuer_col >= 0 { + if ( $cert_issuer_col != "-" ) + # Mark that it's set, but ignore content. + $cert_issuer_col = "+"; +} + { print; } From 9592f6422530aff4873d31453954acacd6034e43 Mon Sep 17 00:00:00 2001 From: Seth Hall Date: Thu, 5 Feb 2015 12:44:10 -0500 Subject: [PATCH 112/299] Update the SOCKS analyzer to support user/pass login. - This addresses BIT-1011 - Add a new field to socks.log; "password". - Two new events; socks_login_userpass and socks_login_reply. - One new weird for unsupported authentication method. - A new test for authenticated socks traffic. - Credit to Nicolas Retrain for the initial patch. Thanks! --- scripts/base/protocols/socks/main.bro | 22 ++++++- src/analyzer/protocol/socks/SOCKS.cc | 3 +- src/analyzer/protocol/socks/events.bif | 16 +++++ .../protocol/socks/socks-analyzer.pac | 40 ++++++++++++ .../protocol/socks/socks-protocol.pac | 57 +++++++++++++++--- .../socks.log | 10 +++ .../tunnel.log | 10 +++ .../socks.log | 10 +-- .../socks.log | 10 +-- testing/btest/Traces/socks-auth.pcap | Bin 0 -> 1326 bytes .../base/protocols/socks/socks-auth.bro | 5 ++ 11 files changed, 162 insertions(+), 21 deletions(-) create mode 100644 testing/btest/Baseline/scripts.base.protocols.socks.socks-auth/socks.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.socks.socks-auth/tunnel.log create mode 100644 testing/btest/Traces/socks-auth.pcap create mode 100644 testing/btest/scripts/base/protocols/socks/socks-auth.bro diff --git a/scripts/base/protocols/socks/main.bro b/scripts/base/protocols/socks/main.bro index 713161d442..f60c3ce41c 100644 --- a/scripts/base/protocols/socks/main.bro +++ b/scripts/base/protocols/socks/main.bro @@ -16,8 +16,10 @@ export { id: conn_id &log; ## Protocol version of SOCKS. version: count &log; - ## Username for the proxy if extracted from the network. + ## Username used to request a login to the proxy. user: string &log &optional; + ## Password used to request a login to the proxy. + password: string &log &optional; ## Server status for the attempt at using the proxy. status: string &log &optional; ## Client requested SOCKS address. Could be an address, a name @@ -91,3 +93,21 @@ event socks_reply(c: connection, version: count, reply: count, sa: SOCKS::Addres if ( "SOCKS" in c$service ) Log::write(SOCKS::LOG, c$socks); } + +event socks_login_userpass(c: connection, user: string, password: string) &priority=5 + { + # Authentication only possible with the version 5. + set_session(c, 5); + + c$socks$user = user; + c$socks$password = password; + } + +event socks_login_reply(c: connection, code: count) &priority=5 + { + # Authentication only possible with the version 5. + set_session(c, 5); + + c$socks$status = v5_status[code]; + } + diff --git a/src/analyzer/protocol/socks/SOCKS.cc b/src/analyzer/protocol/socks/SOCKS.cc index e678528f35..ec1e85653b 100644 --- a/src/analyzer/protocol/socks/SOCKS.cc +++ b/src/analyzer/protocol/socks/SOCKS.cc @@ -57,8 +57,7 @@ void SOCKS_Analyzer::DeliverStream(int len, const u_char* data, bool orig) // with the rest of the conneciton. // // Note that we assume that no payload data arrives before both endpoints - // are done with there part of the SOCKS protocol. - + // are done with their part of the SOCKS protocol. if ( ! pia ) { pia = new pia::PIA_TCP(Conn()); diff --git a/src/analyzer/protocol/socks/events.bif b/src/analyzer/protocol/socks/events.bif index 4f1f8ad1cd..ece69140a1 100644 --- a/src/analyzer/protocol/socks/events.bif +++ b/src/analyzer/protocol/socks/events.bif @@ -27,3 +27,19 @@ event socks_request%(c: connection, version: count, request_type: count, sa: SOC ## p: The destination port for the proxied traffic. event socks_reply%(c: connection, version: count, reply: count, sa: SOCKS::Address, p: port%); +## Generated when a SOCKS client performs username and password based login. +## +## c: The parent connection of the proxy. +## +## user: The given username. +## +## password: The given password. +event socks_login_userpass%(c: connection, user: string, password: string%); + +## Generated when a SOCKS server replies to a login attempt. +## +## c: The parent connection of the proxy. +## +## code: The response code for the attempted login. +event socks_login_reply%(c: connection, code: count%); + diff --git a/src/analyzer/protocol/socks/socks-analyzer.pac b/src/analyzer/protocol/socks/socks-analyzer.pac index db98b3f4b3..7d634e2f46 100644 --- a/src/analyzer/protocol/socks/socks-analyzer.pac +++ b/src/analyzer/protocol/socks/socks-analyzer.pac @@ -148,6 +148,31 @@ refine connection SOCKS_Conn += { return true; %} + function socks5_auth_request_userpass(request: SOCKS5_Auth_Request_UserPass): bool + %{ + StringVal* user = new StringVal(${request.username}.length(), (const char*) ${request.username}.begin()); + StringVal* pass = new StringVal(${request.password}.length(), (const char*) ${request.password}.begin()); + + BifEvent::generate_socks_login_userpass(bro_analyzer(), + bro_analyzer()->Conn(), + user, pass); + return true; + %} + + function socks5_unsupported_authentication(auth_method: uint8): bool + %{ + reporter->Weird(bro_analyzer()->Conn(), fmt("socks5_unsupported_authentication_%d", auth_method)); + return true; + %} + + function socks5_auth_reply(reply: SOCKS5_Auth_Reply): bool + %{ + BifEvent::generate_socks_login_reply(bro_analyzer(), + bro_analyzer()->Conn(), + ${reply.code}); + return true; + %} + function version_error(version: uint8): bool %{ bro_analyzer()->ProtocolViolation(fmt("unsupported/unknown SOCKS version %d", version)); @@ -176,3 +201,18 @@ refine typeattr SOCKS5_Request += &let { refine typeattr SOCKS5_Reply += &let { proc: bool = $context.connection.socks5_reply(this); }; + +refine typeattr SOCKS5_Auth_Negotiation_Reply += &let { +}; + +refine typeattr SOCKS5_Auth_Request_UserPass += &let { + proc: bool = $context.connection.socks5_auth_request_userpass(this); +}; + +refine typeattr SOCKS5_Auth_Reply += &let { + proc: bool = $context.connection.socks5_auth_reply(this); +}; + +refine typeattr SOCKS5_Unsupported_Authentication += &let { + proc: bool = $context.connection.socks5_unsupported_authentication($context.connection.v5_auth_method()); +}; diff --git a/src/analyzer/protocol/socks/socks-protocol.pac b/src/analyzer/protocol/socks/socks-protocol.pac index 05ca4bc861..4e48ea0672 100644 --- a/src/analyzer/protocol/socks/socks-protocol.pac +++ b/src/analyzer/protocol/socks/socks-protocol.pac @@ -2,9 +2,10 @@ type SOCKS_Version(is_orig: bool) = record { version: uint8; msg: case version of { - 4 -> socks4_msg: SOCKS4_Message(is_orig); - 5 -> socks5_msg: SOCKS5_Message(is_orig); - default -> socks_msg_fail: SOCKS_Version_Error(version); + 1 -> socks5_auth_msg: SOCKS5_Auth_Message(is_orig); + 4 -> socks4_msg: SOCKS4_Message(is_orig); + 5 -> socks5_msg: SOCKS5_Message(is_orig); + default -> socks_msg_fail: SOCKS_Version_Error(version); }; }; @@ -14,10 +15,11 @@ type SOCKS_Version_Error(version: uint8) = record { # SOCKS5 Implementation type SOCKS5_Message(is_orig: bool) = case $context.connection.v5_past_authentication() of { - true -> msg: SOCKS5_Real_Message(is_orig); false -> auth: SOCKS5_Auth_Negotiation(is_orig); + true -> msg: SOCKS5_Real_Message(is_orig); }; + type SOCKS5_Auth_Negotiation(is_orig: bool) = case is_orig of { true -> req: SOCKS5_Auth_Negotiation_Request; false -> rep: SOCKS5_Auth_Negotiation_Reply; @@ -32,6 +34,32 @@ type SOCKS5_Auth_Negotiation_Reply = record { selected_auth_method: uint8; } &let { past_auth = $context.connection.set_v5_past_authentication(); + set_auth = $context.connection.set_v5_auth_method(selected_auth_method); +}; + +type SOCKS5_Auth_Message(is_orig: bool) = case is_orig of { + true -> req: SOCKS5_Auth_Request; + false -> rep: SOCKS5_Auth_Reply; +}; + +type SOCKS5_Auth_Request = case $context.connection.v5_auth_method() of { + 0x02 -> userpass : SOCKS5_Auth_Request_UserPass; + default -> unsupported : SOCKS5_Unsupported_Authentication; +}; + +type SOCKS5_Unsupported_Authentication = record { + crap: bytestring &restofdata; +}; + +type SOCKS5_Auth_Request_UserPass = record { + ulen : uint8; + username : bytestring &length=ulen; + plen : uint8; + password : bytestring &length=plen; +}; + +type SOCKS5_Auth_Reply = record { + code : uint8; }; type SOCKS5_Real_Message(is_orig: bool) = case is_orig of { @@ -55,10 +83,10 @@ type SOCKS5_Address = record { } &byteorder = bigendian; type SOCKS5_Request = record { - command: uint8; - reserved: uint8; - remote_name: SOCKS5_Address; - port: uint16; + command : uint8; + reserved : uint8; + remote_name : SOCKS5_Address; + port : uint16; } &byteorder = bigendian; type SOCKS5_Reply = record { @@ -99,10 +127,12 @@ type SOCKS4_Reply = record { refine connection SOCKS_Conn += { %member{ bool v5_authenticated_; + uint8 selected_auth_method_; %} %init{ v5_authenticated_ = false; + selected_auth_method_ = 255; %} function v5_past_authentication(): bool @@ -115,5 +145,16 @@ refine connection SOCKS_Conn += { v5_authenticated_ = true; return true; %} + + function set_v5_auth_method(method: uint8): bool + %{ + selected_auth_method_ = method; + return true; + %} + + function v5_auth_method(): uint8 + %{ + return selected_auth_method_; + %} }; diff --git a/testing/btest/Baseline/scripts.base.protocols.socks.socks-auth/socks.log b/testing/btest/Baseline/scripts.base.protocols.socks.socks-auth/socks.log new file mode 100644 index 0000000000..cc5fa80191 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.socks.socks-auth/socks.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path socks +#open 2015-02-05-16-13-12 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version user password status request.host request.name request_p bound.host bound.name bound_p +#types time string addr port addr port count string string string addr string port addr string port +1368517392.724989 CXWv6p3arKYeMETxOg 192.168.0.2 55951 192.168.0.1 1080 5 bob alice succeeded 192.168.0.2 - 22 192.168.0.1 - 55951 +#close 2015-02-05-16-13-12 diff --git a/testing/btest/Baseline/scripts.base.protocols.socks.socks-auth/tunnel.log b/testing/btest/Baseline/scripts.base.protocols.socks.socks-auth/tunnel.log new file mode 100644 index 0000000000..d53238df93 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.socks.socks-auth/tunnel.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path tunnel +#open 2015-02-05-16-13-12 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p tunnel_type action +#types time string addr port addr port enum enum +1368517392.728523 - 192.168.0.2 0 192.168.0.1 1080 Tunnel::SOCKS Tunnel::DISCOVER +#close 2015-02-05-16-13-12 diff --git a/testing/btest/Baseline/scripts.base.protocols.socks.trace1/socks.log b/testing/btest/Baseline/scripts.base.protocols.socks.trace1/socks.log index 148e4adf02..f69df31b66 100644 --- a/testing/btest/Baseline/scripts.base.protocols.socks.trace1/socks.log +++ b/testing/btest/Baseline/scripts.base.protocols.socks.trace1/socks.log @@ -3,8 +3,8 @@ #empty_field (empty) #unset_field - #path socks -#open 2013-08-26-19-04-20 -#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version user status request.host request.name request_p bound.host bound.name bound_p -#types time string addr port addr port count string string addr string port addr string port -1340213015.276495 CjhGID4nQcgTWjvg4c 10.0.0.55 53994 60.190.189.214 8124 5 - succeeded - www.osnews.com 80 192.168.0.31 - 2688 -#close 2013-08-26-19-04-20 +#open 2015-02-05-17-39-14 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version user password status request.host request.name request_p bound.host bound.name bound_p +#types time string addr port addr port count string string string addr string port addr string port +1340213015.276495 CjhGID4nQcgTWjvg4c 10.0.0.55 53994 60.190.189.214 8124 5 - - succeeded - www.osnews.com 80 192.168.0.31 - 2688 +#close 2015-02-05-17-39-14 diff --git a/testing/btest/Baseline/scripts.base.protocols.socks.trace2/socks.log b/testing/btest/Baseline/scripts.base.protocols.socks.trace2/socks.log index d706a11da3..de7b26f875 100644 --- a/testing/btest/Baseline/scripts.base.protocols.socks.trace2/socks.log +++ b/testing/btest/Baseline/scripts.base.protocols.socks.trace2/socks.log @@ -3,8 +3,8 @@ #empty_field (empty) #unset_field - #path socks -#open 2013-08-26-19-04-20 -#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version user status request.host request.name request_p bound.host bound.name bound_p -#types time string addr port addr port count string string addr string port addr string port -1340113261.914619 CXWv6p3arKYeMETxOg 10.0.0.50 59580 85.194.84.197 1080 5 - succeeded - www.google.com 443 0.0.0.0 - 443 -#close 2013-08-26-19-04-20 +#open 2015-02-05-17-39-29 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version user password status request.host request.name request_p bound.host bound.name bound_p +#types time string addr port addr port count string string string addr string port addr string port +1340113261.914619 CXWv6p3arKYeMETxOg 10.0.0.50 59580 85.194.84.197 1080 5 - - succeeded - www.google.com 443 0.0.0.0 - 443 +#close 2015-02-05-17-39-29 diff --git a/testing/btest/Traces/socks-auth.pcap b/testing/btest/Traces/socks-auth.pcap new file mode 100644 index 0000000000000000000000000000000000000000..1570e229473da081b50bd3906a3263503896de6a GIT binary patch literal 1326 zcmaKsPiWIn9LIkzFX_UfGfpY9D5wV$MAW)E5-sZxrA#(N@HTb2xuGEQ<~hefgf^Ff z?y`ypWlTXt@T54QNb%3Wfs`J-dGe;Xrv84frsSp7!mlJDeDZyt_xrss>(46+s1W^H zEdYi$=J(*@yAOSE9;2`oPSwl-kr!CAid?8JV@=wjO=e-!>&?Rx1mE3X;fWA*S{i& z13igDJP3bxhz&rZ#(^t@okwgesH`BDbqKrYBowqz!IEb><FdsoPSj;elW54rQ$+{kxuPd#Iyhx zzeOpVI}Dh_gj8KR3;whqrg{=lr+G;xGCp%>M!SCd*6bX?cbQV$q|{SF!HBFN!%%8P zNPQH+ZB$^1etNR^zfw;N;&4wQO8s=dLu?X}31)4d0TZ}q12|v+EW5)GH~I95uaJvKz{QEvA^*sZ@mQR#e>ZU7(~phT976yr03CO%@0dt zxfS9wD^3GQ*GmUBeoi2LnPztVZk%i>m^_S5gV#v@_b6=6iOl_p%*#lh^h{U(QH^}q qAvzXq>*gfGvW3xXUhLZ8h}b?~G93G0)x%voJ}wcNhPU)?z4Ql$s7wg} literal 0 HcmV?d00001 diff --git a/testing/btest/scripts/base/protocols/socks/socks-auth.bro b/testing/btest/scripts/base/protocols/socks/socks-auth.bro new file mode 100644 index 0000000000..2123dc1d45 --- /dev/null +++ b/testing/btest/scripts/base/protocols/socks/socks-auth.bro @@ -0,0 +1,5 @@ +# @TEST-EXEC: bro -r $TRACES/socks-auth.pcap %INPUT +# @TEST-EXEC: btest-diff socks.log +# @TEST-EXEC: btest-diff tunnel.log + +@load base/protocols/socks From 0253f49a9433766bcbe3530a9a63f80592d0504c Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Fri, 6 Feb 2015 16:54:01 -0600 Subject: [PATCH 113/299] broker integration: adapt to change in expiration_time --- aux/broker | 2 +- src/comm/store.bif | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/aux/broker b/aux/broker index 0760c6808c..b0d97b1fcb 160000 --- a/aux/broker +++ b/aux/broker @@ -1 +1 @@ -Subproject commit 0760c6808c1d035b7e9f484daefe8ba0a3d6ee13 +Subproject commit b0d97b1fcbdcb9027bd34031c8706be0c0ab315b diff --git a/src/comm/store.bif b/src/comm/store.bif index 7d09704d31..18e63282e8 100644 --- a/src/comm/store.bif +++ b/src/comm/store.bif @@ -108,20 +108,26 @@ function Store::insert%(h: opaque of Store::Handle, auto& key = comm::opaque_field_to_data(k->AsRecordVal(), frame); auto& val = comm::opaque_field_to_data(v->AsRecordVal(), frame); - broker::util::optional expiry; + using broker::store::expiration_time; + broker::util::optional expiry; auto abs_expiry_val = e->AsRecordVal()->Lookup(0); - auto rel_expiry_val = e->AsRecordVal()->Lookup(1); if ( abs_expiry_val ) { - auto tag = broker::store::expiration_time::tag::absolute; - expiry = broker::store::expiration_time(abs_expiry_val->AsTime(), tag); + expiry = expiration_time(abs_expiry_val->AsTime()); + handle->store->insert(key, val, expiry); + return new Val(true, TYPE_BOOL); } - else if ( rel_expiry_val ) + + auto rel_expiry_val = e->AsRecordVal()->Lookup(1); + + if ( rel_expiry_val ) { - auto tag = broker::store::expiration_time::tag::since_last_modification; - expiry = broker::store::expiration_time(rel_expiry_val->AsInterval(), tag); + auto ct = broker::time_point::now().value; + expiry = expiration_time(rel_expiry_val->AsInterval(), ct); + handle->store->insert(key, val, expiry); + return new Val(true, TYPE_BOOL); } handle->store->insert(key, val, expiry); From 3190ca275e4c64ea0eb6c8ddbd81bc6afe95bc04 Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Fri, 6 Feb 2015 19:32:08 -0500 Subject: [PATCH 114/299] SSH: Fix some memleaks. --- src/analyzer/protocol/ssh/ssh-protocol.pac | 167 +++++++++++---------- 1 file changed, 89 insertions(+), 78 deletions(-) diff --git a/src/analyzer/protocol/ssh/ssh-protocol.pac b/src/analyzer/protocol/ssh/ssh-protocol.pac index 36bfcaedf3..e642e87cea 100644 --- a/src/analyzer/protocol/ssh/ssh-protocol.pac +++ b/src/analyzer/protocol/ssh/ssh-protocol.pac @@ -6,44 +6,44 @@ enum version { enum state { VERSION_EXCHANGE = 0, - KEX_INIT = 1, - KEX_DH_GEX = 2, - KEX_DH = 3, - KEX_ECC = 4, - KEX_GSS = 5, - KEX_RSA = 6, - ENCRYPTED = 7, + KEX_INIT = 1, + KEX_DH_GEX = 2, + KEX_DH = 3, + KEX_ECC = 4, + KEX_GSS = 5, + KEX_RSA = 6, + ENCRYPTED = 7, }; -# diffie-hellman-group1-sha1 [RFC4253] Section 8.1 -# diffie-hellman-group14-sha1 [RFC4253] Section 8.2 +# diffie-hellman-group1-sha1 [RFC4253] Section 8.1 +# diffie-hellman-group14-sha1 [RFC4253] Section 8.2 enum KEX_DH_message_id { SSH_MSG_KEXDH_INIT = 30, SSH_MSG_KEXDH_REPLY = 31, }; -# diffie-hellman-group-exchange-sha1 [RFC4419] Section 4.1 -# diffie-hellman-group-exchange-sha256 [RFC4419] Section 4.2 +# diffie-hellman-group-exchange-sha1 [RFC4419] Section 4.1 +# diffie-hellman-group-exchange-sha256 [RFC4419] Section 4.2 enum KEX_DH_GEX_message_id { - SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30, - SSH_MSG_KEX_DH_GEX_GROUP = 31, - SSH_MSG_KEX_DH_GEX_INIT = 32, - SSH_MSG_KEX_DH_GEX_REPLY = 33, - SSH_MSG_KEX_DH_GEX_REQUEST = 34, + SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30, + SSH_MSG_KEX_DH_GEX_GROUP = 31, + SSH_MSG_KEX_DH_GEX_INIT = 32, + SSH_MSG_KEX_DH_GEX_REPLY = 33, + SSH_MSG_KEX_DH_GEX_REQUEST = 34, }; -# rsa1024-sha1 [RFC4432] -# rsa2048-sha256 [RFC4432] +# rsa1024-sha1 [RFC4432] +# rsa2048-sha256 [RFC4432] enum KEX_RSA_message_id { - SSH_MSG_KEXRSA_PUBKEY = 30, - SSH_MSG_KEXRSA_SECRET = 31, - SSH_MSG_KEXRSA_DONE = 32, + SSH_MSG_KEXRSA_PUBKEY = 30, + SSH_MSG_KEXRSA_SECRET = 31, + SSH_MSG_KEXRSA_DONE = 32, }; -# gss-group1-sha1-* [RFC4462] Section 2.3 -# gss-group14-sha1-* [RFC4462] Section 2.4 -# gss-gex-sha1-* [RFC4462] Section 2.5 -# gss-* [RFC4462] Section 2.6 +# gss-group1-sha1-* [RFC4462] Section 2.3 +# gss-group14-sha1-* [RFC4462] Section 2.4 +# gss-gex-sha1-* [RFC4462] Section 2.5 +# gss-* [RFC4462] Section 2.6 enum KEX_GSS_message_id { SSH_MSG_KEXGSS_INIT = 30, SSH_MSG_KEXGSS_CONTINUE = 31, @@ -56,8 +56,8 @@ enum KEX_GSS_message_id { # ecdh-sha2-* [RFC5656] enum KEX_ECDH_message_id { - SSH_MSG_KEX_ECDH_INIT = 30, - SSH_MSG_KEX_ECDH_REPLY = 31, + SSH_MSG_KEX_ECDH_INIT = 30, + SSH_MSG_KEX_ECDH_REPLY = 31, }; # ecmqv-sha2 [RFC5656] @@ -67,74 +67,74 @@ enum KEX_ECMQV_message_id { }; enum ssh1_message_id { - SSH_MSG_NONE = 0, - SSH_MSG_DISCONNECT = 1, - SSH_SMSG_PUBLIC_KEY = 2, - SSH_CMSG_SESSION_KEY = 3, - SSH_CMSG_USER = 4, - SSH_CMSG_AUTH_RHOSTS = 5, - SSH_CMSG_AUTH_RSA = 6, - SSH_SMSG_AUTH_RSA_CHALLENGE = 7, - SSH_CMSG_AUTH_RSA_RESPONSE = 8, - SSH_CMSG_AUTH_PASSWORD = 9, - SSH_CMSG_REQUEST_PTY = 10, - SSH_CMSG_WINDOW_SIZE = 11, - SSH_CMSG_EXEC_SHELL = 12, - SSH_CMSG_EXEC_CMD = 13, - SSH_SMSG_SUCCESS = 14, - SSH_SMSG_FAILURE = 15, - SSH_CMSG_STDIN_DATA = 16, - SSH_SMSG_STDOUT_DATA = 17, - SSH_SMSG_STDERR_DATA = 18, - SSH_CMSG_EOF = 19, - SSH_SMSG_EXITSTATUS = 20, + SSH_MSG_NONE = 0, + SSH_MSG_DISCONNECT = 1, + SSH_SMSG_PUBLIC_KEY = 2, + SSH_CMSG_SESSION_KEY = 3, + SSH_CMSG_USER = 4, + SSH_CMSG_AUTH_RHOSTS = 5, + SSH_CMSG_AUTH_RSA = 6, + SSH_SMSG_AUTH_RSA_CHALLENGE = 7, + SSH_CMSG_AUTH_RSA_RESPONSE = 8, + SSH_CMSG_AUTH_PASSWORD = 9, + SSH_CMSG_REQUEST_PTY = 10, + SSH_CMSG_WINDOW_SIZE = 11, + SSH_CMSG_EXEC_SHELL = 12, + SSH_CMSG_EXEC_CMD = 13, + SSH_SMSG_SUCCESS = 14, + SSH_SMSG_FAILURE = 15, + SSH_CMSG_STDIN_DATA = 16, + SSH_SMSG_STDOUT_DATA = 17, + SSH_SMSG_STDERR_DATA = 18, + SSH_CMSG_EOF = 19, + SSH_SMSG_EXITSTATUS = 20, SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 21, SSH_MSG_CHANNEL_OPEN_FAILURE = 22, - SSH_MSG_CHANNEL_DATA = 23, - SSH_MSG_CHANNEL_CLOSE = 24, + SSH_MSG_CHANNEL_DATA = 23, + SSH_MSG_CHANNEL_CLOSE = 24, SSH_MSG_CHANNEL_CLOSE_CONFIRMATION = 25, SSH_CMSG_X11_REQUEST_FORWARDING_OLD = 26, - SSH_SMSG_X11_OPEN = 27, + SSH_SMSG_X11_OPEN = 27, SSH_CMSG_PORT_FORWARD_REQUEST = 28, - SSH_MSG_PORT_OPEN = 29, + SSH_MSG_PORT_OPEN = 29, SSH_CMSG_AGENT_REQUEST_FORWARDING = 30, - SSH_SMSG_AGENT_OPEN = 31, - SSH_MSG_IGNORE = 32, - SSH_CMSG_EXIT_CONFIRMATION = 33, + SSH_SMSG_AGENT_OPEN = 31, + SSH_MSG_IGNORE = 32, + SSH_CMSG_EXIT_CONFIRMATION = 33, SSH_CMSG_X11_REQUEST_FORWARDING = 34, - SSH_CMSG_AUTH_RHOSTS_RSA = 35, - SSH_MSG_DEBUG = 36, + SSH_CMSG_AUTH_RHOSTS_RSA = 35, + SSH_MSG_DEBUG = 36, SSH_CMSG_REQUEST_COMPRESSION = 37, - SSH_CMSG_MAX_PACKET_SIZE = 38, - SSH_CMSG_AUTH_TIS = 39, - SSH_SMSG_AUTH_TIS_CHALLENGE = 40, - SSH_CMSG_AUTH_TIS_RESPONSE = 41, - SSH_CMSG_AUTH_KERBEROS = 42, + SSH_CMSG_MAX_PACKET_SIZE = 38, + SSH_CMSG_AUTH_TIS = 39, + SSH_SMSG_AUTH_TIS_CHALLENGE = 40, + SSH_CMSG_AUTH_TIS_RESPONSE = 41, + SSH_CMSG_AUTH_KERBEROS = 42, SSH_SMSG_AUTH_KERBEROS_RESPONSE = 43, - SSH_CMSG_HAVE_KERBEROS_TGT = 44, + SSH_CMSG_HAVE_KERBEROS_TGT = 44, }; enum ssh2_message_id { - MSG_DISCONNECT = 1, - MSG_IGNORE = 2, - MSG_UNIMPLEMENTED = 3, - MSG_DEBUG = 4, - MSG_SERVICE_REQUEST = 5, - MSG_SERVICE_ACCEPT = 6, - MSG_KEXINIT = 20, - MSG_NEWKEYS = 21, + MSG_DISCONNECT = 1, + MSG_IGNORE = 2, + MSG_UNIMPLEMENTED = 3, + MSG_DEBUG = 4, + MSG_SERVICE_REQUEST = 5, + MSG_SERVICE_ACCEPT = 6, + MSG_KEXINIT = 20, + MSG_NEWKEYS = 21, }; ## SSH Generic type SSH_PDU(is_orig: bool) = case $context.connection.get_state(is_orig) of { - VERSION_EXCHANGE -> version: SSH_Version(is_orig); - KEX_INIT -> kex: SSH_Key_Exchange(is_orig); - KEX_DH_GEX -> kex_dh_gex: SSH_Key_Exchange_DH_GEX(is_orig); - KEX_DH -> kex_dh: SSH_Key_Exchange_DH(is_orig); - KEX_ECC -> kex_ecc: SSH_Key_Exchange_ECC(is_orig); - KEX_GSS -> kex_gss: SSH_Key_Exchange_GSS(is_orig); - KEX_RSA -> kex_rsa: SSH_Key_Exchange_RSA(is_orig); + VERSION_EXCHANGE -> version: SSH_Version(is_orig); + KEX_INIT -> kex: SSH_Key_Exchange(is_orig); + KEX_DH_GEX -> kex_dh_gex: SSH_Key_Exchange_DH_GEX(is_orig); + KEX_DH -> kex_dh: SSH_Key_Exchange_DH(is_orig); + KEX_ECC -> kex_ecc: SSH_Key_Exchange_ECC(is_orig); + KEX_GSS -> kex_gss: SSH_Key_Exchange_GSS(is_orig); + KEX_RSA -> kex_rsa: SSH_Key_Exchange_RSA(is_orig); } &byteorder=bigendian; type SSH_Version(is_orig: bool) = record { @@ -425,6 +425,11 @@ refine connection SSH_Conn += { kex_algorithm_ = bytestring(); %} + %cleanup{ + kex_algorithm_.free(); + kex_algs_cache_.free(); + %} + function get_state(is_orig: bool): int %{ if ( is_orig ) @@ -514,6 +519,9 @@ refine connection SSH_Conn += { kex_algorithm_.init((const uint8 *) client_list->Lookup(i)->AsStringVal()->Bytes(), client_list->Lookup(i)->AsStringVal()->Len()); + Unref(client_list); + Unref(server_list); + // UNTESTED if ( update_kex_state_if_equal("rsa1024-sha1", KEX_RSA) ) return true; @@ -556,6 +564,9 @@ refine connection SSH_Conn += { } } } + + Unref(client_list); + Unref(server_list); return true; From 530c3c0c6b19fe0e708a076a79567f9fa8334216 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Sun, 8 Feb 2015 18:20:38 -0800 Subject: [PATCH 115/299] Changing load order for plugin scripts. This can be need if they depends on each other. --- src/plugin/Manager.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugin/Manager.cc b/src/plugin/Manager.cc index 2ca34d94f3..ab0b85676b 100644 --- a/src/plugin/Manager.cc +++ b/src/plugin/Manager.cc @@ -172,7 +172,7 @@ bool Manager::ActivateDynamicPluginInternal(const std::string& name, bool ok_if_ // Load {bif,scripts}/__load__.bro automatically. - string init = dir + "scripts/__load__.bro"; + string init = dir + "lib/bif/__load__.bro"; if ( is_file(init) ) { @@ -180,7 +180,7 @@ bool Manager::ActivateDynamicPluginInternal(const std::string& name, bool ok_if_ scripts_to_load.push_back(init); } - init = dir + "lib/bif/__load__.bro"; + init = dir + "scripts/__load__.bro"; if ( is_file(init) ) { From 23b9705a7bbd6333b767b8908be2ddcf7017b5a5 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Sun, 8 Feb 2015 18:21:23 -0800 Subject: [PATCH 116/299] Fixing analyzer tag types for some Files::* functions. --- CHANGES | 6 ++++++ VERSION | 2 +- scripts/base/frameworks/files/main.bro | 22 +++++++++++----------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/CHANGES b/CHANGES index d1031765cc..3367c878cd 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,10 @@ +2.3-413 | 2015-02-08 18:23:05 -0800 + + * Fixing analyzer tag types for some Files::* functions. (Robin Sommer) + + * Changing load order for plugin scripts. (Robin Sommer) + 2.3-411 | 2015-02-05 10:05:48 -0600 * Fix file analysis of files with total size below the bof_buffer size diff --git a/VERSION b/VERSION index defa33cc31..fca56f2eeb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3-411 +2.3-413 diff --git a/scripts/base/frameworks/files/main.bro b/scripts/base/frameworks/files/main.bro index e335d4be9d..94a46578c0 100644 --- a/scripts/base/frameworks/files/main.bro +++ b/scripts/base/frameworks/files/main.bro @@ -267,7 +267,7 @@ export { ## mts: The set of MIME types, each in the form "foo/bar" (case-insensitive). ## ## Returns: True if the MIME types were successfully registered. - global register_for_mime_types: function(tag: Analyzer::Tag, mts: set[string]) : bool; + global register_for_mime_types: function(tag: Files::Tag, mts: set[string]) : bool; ## Registers a MIME type for an analyzer. If a future file with this type is seen, ## the analyzer will be automatically assigned to parsing it. The function *adds* @@ -278,20 +278,20 @@ export { ## mt: The MIME type in the form "foo/bar" (case-insensitive). ## ## Returns: True if the MIME type was successfully registered. - global register_for_mime_type: function(tag: Analyzer::Tag, mt: string) : bool; + global register_for_mime_type: function(tag: Files::Tag, mt: string) : bool; ## Returns a set of all MIME types currently registered for a specific analyzer. ## ## tag: The tag of the analyzer. ## ## Returns: The set of MIME types. - global registered_mime_types: function(tag: Analyzer::Tag) : set[string]; + global registered_mime_types: function(tag: Files::Tag) : set[string]; ## Returns a table of all MIME-type-to-analyzer mappings currently registered. ## ## Returns: A table mapping each analyzer to the set of MIME types ## registered for it. - global all_registered_mime_types: function() : table[Analyzer::Tag] of set[string]; + global all_registered_mime_types: function() : table[Files::Tag] of set[string]; ## Event that can be handled to access the Info record as it is sent on ## to the logging framework. @@ -306,8 +306,8 @@ redef record fa_file += { global registered_protocols: table[Analyzer::Tag] of ProtoRegistration = table(); # Store the MIME type to analyzer mappings. -global mime_types: table[Analyzer::Tag] of set[string]; -global mime_type_to_analyzers: table[string] of set[Analyzer::Tag]; +global mime_types: table[Files::Tag] of set[string]; +global mime_type_to_analyzers: table[string] of set[Files::Tag]; global analyzer_add_callbacks: table[Files::Tag] of function(f: fa_file, args: AnalyzerArgs) = table(); @@ -401,7 +401,7 @@ function register_protocol(tag: Analyzer::Tag, reg: ProtoRegistration): bool return result; } -function register_for_mime_types(tag: Analyzer::Tag, mime_types: set[string]) : bool +function register_for_mime_types(tag: Files::Tag, mime_types: set[string]) : bool { local rc = T; @@ -414,7 +414,7 @@ function register_for_mime_types(tag: Analyzer::Tag, mime_types: set[string]) : return rc; } -function register_for_mime_type(tag: Analyzer::Tag, mt: string) : bool +function register_for_mime_type(tag: Files::Tag, mt: string) : bool { if ( tag !in mime_types ) { @@ -431,12 +431,12 @@ function register_for_mime_type(tag: Analyzer::Tag, mt: string) : bool return T; } -function registered_mime_types(tag: Analyzer::Tag) : set[string] +function registered_mime_types(tag: Files::Tag) : set[string] { return tag in mime_types ? mime_types[tag] : set(); } -function all_registered_mime_types(): table[Analyzer::Tag] of set[string] +function all_registered_mime_types(): table[Files::Tag] of set[string] { return mime_types; } @@ -451,7 +451,7 @@ function describe(f: fa_file): string return handler$describe(f); } -event get_file_handle(tag: Analyzer::Tag, c: connection, is_orig: bool) &priority=5 +event get_file_handle(tag: Files::Tag, c: connection, is_orig: bool) &priority=5 { if ( tag !in registered_protocols ) return; From 5f0a27ca31443ee3c308e49ff5b6e6b1c2fec963 Mon Sep 17 00:00:00 2001 From: Johanna Amann Date: Mon, 9 Feb 2015 12:10:49 -0800 Subject: [PATCH 117/299] Submodule update - newest sqlite version --- src/3rdparty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/3rdparty b/src/3rdparty index 7e15efe9d2..f2e34d731e 160000 --- a/src/3rdparty +++ b/src/3rdparty @@ -1 +1 @@ -Subproject commit 7e15efe9d28d46bfa662fcdd1cbb15ce1db285c9 +Subproject commit f2e34d731ed29bb993fbb065846faa342a8c824f From afc5767165eb6357c9564937e7975759ce76258c Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Mon, 9 Feb 2015 15:48:42 -0600 Subject: [PATCH 118/299] broker integration: add events for incoming connection status updates e.g. for the listen() side of connections to tell when peers have connected or disconnected. --- aux/broker | 2 +- src/comm/Manager.cc | 74 +++++++++++++------ src/comm/comm.bif | 53 +++++++++++++ src/comm/messaging.bif | 49 ------------ .../comm.connection_updates/recv.recv.out | 2 + .../comm.connection_updates/send.send.out | 1 + .../Baseline/comm.remote_event/send.send.out | 2 +- .../Baseline/comm.remote_log/send.send.out | 2 +- .../Baseline/comm.remote_print/send.send.out | 2 +- testing/btest/comm/clone_store.bro | 6 +- testing/btest/comm/connection_updates.bro | 55 ++++++++++++++ testing/btest/comm/remote_event.test | 8 +- testing/btest/comm/remote_log.test | 8 +- testing/btest/comm/remote_print.test | 8 +- 14 files changed, 182 insertions(+), 90 deletions(-) create mode 100644 testing/btest/Baseline/comm.connection_updates/recv.recv.out create mode 100644 testing/btest/Baseline/comm.connection_updates/send.send.out create mode 100644 testing/btest/comm/connection_updates.bro diff --git a/aux/broker b/aux/broker index b0d97b1fcb..4fae86cd67 160000 --- a/aux/broker +++ b/aux/broker @@ -1 +1 @@ -Subproject commit b0d97b1fcbdcb9027bd34031c8706be0c0ab315b +Subproject commit 4fae86cd67b999f48a2f2f354c91e4b1b343b2a1 diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index 7db80ebb40..1dc7cc5415 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -8,6 +8,7 @@ #include "util.h" #include "Var.h" #include "Reporter.h" +#include "comm/comm.bif.h" #include "comm/data.bif.h" #include "comm/messaging.bif.h" #include "comm/store.bif.h" @@ -444,7 +445,8 @@ int comm::Manager::GetFlags(Val* flags) void comm::Manager::GetFds(iosource::FD_Set* read, iosource::FD_Set* write, iosource::FD_Set* except) { - read->Insert(endpoint->peer_status().fd()); + read->Insert(endpoint->outgoing_connection_status().fd()); + read->Insert(endpoint->incoming_connection_status().fd()); for ( const auto& ps : print_subscriptions ) read->Insert(ps.second.fd()); @@ -523,57 +525,85 @@ static RecordVal* response_to_val(broker::store::response r) void comm::Manager::Process() { bool idle = true; - auto peer_status_updates = endpoint->peer_status().want_pop(); + auto outgoing_connection_updates = + endpoint->outgoing_connection_status().want_pop(); + auto incoming_connection_updates = + endpoint->incoming_connection_status().want_pop(); - if ( ! peer_status_updates.empty() ) + for ( auto& u : outgoing_connection_updates ) + { idle = false; - for ( auto& u : peer_status_updates ) - { - if ( ! u.relation.remote() ) - continue; - switch ( u.status ) { - case broker::peer_status::tag::established: - if ( Comm::remote_connection_established ) + case broker::outgoing_connection_status::tag::established: + if ( Comm::outgoing_connection_established ) { val_list* vl = new val_list; vl->append(new StringVal(u.relation.remote_tuple().first)); vl->append(new PortVal(u.relation.remote_tuple().second, TRANSPORT_TCP)); vl->append(new StringVal(u.peer_name)); - mgr.QueueEvent(Comm::remote_connection_established, vl); + mgr.QueueEvent(Comm::outgoing_connection_established, vl); } - break; - case broker::peer_status::tag::disconnected: - if ( Comm::remote_connection_broken ) + case broker::outgoing_connection_status::tag::disconnected: + if ( Comm::outgoing_connection_broken ) { val_list* vl = new val_list; vl->append(new StringVal(u.relation.remote_tuple().first)); vl->append(new PortVal(u.relation.remote_tuple().second, TRANSPORT_TCP)); - mgr.QueueEvent(Comm::remote_connection_broken, vl); + mgr.QueueEvent(Comm::outgoing_connection_broken, vl); } - break; - case broker::peer_status::tag::incompatible: - if ( Comm::remote_connection_incompatible ) + case broker::outgoing_connection_status::tag::incompatible: + if ( Comm::outgoing_connection_incompatible ) { val_list* vl = new val_list; vl->append(new StringVal(u.relation.remote_tuple().first)); vl->append(new PortVal(u.relation.remote_tuple().second, TRANSPORT_TCP)); - mgr.QueueEvent(Comm::remote_connection_incompatible, vl); + mgr.QueueEvent(Comm::outgoing_connection_incompatible, vl); } - break; default: - reporter->InternalWarning("unknown broker::peer_status::tag : %d", - static_cast(u.status)); + reporter->InternalWarning( + "unknown broker::outgoing_connection_status::tag : %d", + static_cast(u.status)); + break; + } + } + + for ( auto& u : incoming_connection_updates ) + { + idle = false; + + switch ( u.status ) { + case broker::incoming_connection_status::tag::established: + if ( Comm::incoming_connection_established ) + { + val_list* vl = new val_list; + vl->append(new StringVal(u.peer_name)); + mgr.QueueEvent(Comm::incoming_connection_established, vl); + } + break; + + case broker::incoming_connection_status::tag::disconnected: + if ( Comm::incoming_connection_broken ) + { + val_list* vl = new val_list; + vl->append(new StringVal(u.peer_name)); + mgr.QueueEvent(Comm::incoming_connection_broken, vl); + } + break; + + default: + reporter->InternalWarning( + "unknown broker::incoming_connection_status::tag : %d", + static_cast(u.status)); break; } } diff --git a/src/comm/comm.bif b/src/comm/comm.bif index 7f8d85b720..e87c6c1144 100644 --- a/src/comm/comm.bif +++ b/src/comm/comm.bif @@ -11,3 +11,56 @@ function Comm::enable%(%): bool %{ return new Val(comm_mgr->Enable(), TYPE_BOOL); %} + +event Comm::outgoing_connection_established%(peer_address: string, + peer_port: port, + peer_name: string%); + +event Comm::outgoing_connection_broken%(peer_address: string, + peer_port: port%); + +event Comm::outgoing_connection_incompatible%(peer_address: string, + peer_port: port%); + +event Comm::incoming_connection_established%(peer_name: string%); + +event Comm::incoming_connection_broken%(peer_name: string%); + +function Comm::listen%(p: port, a: string &default = "", + reuse: bool &default = T%): bool + %{ + if ( ! p->IsTCP() ) + { + reporter->Error("listen port must use tcp"); + return new Val(false, TYPE_BOOL); + } + + auto rval = comm_mgr->Listen(p->Port(), a->Len() ? a->CheckString() : 0, + reuse); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::connect%(a: string, p: port, retry: interval%): bool + %{ + if ( ! p->IsTCP() ) + { + reporter->Error("remote connection port must use tcp"); + return new Val(false, TYPE_BOOL); + } + + auto rval = comm_mgr->Connect(a->CheckString(), p->Port(), + std::chrono::duration(retry)); + return new Val(rval, TYPE_BOOL); + %} + +function Comm::disconnect%(a: string, p: port%): bool + %{ + if ( ! p->IsTCP() ) + { + reporter->Error("remote connection port must use tcp"); + return new Val(false, TYPE_BOOL); + } + + auto rval = comm_mgr->Disconnect(a->CheckString(), p->Port()); + return new Val(rval, TYPE_BOOL); + %} diff --git a/src/comm/messaging.bif b/src/comm/messaging.bif index f5034f842f..26f9497449 100644 --- a/src/comm/messaging.bif +++ b/src/comm/messaging.bif @@ -12,55 +12,6 @@ type Comm::SendFlags: record; type Comm::EventArgs: record; -event Comm::remote_connection_established%(peer_address: string, - peer_port: port, - peer_name: string%); - -event Comm::remote_connection_broken%(peer_address: string, - peer_port: port%); - -event Comm::remote_connection_incompatible%(peer_address: string, - peer_port: port%); - -function Comm::listen%(p: port, a: string &default = "", - reuse: bool &default = T%): bool - %{ - if ( ! p->IsTCP() ) - { - reporter->Error("listen port must use tcp"); - return new Val(false, TYPE_BOOL); - } - - auto rval = comm_mgr->Listen(p->Port(), a->Len() ? a->CheckString() : 0, - reuse); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::connect%(a: string, p: port, retry: interval%): bool - %{ - if ( ! p->IsTCP() ) - { - reporter->Error("remote connection port must use tcp"); - return new Val(false, TYPE_BOOL); - } - - auto rval = comm_mgr->Connect(a->CheckString(), p->Port(), - std::chrono::duration(retry)); - return new Val(rval, TYPE_BOOL); - %} - -function Comm::disconnect%(a: string, p: port%): bool - %{ - if ( ! p->IsTCP() ) - { - reporter->Error("remote connection port must use tcp"); - return new Val(false, TYPE_BOOL); - } - - auto rval = comm_mgr->Disconnect(a->CheckString(), p->Port()); - return new Val(rval, TYPE_BOOL); - %} - event Comm::print_handler%(msg: string%); function Comm::print%(topic: string, msg: string, diff --git a/testing/btest/Baseline/comm.connection_updates/recv.recv.out b/testing/btest/Baseline/comm.connection_updates/recv.recv.out new file mode 100644 index 0000000000..3f2a1a9670 --- /dev/null +++ b/testing/btest/Baseline/comm.connection_updates/recv.recv.out @@ -0,0 +1,2 @@ +Comm::incoming_connection_established, connector +Comm::incoming_connection_broken, connector diff --git a/testing/btest/Baseline/comm.connection_updates/send.send.out b/testing/btest/Baseline/comm.connection_updates/send.send.out new file mode 100644 index 0000000000..e23422e320 --- /dev/null +++ b/testing/btest/Baseline/comm.connection_updates/send.send.out @@ -0,0 +1 @@ +Comm::outgoing_connection_established, 127.0.0.1, 9999/tcp, listener diff --git a/testing/btest/Baseline/comm.remote_event/send.send.out b/testing/btest/Baseline/comm.remote_event/send.send.out index ef1f7bc7e1..9fbb21f245 100644 --- a/testing/btest/Baseline/comm.remote_event/send.send.out +++ b/testing/btest/Baseline/comm.remote_event/send.send.out @@ -1,4 +1,4 @@ -Comm::remote_connection_established, 127.0.0.1, 9999/tcp +Comm::outgoing_connection_established, 127.0.0.1, 9999/tcp got event msg, pong, 0 got auto event msg, ping, 0 got event msg, pong, 1 diff --git a/testing/btest/Baseline/comm.remote_log/send.send.out b/testing/btest/Baseline/comm.remote_log/send.send.out index 0968e6beb9..e2415290d6 100644 --- a/testing/btest/Baseline/comm.remote_log/send.send.out +++ b/testing/btest/Baseline/comm.remote_log/send.send.out @@ -1 +1 @@ -Comm::remote_connection_established, 127.0.0.1, 9999/tcp +Comm::outgoing_connection_established, 127.0.0.1, 9999/tcp diff --git a/testing/btest/Baseline/comm.remote_print/send.send.out b/testing/btest/Baseline/comm.remote_print/send.send.out index 982ee993f6..fc5996194d 100644 --- a/testing/btest/Baseline/comm.remote_print/send.send.out +++ b/testing/btest/Baseline/comm.remote_print/send.send.out @@ -1,4 +1,4 @@ -Comm::remote_connection_established, 127.0.0.1, 9999/tcp +Comm::outgoing_connection_established, 127.0.0.1, 9999/tcp got print msg, pong 0 got print msg, pong 1 got print msg, pong 2 diff --git a/testing/btest/comm/clone_store.bro b/testing/btest/comm/clone_store.bro index 3ea0347024..7a8ccb3a56 100644 --- a/testing/btest/comm/clone_store.bro +++ b/testing/btest/comm/clone_store.bro @@ -81,9 +81,9 @@ event done() terminate(); } -event Comm::remote_connection_established(peer_address: string, - peer_port: port, - peer_name: string) +event Comm::outgoing_connection_established(peer_address: string, + peer_port: port, + peer_name: string) { local myset: set[string] = {"a", "b", "c"}; local myvec: vector of string = {"alpha", "beta", "gamma"}; diff --git a/testing/btest/comm/connection_updates.bro b/testing/btest/comm/connection_updates.bro new file mode 100644 index 0000000000..a1e8c517d2 --- /dev/null +++ b/testing/btest/comm/connection_updates.bro @@ -0,0 +1,55 @@ +# @TEST_SERIALIZE: brokercomm +# @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt + +# @TEST-EXEC: btest-bg-run recv "bro -b ../recv.bro >recv.out" +# @TEST-EXEC: btest-bg-run send "bro -b ../send.bro >send.out" + +# @TEST-EXEC: btest-bg-wait 20 +# @TEST-EXEC: btest-diff recv/recv.out +# @TEST-EXEC: btest-diff send/send.out + +@TEST-START-FILE recv.bro + +redef exit_only_after_terminate = T; +redef Comm::endpoint_name = "listener"; + +event bro_init() + { + Comm::enable(); + Comm::listen(9999/tcp, "127.0.0.1"); + } + +event Comm::incoming_connection_established(peer_name: string) + { + print "Comm::incoming_connection_established", peer_name;; + } + +event Comm::incoming_connection_broken(peer_name: string) + { + print "Comm::incoming_connection_broken", peer_name;; + terminate(); + } + +@TEST-END-FILE + +@TEST-START-FILE send.bro + +redef exit_only_after_terminate = T; +redef Comm::endpoint_name = "connector"; + +event bro_init() + { + Comm::enable(); + Comm::connect("127.0.0.1", 9999/tcp, 1sec); + } + +event Comm::outgoing_connection_established(peer_address: string, + peer_port: port, + peer_name: string) + { + print "Comm::outgoing_connection_established", + peer_address, peer_port, peer_name;; + terminate(); + } + +@TEST-END-FILE diff --git a/testing/btest/comm/remote_event.test b/testing/btest/comm/remote_event.test index f44ed0df10..fc34ad79ec 100644 --- a/testing/btest/comm/remote_event.test +++ b/testing/btest/comm/remote_event.test @@ -55,11 +55,11 @@ event bro_init() global event_count = 0; -event Comm::remote_connection_established(peer_address: string, - peer_port: port, - peer_name: string) +event Comm::outgoing_connection_established(peer_address: string, + peer_port: port, + peer_name: string) { - print "Comm::remote_connection_established", peer_address, peer_port; + print "Comm::outgoing_connection_established", peer_address, peer_port; local args = Comm::event_args(event_handler, "ping", event_count); Comm::event("bro/event/hi", args); ++event_count; diff --git a/testing/btest/comm/remote_log.test b/testing/btest/comm/remote_log.test index 7cdc2ab97d..47227e2fba 100644 --- a/testing/btest/comm/remote_log.test +++ b/testing/btest/comm/remote_log.test @@ -77,11 +77,11 @@ event do_write() } } -event Comm::remote_connection_established(peer_address: string, - peer_port: port, - peer_name: string) +event Comm::outgoing_connection_established(peer_address: string, + peer_port: port, + peer_name: string) { - print "Comm::remote_connection_established", peer_address, peer_port; + print "Comm::outgoing_connection_established", peer_address, peer_port; event do_write(); } diff --git a/testing/btest/comm/remote_print.test b/testing/btest/comm/remote_print.test index 03e7517f20..28e5bccc95 100644 --- a/testing/btest/comm/remote_print.test +++ b/testing/btest/comm/remote_print.test @@ -46,11 +46,11 @@ event bro_init() global n = 0; -event Comm::remote_connection_established(peer_address: string, - peer_port: port, - peer_name: string) +event Comm::outgoing_connection_established(peer_address: string, + peer_port: port, + peer_name: string) { - print "Comm::remote_connection_established", peer_address, peer_port; + print "Comm::outgoing_connection_established", peer_address, peer_port; Comm::print("bro/print/hi", fmt("ping %d", n)); ++n; } From cfb666af2be257994584edd0ea68a8277c22ff27 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Mon, 9 Feb 2015 16:01:31 -0600 Subject: [PATCH 119/299] broker integration: move listen port for unit tests to a btest variable Later, this might be something btest itself could provide to help parallelize communication tests. E.g. unit tests requests a unique number from some range and btest coordinates the distribution of those among all tests. --- testing/btest/btest.cfg | 1 + testing/btest/comm/clone_store.bro | 10 ++++++---- testing/btest/comm/connection_updates.bro | 10 ++++++---- testing/btest/comm/remote_event.test | 10 ++++++---- testing/btest/comm/remote_log.test | 10 ++++++---- testing/btest/comm/remote_print.test | 10 ++++++---- 6 files changed, 31 insertions(+), 20 deletions(-) diff --git a/testing/btest/btest.cfg b/testing/btest/btest.cfg index 2eea514357..3c91872f5a 100644 --- a/testing/btest/btest.cfg +++ b/testing/btest/btest.cfg @@ -25,3 +25,4 @@ TMPDIR=%(testbase)s/.tmp BRO_PROFILER_FILE=%(testbase)s/.tmp/script-coverage.XXXXXX BTEST_RST_FILTER=$SCRIPTS/rst-filter BRO_DNS_FAKE=1 +BROKER_PORT=9999/tcp diff --git a/testing/btest/comm/clone_store.bro b/testing/btest/comm/clone_store.bro index 7a8ccb3a56..44ef0683cf 100644 --- a/testing/btest/comm/clone_store.bro +++ b/testing/btest/comm/clone_store.bro @@ -1,8 +1,8 @@ # @TEST_SERIALIZE: brokercomm # @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt -# @TEST-EXEC: btest-bg-run clone "bro -b ../clone.bro >clone.out" -# @TEST-EXEC: btest-bg-run master "bro -b ../master.bro >master.out" +# @TEST-EXEC: btest-bg-run clone "bro -b ../clone.bro broker_port=$BROKER_PORT >clone.out" +# @TEST-EXEC: btest-bg-run master "bro -b ../master.bro broker_port=$BROKER_PORT >master.out" # @TEST-EXEC: btest-bg-wait 20 # @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-sort btest-diff clone/clone.out @@ -10,6 +10,7 @@ @TEST-START-FILE clone.bro +const broker_port: port &redef; redef exit_only_after_terminate = T; global h: opaque of Store::Handle; @@ -54,7 +55,7 @@ event ready() event bro_init() { Comm::enable(); - Comm::listen(9999/tcp, "127.0.0.1"); + Comm::listen(broker_port, "127.0.0.1"); Comm::subscribe_to_events("bro/event/ready"); Comm::auto_event("bro/event/done", done); } @@ -63,6 +64,7 @@ event bro_init() @TEST-START-FILE master.bro +const broker_port: port &redef; redef exit_only_after_terminate = T; global h: opaque of Store::Handle; @@ -108,7 +110,7 @@ event Comm::outgoing_connection_established(peer_address: string, event bro_init() { Comm::enable(); - Comm::connect("127.0.0.1", 9999/tcp, 1secs); + Comm::connect("127.0.0.1", broker_port, 1secs); Comm::auto_event("bro/event/ready", ready); Comm::subscribe_to_events("bro/event/done"); } diff --git a/testing/btest/comm/connection_updates.bro b/testing/btest/comm/connection_updates.bro index a1e8c517d2..d6f4c99fa3 100644 --- a/testing/btest/comm/connection_updates.bro +++ b/testing/btest/comm/connection_updates.bro @@ -1,8 +1,8 @@ # @TEST_SERIALIZE: brokercomm # @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt -# @TEST-EXEC: btest-bg-run recv "bro -b ../recv.bro >recv.out" -# @TEST-EXEC: btest-bg-run send "bro -b ../send.bro >send.out" +# @TEST-EXEC: btest-bg-run recv "bro -b ../recv.bro broker_port=$BROKER_PORT >recv.out" +# @TEST-EXEC: btest-bg-run send "bro -b ../send.bro broker_port=$BROKER_PORT >send.out" # @TEST-EXEC: btest-bg-wait 20 # @TEST-EXEC: btest-diff recv/recv.out @@ -10,13 +10,14 @@ @TEST-START-FILE recv.bro +const broker_port: port &redef; redef exit_only_after_terminate = T; redef Comm::endpoint_name = "listener"; event bro_init() { Comm::enable(); - Comm::listen(9999/tcp, "127.0.0.1"); + Comm::listen(broker_port, "127.0.0.1"); } event Comm::incoming_connection_established(peer_name: string) @@ -34,13 +35,14 @@ event Comm::incoming_connection_broken(peer_name: string) @TEST-START-FILE send.bro +const broker_port: port &redef; redef exit_only_after_terminate = T; redef Comm::endpoint_name = "connector"; event bro_init() { Comm::enable(); - Comm::connect("127.0.0.1", 9999/tcp, 1sec); + Comm::connect("127.0.0.1", broker_port, 1sec); } event Comm::outgoing_connection_established(peer_address: string, diff --git a/testing/btest/comm/remote_event.test b/testing/btest/comm/remote_event.test index fc34ad79ec..aeced18eea 100644 --- a/testing/btest/comm/remote_event.test +++ b/testing/btest/comm/remote_event.test @@ -1,8 +1,8 @@ # @TEST_SERIALIZE: brokercomm # @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt -# @TEST-EXEC: btest-bg-run recv "bro -b ../recv.bro >recv.out" -# @TEST-EXEC: btest-bg-run send "bro -b ../send.bro >send.out" +# @TEST-EXEC: btest-bg-run recv "bro -b ../recv.bro broker_port=$BROKER_PORT >recv.out" +# @TEST-EXEC: btest-bg-run send "bro -b ../send.bro broker_port=$BROKER_PORT >send.out" # @TEST-EXEC: btest-bg-wait 20 # @TEST-EXEC: btest-diff recv/recv.out @@ -10,6 +10,7 @@ @TEST-START-FILE recv.bro +const broker_port: port &redef; redef exit_only_after_terminate = T; global event_handler: event(msg: string, c: count); @@ -18,7 +19,7 @@ global auto_event_handler: event(msg: string, c: count); event bro_init() { Comm::enable(); - Comm::listen(9999/tcp, "127.0.0.1"); + Comm::listen(broker_port, "127.0.0.1"); Comm::subscribe_to_events("bro/event/"); Comm::auto_event("bro/event/my_topic", auto_event_handler); } @@ -41,6 +42,7 @@ event event_handler(msg: string, n: count) @TEST-START-FILE send.bro +const broker_port: port &redef; redef exit_only_after_terminate = T; global event_handler: event(msg: string, c: count); @@ -50,7 +52,7 @@ event bro_init() { Comm::enable(); Comm::subscribe_to_events("bro/event/my_topic"); - Comm::connect("127.0.0.1", 9999/tcp, 1secs); + Comm::connect("127.0.0.1", broker_port, 1secs); } global event_count = 0; diff --git a/testing/btest/comm/remote_log.test b/testing/btest/comm/remote_log.test index 47227e2fba..2a6174810e 100644 --- a/testing/btest/comm/remote_log.test +++ b/testing/btest/comm/remote_log.test @@ -1,8 +1,8 @@ # @TEST_SERIALIZE: brokercomm # @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt -# @TEST-EXEC: btest-bg-run recv "bro -b ../common.bro ../recv.bro >recv.out" -# @TEST-EXEC: btest-bg-run send "bro -b ../common.bro ../send.bro >send.out" +# @TEST-EXEC: btest-bg-run recv "bro -b ../common.bro ../recv.bro broker_port=$BROKER_PORT >recv.out" +# @TEST-EXEC: btest-bg-run send "bro -b ../common.bro ../send.bro broker_port=$BROKER_PORT >send.out" # @TEST-EXEC: btest-bg-wait 20 # @TEST-EXEC: btest-diff recv/recv.out @@ -35,11 +35,12 @@ event bro_init() &priority=5 @TEST-START-FILE recv.bro +const broker_port: port &redef; redef exit_only_after_terminate = T; event bro_init() { - Comm::listen(9999/tcp, "127.0.0.1"); + Comm::listen(broker_port, "127.0.0.1"); Comm::subscribe_to_logs("bro/log/"); } @@ -55,12 +56,13 @@ event Test::log_test(rec: Test::Info) @TEST-START-FILE send.bro +const broker_port: port &redef; redef exit_only_after_terminate = T; event bro_init() { Comm::enable_remote_logs(Test::LOG); - Comm::connect("127.0.0.1", 9999/tcp, 1secs); + Comm::connect("127.0.0.1", broker_port, 1secs); } global n = 0; diff --git a/testing/btest/comm/remote_print.test b/testing/btest/comm/remote_print.test index 28e5bccc95..0c32e2c1fe 100644 --- a/testing/btest/comm/remote_print.test +++ b/testing/btest/comm/remote_print.test @@ -1,8 +1,8 @@ # @TEST_SERIALIZE: brokercomm # @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt -# @TEST-EXEC: btest-bg-run recv "bro -b ../recv.bro >recv.out" -# @TEST-EXEC: btest-bg-run send "bro -b ../send.bro >send.out" +# @TEST-EXEC: btest-bg-run recv "bro -b ../recv.bro broker_port=$BROKER_PORT >recv.out" +# @TEST-EXEC: btest-bg-run send "bro -b ../send.bro broker_port=$BROKER_PORT >send.out" # @TEST-EXEC: btest-bg-wait 20 # @TEST-EXEC: btest-diff recv/recv.out @@ -10,12 +10,13 @@ @TEST-START-FILE recv.bro +const broker_port: port &redef; redef exit_only_after_terminate = T; event bro_init() { Comm::enable(); - Comm::listen(9999/tcp, "127.0.0.1"); + Comm::listen(broker_port, "127.0.0.1"); Comm::subscribe_to_prints("bro/print/"); } @@ -35,13 +36,14 @@ event Comm::print_handler(msg: string) @TEST-START-FILE send.bro +const broker_port: port &redef; redef exit_only_after_terminate = T; event bro_init() { Comm::enable(); Comm::subscribe_to_prints("bro/print/my_topic"); - Comm::connect("127.0.0.1", 9999/tcp, 1secs); + Comm::connect("127.0.0.1", broker_port, 1secs); } global n = 0; From ebc9407a2b28c81522a00063290e77c99a31c6e6 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Mon, 9 Feb 2015 16:18:46 -0600 Subject: [PATCH 120/299] broker integration: add knobs to set auto publish/advertise behavior --- scripts/base/frameworks/comm/main.bro | 5 ++++ src/comm/Manager.cc | 33 +++++++++++++++++++++++++-- src/comm/Manager.h | 4 +++- src/comm/comm.bif | 11 +++++++-- 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/scripts/base/frameworks/comm/main.bro b/scripts/base/frameworks/comm/main.bro index a2cd1f6ac0..66dc1715f4 100644 --- a/scripts/base/frameworks/comm/main.bro +++ b/scripts/base/frameworks/comm/main.bro @@ -5,6 +5,11 @@ export { const endpoint_name = "" &redef; + type EndpointFlags: record { + auto_publish: bool &default = T; + auto_advertise: bool &default = T; + }; + type SendFlags: record { self: bool &default = F; peers: bool &default = T; diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index 1dc7cc5415..832718b595 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -41,7 +41,25 @@ static int require_field(RecordType* rt, const char* name) return rval; } -bool comm::Manager::Enable() +static int GetEndpointFlags(Val* broker_endpoint_flags) + { + int rval = 0; + auto r = broker_endpoint_flags->AsRecordVal(); + Val* auto_publish_flag = r->Lookup("auto_publish", true); + Val* auto_advertise_flag = r->Lookup("auto_advertise", true); + + if ( auto_publish_flag->AsBool() ) + rval |= broker::AUTO_PUBLISH; + + if ( auto_advertise_flag->AsBool() ) + rval |= broker::AUTO_ADVERTISE; + + Unref(auto_publish_flag); + Unref(auto_advertise_flag); + return rval; + } + +bool comm::Manager::Enable(Val* broker_endpoint_flags) { if ( endpoint != nullptr ) return true; @@ -93,11 +111,22 @@ bool comm::Manager::Enable() name = fmt("bro@.%ld", static_cast(getpid())); } - endpoint = unique_ptr(new broker::endpoint(name)); + int flags = GetEndpointFlags(broker_endpoint_flags); + endpoint = unique_ptr(new broker::endpoint(name, flags)); iosource_mgr->Register(this, true); return true; } +bool comm::Manager::SetEndpointFlags(Val* broker_endpoint_flags) + { + if ( ! Enabled() ) + return false; + + int flags = GetEndpointFlags(broker_endpoint_flags); + endpoint->set_flags(flags); + return true; + } + bool comm::Manager::Listen(uint16_t port, const char* addr, bool reuse_addr) { if ( ! Enabled() ) diff --git a/src/comm/Manager.h b/src/comm/Manager.h index 2317ecea2c..ef1532fbc8 100644 --- a/src/comm/Manager.h +++ b/src/comm/Manager.h @@ -24,7 +24,9 @@ public: ~Manager(); - bool Enable(); + bool Enable(Val* flags); + + bool SetEndpointFlags(Val* flags); bool Enabled() { return endpoint != nullptr; } diff --git a/src/comm/comm.bif b/src/comm/comm.bif index e87c6c1144..aa7efac472 100644 --- a/src/comm/comm.bif +++ b/src/comm/comm.bif @@ -7,9 +7,16 @@ module Comm; -function Comm::enable%(%): bool +type Comm::EndpointFlags: record; + +function Comm::enable%(flags: EndpointFlags &default = EndpointFlags()%): bool %{ - return new Val(comm_mgr->Enable(), TYPE_BOOL); + return new Val(comm_mgr->Enable(flags), TYPE_BOOL); + %} + +function Comm::set_endpoint_flags%(flags: EndpointFlags &default = EndpointFlags()%): bool + %{ + return new Val(comm_mgr->SetEndpointFlags(flags), TYPE_BOOL); %} event Comm::outgoing_connection_established%(peer_address: string, From bdf21c054a5a74408d456446afd009aa2c5f04b7 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 10 Feb 2015 09:51:57 -0600 Subject: [PATCH 121/299] broker integration: add (un)publish/(un)advertise functions For when one wants to manually tune pub/sub behavior instead of use the default automatic settings of allowing publication to all peers and advertising all subscriptions to all peers. --- src/comm/Manager.cc | 36 ++++++++++++++++++++++++++++++++++++ src/comm/Manager.h | 8 ++++++++ src/comm/comm.bif | 20 ++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index 832718b595..6c09c08f2b 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -448,6 +448,42 @@ bool comm::Manager::UnsubscribeToLogs(const string& topic_prefix) return log_subscriptions.erase(topic_prefix); } +bool comm::Manager::PublishTopic(broker::topic t) + { + if ( ! Enabled() ) + return false; + + endpoint->publish(move(t)); + return true; + } + +bool comm::Manager::UnpublishTopic(broker::topic t) + { + if ( ! Enabled() ) + return false; + + endpoint->unpublish(move(t)); + return true; + } + +bool comm::Manager::AdvertiseTopic(broker::topic t) + { + if ( ! Enabled() ) + return false; + + endpoint->advertise(move(t)); + return true; + } + +bool comm::Manager::UnadvertiseTopic(broker::topic t) + { + if ( ! Enabled() ) + return false; + + endpoint->unadvertise(move(t)); + return true; + } + int comm::Manager::GetFlags(Val* flags) { auto r = flags->AsRecordVal(); diff --git a/src/comm/Manager.h b/src/comm/Manager.h index ef1532fbc8..e8a8d5e5b1 100644 --- a/src/comm/Manager.h +++ b/src/comm/Manager.h @@ -64,6 +64,14 @@ public: bool UnsubscribeToLogs(const std::string& topic_prefix); + bool PublishTopic(broker::topic t); + + bool UnpublishTopic(broker::topic t); + + bool AdvertiseTopic(broker::topic t); + + bool UnadvertiseTopic(broker::topic t); + bool AddStore(StoreHandleVal* handle); StoreHandleVal* LookupStore(const broker::store::identifier& id, StoreType type); diff --git a/src/comm/comm.bif b/src/comm/comm.bif index aa7efac472..1d41b572f6 100644 --- a/src/comm/comm.bif +++ b/src/comm/comm.bif @@ -19,6 +19,26 @@ function Comm::set_endpoint_flags%(flags: EndpointFlags &default = EndpointFlags return new Val(comm_mgr->SetEndpointFlags(flags), TYPE_BOOL); %} +function Comm::publish_topic%(topic: string%): bool + %{ + return new Val(comm_mgr->PublishTopic(topic->CheckString()), TYPE_BOOL); + %} + +function Comm::unpublish_topic%(topic: string%): bool + %{ + return new Val(comm_mgr->UnpublishTopic(topic->CheckString()), TYPE_BOOL); + %} + +function Comm::advertise_topic%(topic: string%): bool + %{ + return new Val(comm_mgr->AdvertiseTopic(topic->CheckString()), TYPE_BOOL); + %} + +function Comm::unadvertise_topic%(topic: string%): bool + %{ + return new Val(comm_mgr->UnadvertiseTopic(topic->CheckString()), TYPE_BOOL); + %} + event Comm::outgoing_connection_established%(peer_address: string, peer_port: port, peer_name: string%); From fc36777e66c71fb975b344a8930bd7980b4f5e9f Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 10 Feb 2015 12:34:47 -0600 Subject: [PATCH 122/299] Add --enable-c++11 configure flag. And try to detect that compiler version is sufficient for C++11 support. --enable-broker implies --enable-c++11 --- CMakeLists.txt | 5 ++++- aux/broker | 2 +- cmake | 2 +- configure | 5 +++++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b31e60ac01..28ebc8b568 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,8 +177,11 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) ######################################################################## ## Recurse on sub-directories +if ( ENABLE_CXX11 ) + include(RequireCXX11) +endif () + if ( ENABLE_BROKER ) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") add_subdirectory(aux/broker) set(brodeps ${brodeps} broker) add_definitions(-DENABLE_BROKER) diff --git a/aux/broker b/aux/broker index 4fae86cd67..0af74017e2 160000 --- a/aux/broker +++ b/aux/broker @@ -1 +1 @@ -Subproject commit 4fae86cd67b999f48a2f2f354c91e4b1b343b2a1 +Subproject commit 0af74017e28d78179a25d60ca80385af444d39f1 diff --git a/cmake b/cmake index c2057b7f15..532dd04e8c 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit c2057b7f15dedc27641a50312384505ce4f2112c +Subproject commit 532dd04e8c5027c613a65ea10bcdbaf5e876fcfa diff --git a/configure b/configure index 6235aba7dd..3f7295711c 100755 --- a/configure +++ b/configure @@ -41,6 +41,7 @@ Usage: $0 [OPTION]... [VAR=VALUE]... --enable-perftools-debug use Google's perftools for debugging --enable-jemalloc link against jemalloc --enable-ruby build ruby bindings for broccoli (deprecated) + --enable-c++11 build using the C++11 standard --enable-broker enable use of the Broker communication library (requires C++ Actor Framework and C++11) --disable-broccoli don't build or install the Broccoli library @@ -182,7 +183,11 @@ while [ $# -ne 0 ]; do --enable-jemalloc) append_cache_entry ENABLE_JEMALLOC BOOL true ;; + --enable-c++11) + append_cache_entry ENABLE_CXX11 BOOL true + ;; --enable-broker) + append_cache_entry ENABLE_CXX11 BOOL true append_cache_entry ENABLE_BROKER BOOL true ;; --disable-broccoli) From 6d868d83bea1df0c149704f05361768456231315 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 10 Feb 2015 13:44:04 -0600 Subject: [PATCH 123/299] broker integration: fix unit tests to work when broker is not enabled. --- src/CMakeLists.txt | 4 ++++ src/comm-dummy/CMakeLists.txt | 13 +++++++++++++ src/comm-dummy/comm.bif | 3 +++ src/comm-dummy/data.bif | 3 +++ src/comm-dummy/messaging.bif | 3 +++ src/comm-dummy/store.bif | 3 +++ testing/btest/comm/clone_store.bro | 4 ++-- testing/btest/comm/connection_updates.bro | 4 ++-- testing/btest/comm/data.bro | 2 ++ testing/btest/comm/master_store.bro | 2 ++ testing/btest/comm/remote_event.test | 4 ++-- testing/btest/comm/remote_log.test | 4 ++-- testing/btest/comm/remote_print.test | 4 ++-- 13 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 src/comm-dummy/CMakeLists.txt create mode 100644 src/comm-dummy/comm.bif create mode 100644 src/comm-dummy/data.bif create mode 100644 src/comm-dummy/messaging.bif create mode 100644 src/comm-dummy/store.bif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 55ca12c873..323fb6f023 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -163,6 +163,10 @@ add_subdirectory(probabilistic) if ( ENABLE_BROKER ) add_subdirectory(comm) +else () + # Just to satisfy coverage unit tests until new Broker-based + # communication is enabled by default. + add_subdirectory(comm-dummy) endif () set(bro_SUBDIRS diff --git a/src/comm-dummy/CMakeLists.txt b/src/comm-dummy/CMakeLists.txt new file mode 100644 index 0000000000..cddea1342d --- /dev/null +++ b/src/comm-dummy/CMakeLists.txt @@ -0,0 +1,13 @@ +# Placeholder for Broker-based communication functionality, not enabled +# by default. This helps satisfy coverage unit tests pass regardless of +# whether Broker is enabled or not. + +include(BroSubdir) + +bif_target(comm.bif) +bif_target(data.bif) +bif_target(messaging.bif) +bif_target(store.bif) + +bro_add_subdir_library(comm_dummy ${BIF_OUTPUT_CC}) +add_dependencies(bro_comm_dummy generate_outputs) diff --git a/src/comm-dummy/comm.bif b/src/comm-dummy/comm.bif new file mode 100644 index 0000000000..b030a4cc73 --- /dev/null +++ b/src/comm-dummy/comm.bif @@ -0,0 +1,3 @@ + +##! Placeholder for Broker-based communication functionality, not enabled +##! by default. diff --git a/src/comm-dummy/data.bif b/src/comm-dummy/data.bif new file mode 100644 index 0000000000..e9b9950474 --- /dev/null +++ b/src/comm-dummy/data.bif @@ -0,0 +1,3 @@ + +##! Placeholder for Broker-based communication functionality, not enabled +##! by default diff --git a/src/comm-dummy/messaging.bif b/src/comm-dummy/messaging.bif new file mode 100644 index 0000000000..e9b9950474 --- /dev/null +++ b/src/comm-dummy/messaging.bif @@ -0,0 +1,3 @@ + +##! Placeholder for Broker-based communication functionality, not enabled +##! by default diff --git a/src/comm-dummy/store.bif b/src/comm-dummy/store.bif new file mode 100644 index 0000000000..e9b9950474 --- /dev/null +++ b/src/comm-dummy/store.bif @@ -0,0 +1,3 @@ + +##! Placeholder for Broker-based communication functionality, not enabled +##! by default diff --git a/testing/btest/comm/clone_store.bro b/testing/btest/comm/clone_store.bro index 44ef0683cf..da05f45210 100644 --- a/testing/btest/comm/clone_store.bro +++ b/testing/btest/comm/clone_store.bro @@ -1,5 +1,5 @@ -# @TEST_SERIALIZE: brokercomm -# @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt +# @TEST-SERIALIZE: brokercomm +# @TEST-REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt # @TEST-EXEC: btest-bg-run clone "bro -b ../clone.bro broker_port=$BROKER_PORT >clone.out" # @TEST-EXEC: btest-bg-run master "bro -b ../master.bro broker_port=$BROKER_PORT >master.out" diff --git a/testing/btest/comm/connection_updates.bro b/testing/btest/comm/connection_updates.bro index d6f4c99fa3..67f66646c9 100644 --- a/testing/btest/comm/connection_updates.bro +++ b/testing/btest/comm/connection_updates.bro @@ -1,5 +1,5 @@ -# @TEST_SERIALIZE: brokercomm -# @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt +# @TEST-SERIALIZE: brokercomm +# @TEST-REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt # @TEST-EXEC: btest-bg-run recv "bro -b ../recv.bro broker_port=$BROKER_PORT >recv.out" # @TEST-EXEC: btest-bg-run send "bro -b ../send.bro broker_port=$BROKER_PORT >send.out" diff --git a/testing/btest/comm/data.bro b/testing/btest/comm/data.bro index dfbb8fc1d7..a7de41be7a 100644 --- a/testing/btest/comm/data.bro +++ b/testing/btest/comm/data.bro @@ -1,3 +1,5 @@ +# @TEST-REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt + # @TEST-EXEC: bro -b %INPUT >out # @TEST-EXEC: btest-diff out diff --git a/testing/btest/comm/master_store.bro b/testing/btest/comm/master_store.bro index a1cc6a8c95..61331bd170 100644 --- a/testing/btest/comm/master_store.bro +++ b/testing/btest/comm/master_store.bro @@ -1,3 +1,5 @@ +# @TEST-REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt + # @TEST-EXEC: bro -b %INPUT >out # @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-sort btest-diff out diff --git a/testing/btest/comm/remote_event.test b/testing/btest/comm/remote_event.test index aeced18eea..2e7aa02d6a 100644 --- a/testing/btest/comm/remote_event.test +++ b/testing/btest/comm/remote_event.test @@ -1,5 +1,5 @@ -# @TEST_SERIALIZE: brokercomm -# @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt +# @TEST-SERIALIZE: brokercomm +# @TEST-REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt # @TEST-EXEC: btest-bg-run recv "bro -b ../recv.bro broker_port=$BROKER_PORT >recv.out" # @TEST-EXEC: btest-bg-run send "bro -b ../send.bro broker_port=$BROKER_PORT >send.out" diff --git a/testing/btest/comm/remote_log.test b/testing/btest/comm/remote_log.test index 2a6174810e..b7dd54abf3 100644 --- a/testing/btest/comm/remote_log.test +++ b/testing/btest/comm/remote_log.test @@ -1,5 +1,5 @@ -# @TEST_SERIALIZE: brokercomm -# @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt +# @TEST-SERIALIZE: brokercomm +# @TEST-REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt # @TEST-EXEC: btest-bg-run recv "bro -b ../common.bro ../recv.bro broker_port=$BROKER_PORT >recv.out" # @TEST-EXEC: btest-bg-run send "bro -b ../common.bro ../send.bro broker_port=$BROKER_PORT >send.out" diff --git a/testing/btest/comm/remote_print.test b/testing/btest/comm/remote_print.test index 0c32e2c1fe..4cf9d9b489 100644 --- a/testing/btest/comm/remote_print.test +++ b/testing/btest/comm/remote_print.test @@ -1,5 +1,5 @@ -# @TEST_SERIALIZE: brokercomm -# @TEST_REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt +# @TEST-SERIALIZE: brokercomm +# @TEST-REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt # @TEST-EXEC: btest-bg-run recv "bro -b ../recv.bro broker_port=$BROKER_PORT >recv.out" # @TEST-EXEC: btest-bg-run send "bro -b ../send.bro broker_port=$BROKER_PORT >send.out" From 07cba950b89253a1a9d2857dfcec7744e87fc5ba Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 10 Feb 2015 16:14:49 -0600 Subject: [PATCH 124/299] Fix gcc compile warnings. --- aux/broker | 2 +- src/EventHandler.cc | 2 +- src/comm/Data.cc | 10 ++++++---- src/comm/Manager.cc | 6 +++--- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/aux/broker b/aux/broker index 0af74017e2..9def853ec4 160000 --- a/aux/broker +++ b/aux/broker @@ -1 +1 @@ -Subproject commit 0af74017e28d78179a25d60ca80385af444d39f1 +Subproject commit 9def853ec4498e0133735938355832e0a7628ec8 diff --git a/src/EventHandler.cc b/src/EventHandler.cc index d623f43b66..c252951781 100644 --- a/src/EventHandler.cc +++ b/src/EventHandler.cc @@ -94,7 +94,7 @@ void EventHandler::Call(val_list* vl, bool no_remote) msg.emplace_back(Name()); bool valid_args = true; - for ( auto i = 0u; i < vl->length(); ++i ) + for ( auto i = 0; i < vl->length(); ++i ) { auto opt_data = comm::val_to_data((*vl)[i]); diff --git a/src/comm/Data.cc b/src/comm/Data.cc index 0ea7666f9e..77b2a496bf 100644 --- a/src/comm/Data.cc +++ b/src/comm/Data.cc @@ -192,7 +192,8 @@ struct val_converter { auto expected_index_types = tt->Indices()->Types(); - if ( expected_index_types->length() != indices->size() ) + if ( static_cast(expected_index_types->length()) != + indices->size() ) { Unref(rval); return nullptr; @@ -244,7 +245,8 @@ struct val_converter { auto expected_index_types = tt->Indices()->Types(); - if ( expected_index_types->length() != indices->size() ) + if ( static_cast(expected_index_types->length()) != + indices->size() ) { Unref(rval); return nullptr; @@ -315,7 +317,7 @@ struct val_converter { auto rt = type->AsRecordType(); - if ( a.fields.size() != rt->NumFields() ) + if ( a.fields.size() != static_cast(rt->NumFields()) ) return nullptr; auto rval = new RecordVal(rt); @@ -505,7 +507,7 @@ broker::util::optional comm::val_to_data(Val* v) { auto rec = v->AsRecordVal(); broker::record rval; - auto num_fields = v->Type()->AsRecordType()->NumFields(); + size_t num_fields = v->Type()->AsRecordType()->NumFields(); rval.fields.reserve(num_fields); for ( auto i = 0u; i < num_fields; ++i ) diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index 6c09c08f2b..92b2c167dd 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -322,7 +322,7 @@ RecordVal* comm::Manager::MakeEventArgs(val_list* args) rval->Assign(1, arg_vec); Func* func; - for ( auto i = 0u; i < args->length(); ++i ) + for ( auto i = 0; i < args->length(); ++i ) { auto arg_val = (*args)[i]; @@ -742,7 +742,7 @@ void comm::Manager::Process() auto arg_types = ehp->FType()->ArgTypes()->Types(); - if ( arg_types->length() != em.size() - 1 ) + if ( static_cast(arg_types->length()) != em.size() - 1 ) { reporter->Warning("got event message with invalid # of args," " got %zd, expected %d", em.size() - 1, @@ -766,7 +766,7 @@ void comm::Manager::Process() } } - if ( vl->length() == em.size() - 1 ) + if ( static_cast(vl->length()) == em.size() - 1 ) mgr.QueueEvent(ehp, vl); else delete_vals(vl); From 8e4d37d5c1a24ab385286cb6ee8ba976bccbca00 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 11 Feb 2015 11:21:01 -0600 Subject: [PATCH 125/299] Improve comm tests. Same old problems: hard to get termination conditions right. --- .../Baseline/comm.remote_event/send.send.out | 2 - .../Baseline/comm.remote_print/send.send.out | 1 - testing/btest/comm/clone_store.bro | 12 ++---- testing/btest/comm/remote_event.test | 36 +++++++----------- testing/btest/comm/remote_log.test | 8 +++- testing/btest/comm/remote_print.test | 37 +++++++++++++------ 6 files changed, 48 insertions(+), 48 deletions(-) diff --git a/testing/btest/Baseline/comm.remote_event/send.send.out b/testing/btest/Baseline/comm.remote_event/send.send.out index 9fbb21f245..0e529e08fc 100644 --- a/testing/btest/Baseline/comm.remote_event/send.send.out +++ b/testing/btest/Baseline/comm.remote_event/send.send.out @@ -9,5 +9,3 @@ got event msg, pong, 3 got auto event msg, ping, 3 got event msg, pong, 4 got auto event msg, ping, 4 -got event msg, pong, 5 -got auto event msg, ping, 5 diff --git a/testing/btest/Baseline/comm.remote_print/send.send.out b/testing/btest/Baseline/comm.remote_print/send.send.out index fc5996194d..777afdc0d2 100644 --- a/testing/btest/Baseline/comm.remote_print/send.send.out +++ b/testing/btest/Baseline/comm.remote_print/send.send.out @@ -4,4 +4,3 @@ got print msg, pong 1 got print msg, pong 2 got print msg, pong 3 got print msg, pong 4 -got print msg, pong 5 diff --git a/testing/btest/comm/clone_store.bro b/testing/btest/comm/clone_store.bro index da05f45210..5a01a497fd 100644 --- a/testing/btest/comm/clone_store.bro +++ b/testing/btest/comm/clone_store.bro @@ -17,11 +17,6 @@ global h: opaque of Store::Handle; global expected_key_count = 4; global key_count = 0; -event done() - { - terminate(); - } - function do_lookup(key: string) { when ( local res = Store::lookup(h, Comm::data(key)) ) @@ -30,7 +25,7 @@ function do_lookup(key: string) print "lookup", key, res; if ( key_count == expected_key_count ) - event done(); + terminate(); } timeout 10sec { print "timeout"; } @@ -57,7 +52,6 @@ event bro_init() Comm::enable(); Comm::listen(broker_port, "127.0.0.1"); Comm::subscribe_to_events("bro/event/ready"); - Comm::auto_event("bro/event/done", done); } @TEST-END-FILE @@ -78,7 +72,8 @@ function dv(d: Comm::Data): Comm::DataVector global ready: event(); -event done() +event Comm::outgoing_connection_broken(peer_address: string, + peer_port: port) { terminate(); } @@ -112,7 +107,6 @@ event bro_init() Comm::enable(); Comm::connect("127.0.0.1", broker_port, 1secs); Comm::auto_event("bro/event/ready", ready); - Comm::subscribe_to_events("bro/event/done"); } @TEST-END-FILE diff --git a/testing/btest/comm/remote_event.test b/testing/btest/comm/remote_event.test index 2e7aa02d6a..31897bea31 100644 --- a/testing/btest/comm/remote_event.test +++ b/testing/btest/comm/remote_event.test @@ -25,17 +25,22 @@ event bro_init() } global event_count = 0; +global events_to_recv = 6; event event_handler(msg: string, n: count) { - event auto_event_handler(msg, n); - print "got event msg", msg, n; - local args = Comm::event_args(event_handler, "pong", event_count); - Comm::event("bro/event/my_topic", args); ++event_count; + print "got event msg", msg, n; - if ( n == 5 ) + if ( event_count == events_to_recv ) + { terminate(); + return; + } + + event auto_event_handler(msg, n); + local args = Comm::event_args(event_handler, "pong", n); + Comm::event("bro/event/my_topic", args); } @TEST-END-FILE @@ -67,13 +72,10 @@ event Comm::outgoing_connection_established(peer_address: string, ++event_count; } -global done = F; -global done_auto = F; - -function check_terminate() +event Comm::outgoing_connection_broken(peer_address: string, + peer_port: port) { - if ( done && done_auto ) - terminate(); + terminate(); } event event_handler(msg: string, n: count) @@ -82,23 +84,11 @@ event event_handler(msg: string, n: count) local args = Comm::event_args(event_handler, "ping", event_count); Comm::event("bro/event/hi", args); ++event_count; - - if ( n == 5 ) - { - done = T; - check_terminate(); - } } event auto_event_handler(msg: string, n: count) { print "got auto event msg", msg, n; - - if ( n == 5 ) - { - done_auto = T; - check_terminate(); - } } @TEST-END-FILE diff --git a/testing/btest/comm/remote_log.test b/testing/btest/comm/remote_log.test index b7dd54abf3..42f3f8a594 100644 --- a/testing/btest/comm/remote_log.test +++ b/testing/btest/comm/remote_log.test @@ -70,7 +70,7 @@ global n = 0; event do_write() { if ( n == 6 ) - terminate(); + return; else { Log::write(Test::LOG, [$msg = "ping", $num = n]); @@ -87,4 +87,10 @@ event Comm::outgoing_connection_established(peer_address: string, event do_write(); } +event Comm::outgoing_connection_broken(peer_address: string, + peer_port: port) + { + terminate(); + } + @TEST-END-FILE diff --git a/testing/btest/comm/remote_print.test b/testing/btest/comm/remote_print.test index 4cf9d9b489..d77bc92e9c 100644 --- a/testing/btest/comm/remote_print.test +++ b/testing/btest/comm/remote_print.test @@ -20,16 +20,23 @@ event bro_init() Comm::subscribe_to_prints("bro/print/"); } -global n = 0; +global messages_to_recv = 6; +global messages_sent = 0; +global messages_recv = 0; event Comm::print_handler(msg: string) { + ++messages_recv; print "got print msg", msg; - Comm::print("bro/print/my_topic", fmt("pong %d", n)); - ++n; - if ( msg == "ping 5" ) + if ( messages_to_recv == messages_recv ) + { terminate(); + return; + } + + Comm::print("bro/print/my_topic", fmt("pong %d", messages_sent)); + ++messages_sent; } @TEST-END-FILE @@ -46,25 +53,31 @@ event bro_init() Comm::connect("127.0.0.1", broker_port, 1secs); } -global n = 0; +global messages_sent = 0; +global messages_recv = 0; +global peer_disconnected = F; event Comm::outgoing_connection_established(peer_address: string, peer_port: port, peer_name: string) { print "Comm::outgoing_connection_established", peer_address, peer_port; - Comm::print("bro/print/hi", fmt("ping %d", n)); - ++n; + Comm::print("bro/print/hi", fmt("ping %d", messages_sent)); + ++messages_sent; + } + +event Comm::outgoing_connection_broken(peer_address: string, + peer_port: port) + { + terminate(); } event Comm::print_handler(msg: string) { + ++messages_recv; print "got print msg", msg; - Comm::print("bro/print/hi", fmt("ping %d", n)); - ++n; - - if ( msg == "pong 5" ) - terminate(); + Comm::print("bro/print/hi", fmt("ping %d", messages_sent)); + ++messages_sent; } @TEST-END-FILE From dab4d6c8bd296ab89ae8c88931c5efd8e80d4424 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 11 Feb 2015 13:21:36 -0600 Subject: [PATCH 126/299] Update broker submodule. --- aux/broker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aux/broker b/aux/broker index 9def853ec4..0767494b9f 160000 --- a/aux/broker +++ b/aux/broker @@ -1 +1 @@ -Subproject commit 9def853ec4498e0133735938355832e0a7628ec8 +Subproject commit 0767494b9f11fabd464cd95c125d5987b6d52858 From 88af106b6b5de8497499e1f6be9d317b6fbb4707 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 11 Feb 2015 13:56:34 -0600 Subject: [PATCH 127/299] Fix use of deprecated gperftools headers. As of gperftools 2.0 (Feb. 2012), they've been renamed in to gperftools/ instead of google/, and as of gperftools 2.2, including the later emits deprecation warnings. --- src/util.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util.h b/src/util.h index db77888c16..50c33d5608 100644 --- a/src/util.h +++ b/src/util.h @@ -48,8 +48,8 @@ #endif #ifdef USE_PERFTOOLS_DEBUG -#include -#include +#include +#include extern HeapLeakChecker* heap_checker; #endif From 5a73c11baa783088d9a03460ca7000f940b72121 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Thu, 12 Feb 2015 11:40:04 -0600 Subject: [PATCH 128/299] broker integration: fix memory leak, add leak tests Leak tests won't pass w/ libcaf 0.12.2, needs the develop branch (actor-framework@a89485a3098965f104264808994fabfbc3a1bf61). --- src/comm/Data.cc | 9 +- .../clone.clone.out | 5 + .../Baseline/core.leaks.comm.data/bro..stdout | 99 ++++++++ .../core.leaks.comm.master_store/bro..stdout | 14 ++ .../recv.recv.out | 6 + .../send.send.out | 11 + .../core.leaks.comm.remote_log/recv.recv.out | 6 + .../core.leaks.comm.remote_log/recv.test.log | 15 ++ .../core.leaks.comm.remote_log/send.send.out | 1 + .../core.leaks.comm.remote_log/send.test.log | 15 ++ .../recv.recv.out | 6 + .../send.send.out | 6 + testing/btest/core/leaks/comm/clone_store.bro | 113 +++++++++ testing/btest/core/leaks/comm/data.bro | 233 ++++++++++++++++++ .../btest/core/leaks/comm/master_store.bro | 155 ++++++++++++ .../btest/core/leaks/comm/remote_event.test | 96 ++++++++ testing/btest/core/leaks/comm/remote_log.test | 98 ++++++++ .../btest/core/leaks/comm/remote_print.test | 85 +++++++ 18 files changed, 969 insertions(+), 4 deletions(-) create mode 100644 testing/btest/Baseline/core.leaks.comm.clone_store/clone.clone.out create mode 100644 testing/btest/Baseline/core.leaks.comm.data/bro..stdout create mode 100644 testing/btest/Baseline/core.leaks.comm.master_store/bro..stdout create mode 100644 testing/btest/Baseline/core.leaks.comm.remote_event/recv.recv.out create mode 100644 testing/btest/Baseline/core.leaks.comm.remote_event/send.send.out create mode 100644 testing/btest/Baseline/core.leaks.comm.remote_log/recv.recv.out create mode 100644 testing/btest/Baseline/core.leaks.comm.remote_log/recv.test.log create mode 100644 testing/btest/Baseline/core.leaks.comm.remote_log/send.send.out create mode 100644 testing/btest/Baseline/core.leaks.comm.remote_log/send.test.log create mode 100644 testing/btest/Baseline/core.leaks.comm.remote_print/recv.recv.out create mode 100644 testing/btest/Baseline/core.leaks.comm.remote_print/send.send.out create mode 100644 testing/btest/core/leaks/comm/clone_store.bro create mode 100644 testing/btest/core/leaks/comm/data.bro create mode 100644 testing/btest/core/leaks/comm/master_store.bro create mode 100644 testing/btest/core/leaks/comm/remote_event.test create mode 100644 testing/btest/core/leaks/comm/remote_log.test create mode 100644 testing/btest/core/leaks/comm/remote_print.test diff --git a/src/comm/Data.cc b/src/comm/Data.cc index 77b2a496bf..46fc8bc8eb 100644 --- a/src/comm/Data.cc +++ b/src/comm/Data.cc @@ -414,7 +414,6 @@ broker::util::optional comm::val_to_data(Val* v) auto is_set = v->Type()->IsSet(); auto table = v->AsTable(); auto table_val = v->AsTableVal(); - auto c = table->InitForIteration(); broker::data rval; if ( is_set ) @@ -437,10 +436,12 @@ broker::util::optional comm::val_to_data(Val* v) ListVal* lv; }; - for ( auto i = 0; i < table->Length(); ++i ) + HashKey* k; + TableEntryVal* entry; + auto c = table->InitForIteration(); + + while ( (entry = table->NextEntry(k, c)) ) { - HashKey* k; - auto entry = table->NextEntry(k, c); auto vl = table_val->RecoverIndex(k); iter_guard ig(k, vl); diff --git a/testing/btest/Baseline/core.leaks.comm.clone_store/clone.clone.out b/testing/btest/Baseline/core.leaks.comm.clone_store/clone.clone.out new file mode 100644 index 0000000000..8a7c89a19b --- /dev/null +++ b/testing/btest/Baseline/core.leaks.comm.clone_store/clone.clone.out @@ -0,0 +1,5 @@ +clone keys, [status=Store::SUCCESS, result=[d=broker::data{[one, two, myset, myvec]}]] +lookup, one, [status=Store::SUCCESS, result=[d=broker::data{111}]] +lookup, two, [status=Store::SUCCESS, result=[d=broker::data{222}]] +lookup, myset, [status=Store::SUCCESS, result=[d=broker::data{{a, c, d}}]] +lookup, myvec, [status=Store::SUCCESS, result=[d=broker::data{[delta, alpha, beta, gamma, omega]}]] diff --git a/testing/btest/Baseline/core.leaks.comm.data/bro..stdout b/testing/btest/Baseline/core.leaks.comm.data/bro..stdout new file mode 100644 index 0000000000..eea78d39a2 --- /dev/null +++ b/testing/btest/Baseline/core.leaks.comm.data/bro..stdout @@ -0,0 +1,99 @@ +Comm::BOOL +Comm::INT +Comm::COUNT +Comm::DOUBLE +Comm::STRING +Comm::ADDR +Comm::SUBNET +Comm::PORT +Comm::TIME +Comm::INTERVAL +Comm::ENUM +Comm::SET +Comm::TABLE +Comm::VECTOR +Comm::RECORD +*************************** +T +F +1 +0 +-1 +1 +0 +1.1 +-11.1 +hello +1.2.3.4 +192.168.0.0/16 +22/tcp +42.0 +180.0 +Comm::BOOL +*************************** +{ +two, +one, +three +} +0 +T +1 +T +F +T +2 +T +1 +F +{ +bye +} +0 +*************************** +{ +[two] = 2, +[one] = 1, +[three] = 3 +} +0 +[d=] +1 +T +42 +F +[d=] +2 +[d=broker::data{7}] +2 +37 +[d=broker::data{42}] +1 +*************************** +[zero, one, two] +0 +T +T +T +T +[hi, salutations, hello, greetings] +4 +[d=broker::data{hello}] +[d=broker::data{bah}] +[d=broker::data{hi}] +[hi, salutations, bah, greetings] +[d=broker::data{bah}] +[hi, salutations, greetings] +3 +*************************** +[a=, b=bee, c=1] +[a=test, b=bee, c=1] +[a=test, b=testagain, c=1] +3 +T +T +T +[d=broker::data{hi}] +[d=broker::data{hello}] +[d=broker::data{37}] +3 diff --git a/testing/btest/Baseline/core.leaks.comm.master_store/bro..stdout b/testing/btest/Baseline/core.leaks.comm.master_store/bro..stdout new file mode 100644 index 0000000000..defdc9a3e1 --- /dev/null +++ b/testing/btest/Baseline/core.leaks.comm.master_store/bro..stdout @@ -0,0 +1,14 @@ +lookup(two): [status=Store::SUCCESS, result=[d=broker::data{222}]] +lookup(four): [status=Store::SUCCESS, result=[d=]] +lookup(myset): [status=Store::SUCCESS, result=[d=broker::data{{a, c, d}}]] +lookup(one): [status=Store::SUCCESS, result=[d=broker::data{111}]] +lookup(myvec): [status=Store::SUCCESS, result=[d=broker::data{[delta, alpha, beta, gamma, omega]}]] +exists(one): [status=Store::SUCCESS, result=[d=broker::data{1}]] +exists(two): [status=Store::SUCCESS, result=[d=broker::data{0}]] +exists(myset): [status=Store::SUCCESS, result=[d=broker::data{1}]] +exists(four): [status=Store::SUCCESS, result=[d=broker::data{0}]] +pop_right(myvec): [status=Store::SUCCESS, result=[d=broker::data{omega}]] +pop_left(myvec): [status=Store::SUCCESS, result=[d=broker::data{delta}]] +keys: [status=Store::SUCCESS, result=[d=broker::data{[myvec, myset, one]}]] +size: [status=Store::SUCCESS, result=[d=broker::data{3}]] +size (after clear): [status=Store::SUCCESS, result=[d=broker::data{0}]] diff --git a/testing/btest/Baseline/core.leaks.comm.remote_event/recv.recv.out b/testing/btest/Baseline/core.leaks.comm.remote_event/recv.recv.out new file mode 100644 index 0000000000..7dab0284ea --- /dev/null +++ b/testing/btest/Baseline/core.leaks.comm.remote_event/recv.recv.out @@ -0,0 +1,6 @@ +got event msg, ping, 0 +got event msg, ping, 1 +got event msg, ping, 2 +got event msg, ping, 3 +got event msg, ping, 4 +got event msg, ping, 5 diff --git a/testing/btest/Baseline/core.leaks.comm.remote_event/send.send.out b/testing/btest/Baseline/core.leaks.comm.remote_event/send.send.out new file mode 100644 index 0000000000..0e529e08fc --- /dev/null +++ b/testing/btest/Baseline/core.leaks.comm.remote_event/send.send.out @@ -0,0 +1,11 @@ +Comm::outgoing_connection_established, 127.0.0.1, 9999/tcp +got event msg, pong, 0 +got auto event msg, ping, 0 +got event msg, pong, 1 +got auto event msg, ping, 1 +got event msg, pong, 2 +got auto event msg, ping, 2 +got event msg, pong, 3 +got auto event msg, ping, 3 +got event msg, pong, 4 +got auto event msg, ping, 4 diff --git a/testing/btest/Baseline/core.leaks.comm.remote_log/recv.recv.out b/testing/btest/Baseline/core.leaks.comm.remote_log/recv.recv.out new file mode 100644 index 0000000000..3e0957442d --- /dev/null +++ b/testing/btest/Baseline/core.leaks.comm.remote_log/recv.recv.out @@ -0,0 +1,6 @@ +wrote log, [msg=ping, num=0] +wrote log, [msg=ping, num=1] +wrote log, [msg=ping, num=2] +wrote log, [msg=ping, num=3] +wrote log, [msg=ping, num=4] +wrote log, [msg=ping, num=5] diff --git a/testing/btest/Baseline/core.leaks.comm.remote_log/recv.test.log b/testing/btest/Baseline/core.leaks.comm.remote_log/recv.test.log new file mode 100644 index 0000000000..4fe7790779 --- /dev/null +++ b/testing/btest/Baseline/core.leaks.comm.remote_log/recv.test.log @@ -0,0 +1,15 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path test +#open 2015-02-12-17-33-13 +#fields msg num +#types string count +ping 0 +ping 1 +ping 2 +ping 3 +ping 4 +ping 5 +#close 2015-02-12-17-33-14 diff --git a/testing/btest/Baseline/core.leaks.comm.remote_log/send.send.out b/testing/btest/Baseline/core.leaks.comm.remote_log/send.send.out new file mode 100644 index 0000000000..e2415290d6 --- /dev/null +++ b/testing/btest/Baseline/core.leaks.comm.remote_log/send.send.out @@ -0,0 +1 @@ +Comm::outgoing_connection_established, 127.0.0.1, 9999/tcp diff --git a/testing/btest/Baseline/core.leaks.comm.remote_log/send.test.log b/testing/btest/Baseline/core.leaks.comm.remote_log/send.test.log new file mode 100644 index 0000000000..884517b252 --- /dev/null +++ b/testing/btest/Baseline/core.leaks.comm.remote_log/send.test.log @@ -0,0 +1,15 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path test +#open 2015-02-12-17-33-13 +#fields msg num +#types string count +ping 0 +ping 1 +ping 2 +ping 3 +ping 4 +ping 5 +#close 2015-02-12-17-33-15 diff --git a/testing/btest/Baseline/core.leaks.comm.remote_print/recv.recv.out b/testing/btest/Baseline/core.leaks.comm.remote_print/recv.recv.out new file mode 100644 index 0000000000..6e5a37abbf --- /dev/null +++ b/testing/btest/Baseline/core.leaks.comm.remote_print/recv.recv.out @@ -0,0 +1,6 @@ +got print msg, ping 0 +got print msg, ping 1 +got print msg, ping 2 +got print msg, ping 3 +got print msg, ping 4 +got print msg, ping 5 diff --git a/testing/btest/Baseline/core.leaks.comm.remote_print/send.send.out b/testing/btest/Baseline/core.leaks.comm.remote_print/send.send.out new file mode 100644 index 0000000000..777afdc0d2 --- /dev/null +++ b/testing/btest/Baseline/core.leaks.comm.remote_print/send.send.out @@ -0,0 +1,6 @@ +Comm::outgoing_connection_established, 127.0.0.1, 9999/tcp +got print msg, pong 0 +got print msg, pong 1 +got print msg, pong 2 +got print msg, pong 3 +got print msg, pong 4 diff --git a/testing/btest/core/leaks/comm/clone_store.bro b/testing/btest/core/leaks/comm/clone_store.bro new file mode 100644 index 0000000000..2a75bfa62f --- /dev/null +++ b/testing/btest/core/leaks/comm/clone_store.bro @@ -0,0 +1,113 @@ +# @TEST-SERIALIZE: brokercomm +# @TEST-REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt +# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks +# @TEST-GROUP: leak + +# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run clone "bro -m -b ../clone.bro broker_port=$BROKER_PORT >clone.out" +# @TEST-EXEC: btest-bg-run master "bro -b ../master.bro broker_port=$BROKER_PORT >master.out" + +# @TEST-EXEC: btest-bg-wait 45 +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-sort btest-diff clone/clone.out + +@TEST-START-FILE clone.bro + +const broker_port: port &redef; +redef exit_only_after_terminate = T; + +global h: opaque of Store::Handle; +global expected_key_count = 4; +global key_count = 0; + +function do_lookup(key: string) + { + when ( local res = Store::lookup(h, Comm::data(key)) ) + { + ++key_count; + print "lookup", key, res; + + if ( key_count == expected_key_count ) + terminate(); + } + timeout 10sec + { print "timeout"; } + } + +event ready() + { + h = Store::create_clone("mystore"); + + when ( local res = Store::keys(h) ) + { + print "clone keys", res; + do_lookup(Comm::refine_to_string(Comm::vector_lookup(res$result, 0))); + do_lookup(Comm::refine_to_string(Comm::vector_lookup(res$result, 1))); + do_lookup(Comm::refine_to_string(Comm::vector_lookup(res$result, 2))); + do_lookup(Comm::refine_to_string(Comm::vector_lookup(res$result, 3))); + } + timeout 10sec + { print "timeout"; } + } + +event bro_init() + { + Comm::enable(); + Comm::listen(broker_port, "127.0.0.1"); + Comm::subscribe_to_events("bro/event/ready"); + } + +@TEST-END-FILE + +@TEST-START-FILE master.bro + +const broker_port: port &redef; +redef exit_only_after_terminate = T; + +global h: opaque of Store::Handle; + +function dv(d: Comm::Data): Comm::DataVector + { + local rval: Comm::DataVector; + rval[0] = d; + return rval; + } + +global ready: event(); + +event Comm::outgoing_connection_broken(peer_address: string, + peer_port: port) + { + terminate(); + } + +event Comm::outgoing_connection_established(peer_address: string, + peer_port: port, + peer_name: string) + { + local myset: set[string] = {"a", "b", "c"}; + local myvec: vector of string = {"alpha", "beta", "gamma"}; + Store::insert(h, Comm::data("one"), Comm::data(110)); + Store::insert(h, Comm::data("two"), Comm::data(223)); + Store::insert(h, Comm::data("myset"), Comm::data(myset)); + Store::insert(h, Comm::data("myvec"), Comm::data(myvec)); + Store::increment(h, Comm::data("one")); + Store::decrement(h, Comm::data("two")); + Store::add_to_set(h, Comm::data("myset"), Comm::data("d")); + Store::remove_from_set(h, Comm::data("myset"), Comm::data("b")); + Store::push_left(h, Comm::data("myvec"), dv(Comm::data("delta"))); + Store::push_right(h, Comm::data("myvec"), dv(Comm::data("omega"))); + + when ( local res = Store::size(h) ) + { event ready(); } + timeout 10sec + { print "timeout"; } + } + +event bro_init() + { + Comm::enable(); + h = Store::create_master("mystore"); + Comm::connect("127.0.0.1", broker_port, 1secs); + Comm::auto_event("bro/event/ready", ready); + } + +@TEST-END-FILE diff --git a/testing/btest/core/leaks/comm/data.bro b/testing/btest/core/leaks/comm/data.bro new file mode 100644 index 0000000000..bf614a2092 --- /dev/null +++ b/testing/btest/core/leaks/comm/data.bro @@ -0,0 +1,233 @@ +# @TEST-REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt +# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks +# @TEST-GROUP: leaks + +# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run bro bro -m -b -r $TRACES/http/get.trace %INPUT +# @TEST-EXEC: btest-bg-wait 45 +# @TEST-EXEC: btest-diff bro/.stdout + +type bro_set: set[string]; +type bro_table: table[string] of count; +type bro_vector: vector of string; + +type bro_record : record { + a: string &optional; + b: string &default = "bee"; + c: count; +}; + +function comm_record_to_bro_record_recurse(it: opaque of Comm::RecordIterator, + rval: bro_record, + idx: count): bro_record + { + if ( Comm::record_iterator_last(it) ) + return rval; + + local field_value = Comm::record_iterator_value(it); + + if ( field_value?$d ) + switch ( idx ) { + case 0: + rval$a = Comm::refine_to_string(field_value); + break; + case 1: + rval$b = Comm::refine_to_string(field_value); + break; + case 2: + rval$c = Comm::refine_to_count(field_value); + break; + }; + + ++idx; + Comm::record_iterator_next(it); + return comm_record_to_bro_record_recurse(it, rval, idx); + } + +function comm_record_to_bro_record(d: Comm::Data): bro_record + { + return comm_record_to_bro_record_recurse(Comm::record_iterator(d), + bro_record($c = 0), 0); + } + +function +comm_set_to_bro_set_recurse(it: opaque of Comm::SetIterator, + rval: bro_set): bro_set + { + if ( Comm::set_iterator_last(it) ) + return rval; + + add rval[Comm::refine_to_string(Comm::set_iterator_value(it))]; + Comm::set_iterator_next(it); + return comm_set_to_bro_set_recurse(it, rval); + } + + +function comm_set_to_bro_set(d: Comm::Data): bro_set + { + return comm_set_to_bro_set_recurse(Comm::set_iterator(d), bro_set()); + } + +function +comm_table_to_bro_table_recurse(it: opaque of Comm::TableIterator, + rval: bro_table): bro_table + { + if ( Comm::table_iterator_last(it) ) + return rval; + + local item = Comm::table_iterator_value(it); + rval[Comm::refine_to_string(item$key)] = Comm::refine_to_count(item$val); + Comm::table_iterator_next(it); + return comm_table_to_bro_table_recurse(it, rval); + } + +function comm_table_to_bro_table(d: Comm::Data): bro_table + { + return comm_table_to_bro_table_recurse(Comm::table_iterator(d), + bro_table()); + } + +function comm_vector_to_bro_vector_recurse(it: opaque of Comm::VectorIterator, + rval: bro_vector): bro_vector + { + if ( Comm::vector_iterator_last(it) ) + return rval; + + rval[|rval|] = Comm::refine_to_string(Comm::vector_iterator_value(it)); + Comm::vector_iterator_next(it); + return comm_vector_to_bro_vector_recurse(it, rval); + } + +function comm_vector_to_bro_vector(d: Comm::Data): bro_vector + { + return comm_vector_to_bro_vector_recurse(Comm::vector_iterator(d), + bro_vector()); + } + +event bro_init() + { +Comm::enable(); + } + +global did_it = F; + +event new_connection(c: connection) + { +if ( did_it ) return; +did_it = T; +print Comm::data_type(Comm::data(T)); +print Comm::data_type(Comm::data(+1)); +print Comm::data_type(Comm::data(1)); +print Comm::data_type(Comm::data(1.1)); +print Comm::data_type(Comm::data("1 (how creative)")); +print Comm::data_type(Comm::data(1.1.1.1)); +print Comm::data_type(Comm::data(1.1.1.1/1)); +print Comm::data_type(Comm::data(1/udp)); +print Comm::data_type(Comm::data(double_to_time(1))); +print Comm::data_type(Comm::data(1sec)); +print Comm::data_type(Comm::data(Comm::BOOL)); +local s: bro_set = bro_set("one", "two", "three"); +local t: bro_table = bro_table(["one"] = 1, ["two"] = 2, ["three"] = 3); +local v: bro_vector = bro_vector("zero", "one", "two"); +local r: bro_record = bro_record($c = 1); +print Comm::data_type(Comm::data(s)); +print Comm::data_type(Comm::data(t)); +print Comm::data_type(Comm::data(v)); +print Comm::data_type(Comm::data(r)); + +print "***************************"; + +print Comm::refine_to_bool(Comm::data(T)); +print Comm::refine_to_bool(Comm::data(F)); +print Comm::refine_to_int(Comm::data(+1)); +print Comm::refine_to_int(Comm::data(+0)); +print Comm::refine_to_int(Comm::data(-1)); +print Comm::refine_to_count(Comm::data(1)); +print Comm::refine_to_count(Comm::data(0)); +print Comm::refine_to_double(Comm::data(1.1)); +print Comm::refine_to_double(Comm::data(-11.1)); +print Comm::refine_to_string(Comm::data("hello")); +print Comm::refine_to_addr(Comm::data(1.2.3.4)); +print Comm::refine_to_subnet(Comm::data(192.168.1.1/16)); +print Comm::refine_to_port(Comm::data(22/tcp)); +print Comm::refine_to_time(Comm::data(double_to_time(42))); +print Comm::refine_to_interval(Comm::data(3min)); +print Comm::refine_to_enum_name(Comm::data(Comm::BOOL)); + +print "***************************"; + +local cs = Comm::data(s); +print comm_set_to_bro_set(cs); +cs = Comm::set_create(); +print Comm::set_size(cs); +print Comm::set_insert(cs, Comm::data("hi")); +print Comm::set_size(cs); +print Comm::set_contains(cs, Comm::data("hi")); +print Comm::set_contains(cs, Comm::data("bye")); +print Comm::set_insert(cs, Comm::data("bye")); +print Comm::set_size(cs); +print Comm::set_remove(cs, Comm::data("hi")); +print Comm::set_size(cs); +print Comm::set_remove(cs, Comm::data("hi")); +print comm_set_to_bro_set(cs); +Comm::set_clear(cs); +print Comm::set_size(cs); + +print "***************************"; + +local ct = Comm::data(t); +print comm_table_to_bro_table(ct); +ct = Comm::table_create(); +print Comm::table_size(ct); +print Comm::table_insert(ct, Comm::data("hi"), Comm::data(42)); +print Comm::table_size(ct); +print Comm::table_contains(ct, Comm::data("hi")); +print Comm::refine_to_count(Comm::table_lookup(ct, Comm::data("hi"))); +print Comm::table_contains(ct, Comm::data("bye")); +print Comm::table_insert(ct, Comm::data("bye"), Comm::data(7)); +print Comm::table_size(ct); +print Comm::table_insert(ct, Comm::data("bye"), Comm::data(37)); +print Comm::table_size(ct); +print Comm::refine_to_count(Comm::table_lookup(ct, Comm::data("bye"))); +print Comm::table_remove(ct, Comm::data("hi")); +print Comm::table_size(ct); + +print "***************************"; + +local cv = Comm::data(v); +print comm_vector_to_bro_vector(cv); +cv = Comm::vector_create(); +print Comm::vector_size(cv); +print Comm::vector_insert(cv, Comm::data("hi"), 0); +print Comm::vector_insert(cv, Comm::data("hello"), 1); +print Comm::vector_insert(cv, Comm::data("greetings"), 2); +print Comm::vector_insert(cv, Comm::data("salutations"), 1); +print comm_vector_to_bro_vector(cv); +print Comm::vector_size(cv); +print Comm::vector_replace(cv, Comm::data("bah"), 2); +print Comm::vector_lookup(cv, 2); +print Comm::vector_lookup(cv, 0); +print comm_vector_to_bro_vector(cv); +print Comm::vector_remove(cv, 2); +print comm_vector_to_bro_vector(cv); +print Comm::vector_size(cv); + +print "***************************"; + +local cr = Comm::data(r); +print comm_record_to_bro_record(cr); +r$a = "test"; +cr = Comm::data(r); +print comm_record_to_bro_record(cr); +r$b = "testagain"; +cr = Comm::data(r); +print comm_record_to_bro_record(cr); +cr = Comm::record_create(3); +print Comm::record_size(cr); +print Comm::record_assign(cr, Comm::data("hi"), 0); +print Comm::record_assign(cr, Comm::data("hello"), 1); +print Comm::record_assign(cr, Comm::data(37), 2); +print Comm::record_lookup(cr, 0); +print Comm::record_lookup(cr, 1); +print Comm::record_lookup(cr, 2); +print Comm::record_size(cr); +} diff --git a/testing/btest/core/leaks/comm/master_store.bro b/testing/btest/core/leaks/comm/master_store.bro new file mode 100644 index 0000000000..a5c1063e6f --- /dev/null +++ b/testing/btest/core/leaks/comm/master_store.bro @@ -0,0 +1,155 @@ +# @TEST-REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt +# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks +# @TEST-GROUP: leaks + +# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run bro bro -m -b -r $TRACES/http/get.trace %INPUT +# @TEST-EXEC: btest-bg-wait 45 +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-sort btest-diff bro/.stdout + +redef exit_only_after_terminate = T; + +global h: opaque of Store::Handle; +global lookup_count = 0; +const lookup_expect_count = 5; +global exists_count = 0; +const exists_expect_count = 4; +global pop_count = 0; +const pop_expect_count = 2; + +global test_size: event(where: string &default = ""); + +event test_clear() + { + Store::clear(h); + event test_size("after clear"); + } + +event test_size(where: string) + { + when ( local res = Store::size(h) ) + { + if ( where == "" ) + { + print fmt("size: %s", res); + event test_clear(); + } + else + { + print fmt("size (%s): %s", where, res); + terminate(); + } + } + timeout 10sec + { print "timeout"; } + } + +event test_keys() + { + when ( local res = Store::keys(h) ) + { + print fmt("keys: %s", res); + event test_size(); + } + timeout 10sec + { print "timeout"; } + } + +event test_pop(key: string) + { + when ( local lres = Store::pop_left(h, Comm::data(key)) ) + { + print fmt("pop_left(%s): %s", key, lres); + ++pop_count; + + if ( pop_count == pop_expect_count ) + event test_keys(); + } + timeout 10sec + { print "timeout"; } + + when ( local rres = Store::pop_right(h, Comm::data(key)) ) + { + print fmt("pop_right(%s): %s", key, rres); + ++pop_count; + + if ( pop_count == pop_expect_count ) + event test_keys(); + } + timeout 10sec + { print "timeout"; } + } + +function do_exists(key: string) + { + when ( local res = Store::exists(h, Comm::data(key)) ) + { + print fmt("exists(%s): %s", key, res); + ++exists_count; + + if ( exists_count == exists_expect_count ) + event test_pop("myvec"); + } + timeout 10sec + { print "timeout"; } + } + +event test_erase() + { + Store::erase(h, Comm::data("two")); + do_exists("one"); + do_exists("two"); + do_exists("myset"); + do_exists("four"); + } + +function do_lookup(key: string) + { + when ( local res = Store::lookup(h, Comm::data(key)) ) + { + print fmt("lookup(%s): %s", key, res); + ++lookup_count; + + if ( lookup_count == lookup_expect_count ) + event test_erase(); + } + timeout 10sec + { print "timeout"; } + } + +function dv(d: Comm::Data): Comm::DataVector + { + local rval: Comm::DataVector; + rval[0] = d; + return rval; + } + +global did_it = F; + +event bro_init() + { + Comm::enable(); + h = Store::create_master("master"); + } + +event new_connection(c: connection) + { + if ( did_it ) return; + did_it = T; + local myset: set[string] = {"a", "b", "c"}; + local myvec: vector of string = {"alpha", "beta", "gamma"}; + Store::insert(h, Comm::data("one"), Comm::data(110)); + Store::insert(h, Comm::data("two"), Comm::data(223)); + Store::insert(h, Comm::data("myset"), Comm::data(myset)); + Store::insert(h, Comm::data("myvec"), Comm::data(myvec)); + Store::increment(h, Comm::data("one")); + Store::decrement(h, Comm::data("two")); + Store::add_to_set(h, Comm::data("myset"), Comm::data("d")); + Store::remove_from_set(h, Comm::data("myset"), Comm::data("b")); + Store::push_left(h, Comm::data("myvec"), dv(Comm::data("delta"))); + Store::push_right(h, Comm::data("myvec"), dv(Comm::data("omega"))); + do_lookup("one"); + do_lookup("two"); + do_lookup("myset"); + do_lookup("four"); + do_lookup("myvec"); + } diff --git a/testing/btest/core/leaks/comm/remote_event.test b/testing/btest/core/leaks/comm/remote_event.test new file mode 100644 index 0000000000..a329b527db --- /dev/null +++ b/testing/btest/core/leaks/comm/remote_event.test @@ -0,0 +1,96 @@ +# @TEST-SERIALIZE: brokercomm +# @TEST-REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt +# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks +# @TEST-GROUP: leak + +# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run recv "bro -m -b ../recv.bro broker_port=$BROKER_PORT >recv.out" +# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run send "bro -m -b ../send.bro broker_port=$BROKER_PORT >send.out" + +# @TEST-EXEC: btest-bg-wait 45 +# @TEST-EXEC: btest-diff recv/recv.out +# @TEST-EXEC: btest-diff send/send.out + +@TEST-START-FILE recv.bro + +const broker_port: port &redef; +redef exit_only_after_terminate = T; + +global event_handler: event(msg: string, c: count); +global auto_event_handler: event(msg: string, c: count); + +event bro_init() + { + Comm::enable(); + Comm::listen(broker_port, "127.0.0.1"); + Comm::subscribe_to_events("bro/event/"); + Comm::auto_event("bro/event/my_topic", auto_event_handler); + } + +global event_count = 0; +global events_to_recv = 6; + +event event_handler(msg: string, n: count) + { + ++event_count; + print "got event msg", msg, n; + + if ( event_count == events_to_recv ) + { + terminate(); + return; + } + + event auto_event_handler(msg, n); + local args = Comm::event_args(event_handler, "pong", n); + Comm::event("bro/event/my_topic", args); + } + +@TEST-END-FILE + +@TEST-START-FILE send.bro + +const broker_port: port &redef; +redef exit_only_after_terminate = T; + +global event_handler: event(msg: string, c: count); +global auto_event_handler: event(msg: string, c: count); + +event bro_init() + { + Comm::enable(); + Comm::subscribe_to_events("bro/event/my_topic"); + Comm::connect("127.0.0.1", broker_port, 1secs); + } + +global event_count = 0; + +event Comm::outgoing_connection_established(peer_address: string, + peer_port: port, + peer_name: string) + { + print "Comm::outgoing_connection_established", peer_address, peer_port; + local args = Comm::event_args(event_handler, "ping", event_count); + Comm::event("bro/event/hi", args); + ++event_count; + } + +event Comm::outgoing_connection_broken(peer_address: string, + peer_port: port) + { + terminate(); + } + +event event_handler(msg: string, n: count) + { + print "got event msg", msg, n; + local args = Comm::event_args(event_handler, "ping", event_count); + Comm::event("bro/event/hi", args); + ++event_count; + } + +event auto_event_handler(msg: string, n: count) + { + print "got auto event msg", msg, n; + } + +@TEST-END-FILE diff --git a/testing/btest/core/leaks/comm/remote_log.test b/testing/btest/core/leaks/comm/remote_log.test new file mode 100644 index 0000000000..6f20bf8cd4 --- /dev/null +++ b/testing/btest/core/leaks/comm/remote_log.test @@ -0,0 +1,98 @@ +# @TEST-SERIALIZE: brokercomm +# @TEST-REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt +# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks +# @TEST-GROUP: leak + +# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run recv "bro -m -b ../common.bro ../recv.bro broker_port=$BROKER_PORT >recv.out" +# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run send "bro -m -b ../common.bro ../send.bro broker_port=$BROKER_PORT >send.out" + +# @TEST-EXEC: btest-bg-wait 45 +# @TEST-EXEC: btest-diff recv/recv.out +# @TEST-EXEC: btest-diff recv/test.log +# @TEST-EXEC: btest-diff send/send.out +# @TEST-EXEC: btest-diff send/test.log + +@TEST-START-FILE common.bro + +module Test; + +export { + redef enum Log::ID += { LOG }; + + type Info: record { + msg: string &log; + num: count &log; + }; + + global log_test: event(rec: Test::Info); +} + +event bro_init() &priority=5 + { + Comm::enable(); + Log::create_stream(Test::LOG, [$columns=Test::Info, $ev=log_test]); + } + +@TEST-END-FILE + +@TEST-START-FILE recv.bro + +const broker_port: port &redef; +redef exit_only_after_terminate = T; + +event bro_init() + { + Comm::listen(broker_port, "127.0.0.1"); + Comm::subscribe_to_logs("bro/log/"); + } + +event Test::log_test(rec: Test::Info) + { + print "wrote log", rec; + + if ( rec$num == 5 ) + terminate(); + } + +@TEST-END-FILE + +@TEST-START-FILE send.bro + +const broker_port: port &redef; +redef exit_only_after_terminate = T; + +event bro_init() + { + Comm::enable_remote_logs(Test::LOG); + Comm::connect("127.0.0.1", broker_port, 1secs); + } + +global n = 0; + +event do_write() + { + if ( n == 6 ) + return; + else + { + Log::write(Test::LOG, [$msg = "ping", $num = n]); + ++n; + event do_write(); + } + } + +event Comm::outgoing_connection_established(peer_address: string, + peer_port: port, + peer_name: string) + { + print "Comm::outgoing_connection_established", peer_address, peer_port; + event do_write(); + } + +event Comm::outgoing_connection_broken(peer_address: string, + peer_port: port) + { + terminate(); + } + +@TEST-END-FILE diff --git a/testing/btest/core/leaks/comm/remote_print.test b/testing/btest/core/leaks/comm/remote_print.test new file mode 100644 index 0000000000..43fe50b632 --- /dev/null +++ b/testing/btest/core/leaks/comm/remote_print.test @@ -0,0 +1,85 @@ +# @TEST-SERIALIZE: brokercomm +# @TEST-REQUIRES: grep -q ENABLE_BROKER $BUILD/CMakeCache.txt +# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks +# @TEST-GROUP: leak + +# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run recv "bro -m -b ../recv.bro broker_port=$BROKER_PORT >recv.out" +# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run send "bro -m -b ../send.bro broker_port=$BROKER_PORT >send.out" + +# @TEST-EXEC: btest-bg-wait 45 +# @TEST-EXEC: btest-diff recv/recv.out +# @TEST-EXEC: btest-diff send/send.out + +@TEST-START-FILE recv.bro + +const broker_port: port &redef; +redef exit_only_after_terminate = T; + +event bro_init() + { + Comm::enable(); + Comm::listen(broker_port, "127.0.0.1"); + Comm::subscribe_to_prints("bro/print/"); + } + +global messages_to_recv = 6; +global messages_sent = 0; +global messages_recv = 0; + +event Comm::print_handler(msg: string) + { + ++messages_recv; + print "got print msg", msg; + + if ( messages_to_recv == messages_recv ) + { + terminate(); + return; + } + + Comm::print("bro/print/my_topic", fmt("pong %d", messages_sent)); + ++messages_sent; + } + +@TEST-END-FILE + +@TEST-START-FILE send.bro + +const broker_port: port &redef; +redef exit_only_after_terminate = T; + +event bro_init() + { + Comm::enable(); + Comm::subscribe_to_prints("bro/print/my_topic"); + Comm::connect("127.0.0.1", broker_port, 1secs); + } + +global messages_sent = 0; +global messages_recv = 0; +global peer_disconnected = F; + +event Comm::outgoing_connection_established(peer_address: string, + peer_port: port, + peer_name: string) + { + print "Comm::outgoing_connection_established", peer_address, peer_port; + Comm::print("bro/print/hi", fmt("ping %d", messages_sent)); + ++messages_sent; + } + +event Comm::outgoing_connection_broken(peer_address: string, + peer_port: port) + { + terminate(); + } + +event Comm::print_handler(msg: string) + { + ++messages_recv; + print "got print msg", msg; + Comm::print("bro/print/hi", fmt("ping %d", messages_sent)); + ++messages_sent; + } + +@TEST-END-FILE From 961fd06cad004f1f167ebbf65f241349a2ea9b63 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Thu, 12 Feb 2015 17:06:38 -0600 Subject: [PATCH 129/299] Refactor SOCKS5 user/pass authentication support. - Rename event "socks_login_userpass" to "socks_login_userpass_request" - Rename event "socks_login_reply" to "socks_login_userpass_reply" - Split unsupported authN weird into 2 types: method vs. version Addresses BIT-1011 --- scripts/base/protocols/socks/main.bro | 4 +- src/analyzer/protocol/socks/events.bif | 6 +-- .../protocol/socks/socks-analyzer.pac | 40 ++++++++------ .../protocol/socks/socks-protocol.pac | 53 +++++++++++++++++-- src/analyzer/protocol/socks/socks.pac | 2 +- testing/btest/Baseline/plugins.writer/output | 4 +- 6 files changed, 82 insertions(+), 27 deletions(-) diff --git a/scripts/base/protocols/socks/main.bro b/scripts/base/protocols/socks/main.bro index f60c3ce41c..e052962888 100644 --- a/scripts/base/protocols/socks/main.bro +++ b/scripts/base/protocols/socks/main.bro @@ -94,7 +94,7 @@ event socks_reply(c: connection, version: count, reply: count, sa: SOCKS::Addres Log::write(SOCKS::LOG, c$socks); } -event socks_login_userpass(c: connection, user: string, password: string) &priority=5 +event socks_login_userpass_request(c: connection, user: string, password: string) &priority=5 { # Authentication only possible with the version 5. set_session(c, 5); @@ -103,7 +103,7 @@ event socks_login_userpass(c: connection, user: string, password: string) &prior c$socks$password = password; } -event socks_login_reply(c: connection, code: count) &priority=5 +event socks_login_userpass_reply(c: connection, code: count) &priority=5 { # Authentication only possible with the version 5. set_session(c, 5); diff --git a/src/analyzer/protocol/socks/events.bif b/src/analyzer/protocol/socks/events.bif index ece69140a1..224f570817 100644 --- a/src/analyzer/protocol/socks/events.bif +++ b/src/analyzer/protocol/socks/events.bif @@ -34,12 +34,12 @@ event socks_reply%(c: connection, version: count, reply: count, sa: SOCKS::Addre ## user: The given username. ## ## password: The given password. -event socks_login_userpass%(c: connection, user: string, password: string%); +event socks_login_userpass_request%(c: connection, user: string, password: string%); -## Generated when a SOCKS server replies to a login attempt. +## Generated when a SOCKS server replies to a username/password login attempt. ## ## c: The parent connection of the proxy. ## ## code: The response code for the attempted login. -event socks_login_reply%(c: connection, code: count%); +event socks_login_userpass_reply%(c: connection, code: count%); diff --git a/src/analyzer/protocol/socks/socks-analyzer.pac b/src/analyzer/protocol/socks/socks-analyzer.pac index 7d634e2f46..b8c4165a54 100644 --- a/src/analyzer/protocol/socks/socks-analyzer.pac +++ b/src/analyzer/protocol/socks/socks-analyzer.pac @@ -148,28 +148,34 @@ refine connection SOCKS_Conn += { return true; %} - function socks5_auth_request_userpass(request: SOCKS5_Auth_Request_UserPass): bool + function socks5_auth_request_userpass(request: SOCKS5_Auth_Request_UserPass_v1): bool %{ StringVal* user = new StringVal(${request.username}.length(), (const char*) ${request.username}.begin()); StringVal* pass = new StringVal(${request.password}.length(), (const char*) ${request.password}.begin()); - BifEvent::generate_socks_login_userpass(bro_analyzer(), - bro_analyzer()->Conn(), - user, pass); + BifEvent::generate_socks_login_userpass_request(bro_analyzer(), + bro_analyzer()->Conn(), + user, pass); return true; %} - function socks5_unsupported_authentication(auth_method: uint8): bool + function socks5_unsupported_authentication_method(auth_method: uint8): bool %{ - reporter->Weird(bro_analyzer()->Conn(), fmt("socks5_unsupported_authentication_%d", auth_method)); + reporter->Weird(bro_analyzer()->Conn(), fmt("socks5_unsupported_authentication_method_%d", auth_method)); + return true; + %} + + function socks5_unsupported_authentication_version(auth_method: uint8, version: uint8): bool + %{ + reporter->Weird(bro_analyzer()->Conn(), fmt("socks5_unsupported_authentication_%d_%d", auth_method, version)); return true; %} - function socks5_auth_reply(reply: SOCKS5_Auth_Reply): bool + function socks5_auth_reply_userpass(reply: SOCKS5_Auth_Reply_UserPass_v1): bool %{ - BifEvent::generate_socks_login_reply(bro_analyzer(), - bro_analyzer()->Conn(), - ${reply.code}); + BifEvent::generate_socks_login_userpass_reply(bro_analyzer(), + bro_analyzer()->Conn(), + ${reply.code}); return true; %} @@ -205,14 +211,18 @@ refine typeattr SOCKS5_Reply += &let { refine typeattr SOCKS5_Auth_Negotiation_Reply += &let { }; -refine typeattr SOCKS5_Auth_Request_UserPass += &let { +refine typeattr SOCKS5_Auth_Request_UserPass_v1 += &let { proc: bool = $context.connection.socks5_auth_request_userpass(this); }; -refine typeattr SOCKS5_Auth_Reply += &let { - proc: bool = $context.connection.socks5_auth_reply(this); +refine typeattr SOCKS5_Auth_Reply_UserPass_v1 += &let { + proc: bool = $context.connection.socks5_auth_reply_userpass(this); }; -refine typeattr SOCKS5_Unsupported_Authentication += &let { - proc: bool = $context.connection.socks5_unsupported_authentication($context.connection.v5_auth_method()); +refine typeattr SOCKS5_Unsupported_Authentication_Method += &let { + proc: bool = $context.connection.socks5_unsupported_authentication_method($context.connection.v5_auth_method()); +}; + +refine typeattr SOCKS5_Unsupported_Authentication_Version += &let { + proc: bool = $context.connection.socks5_unsupported_authentication_version($context.connection.v5_auth_method(), version); }; diff --git a/src/analyzer/protocol/socks/socks-protocol.pac b/src/analyzer/protocol/socks/socks-protocol.pac index 4e48ea0672..d9c31d2377 100644 --- a/src/analyzer/protocol/socks/socks-protocol.pac +++ b/src/analyzer/protocol/socks/socks-protocol.pac @@ -1,8 +1,12 @@ +type SOCKS_Message(is_orig: bool) = case $context.connection.v5_in_auth_sub_negotiation() of { + true -> auth: SOCKS5_Auth_Message(is_orig); + false -> msg: SOCKS_Version(is_orig); +}; + type SOCKS_Version(is_orig: bool) = record { version: uint8; msg: case version of { - 1 -> socks5_auth_msg: SOCKS5_Auth_Message(is_orig); 4 -> socks4_msg: SOCKS4_Message(is_orig); 5 -> socks5_msg: SOCKS5_Message(is_orig); default -> socks_msg_fail: SOCKS_Version_Error(version); @@ -33,6 +37,7 @@ type SOCKS5_Auth_Negotiation_Request = record { type SOCKS5_Auth_Negotiation_Reply = record { selected_auth_method: uint8; } &let { + in_auth_sub_neg = $context.connection.set_v5_in_auth_sub_negotiation(selected_auth_method == 0 || selected_auth_method == 0xff ? false : true); past_auth = $context.connection.set_v5_past_authentication(); set_auth = $context.connection.set_v5_auth_method(selected_auth_method); }; @@ -44,21 +49,48 @@ type SOCKS5_Auth_Message(is_orig: bool) = case is_orig of { type SOCKS5_Auth_Request = case $context.connection.v5_auth_method() of { 0x02 -> userpass : SOCKS5_Auth_Request_UserPass; - default -> unsupported : SOCKS5_Unsupported_Authentication; + default -> unsupported : SOCKS5_Unsupported_Authentication_Method; }; -type SOCKS5_Unsupported_Authentication = record { +type SOCKS5_Unsupported_Authentication_Method = record { + crap: bytestring &restofdata; +}; + +type SOCKS5_Unsupported_Authentication_Version(version: uint8) = record { crap: bytestring &restofdata; }; type SOCKS5_Auth_Request_UserPass = record { + version: uint8; + msg: case version of { + 1 -> v1: SOCKS5_Auth_Request_UserPass_v1; + default -> unsupported: SOCKS5_Unsupported_Authentication_Version(version); + }; +}; + +type SOCKS5_Auth_Request_UserPass_v1 = record { ulen : uint8; username : bytestring &length=ulen; plen : uint8; password : bytestring &length=plen; }; -type SOCKS5_Auth_Reply = record { +type SOCKS5_Auth_Reply = case $context.connection.v5_auth_method() of { + 0x02 -> userpass : SOCKS5_Auth_Reply_UserPass; + default -> unsupported : SOCKS5_Unsupported_Authentication_Method; +} &let { + in_auth_sub_neg = $context.connection.set_v5_in_auth_sub_negotiation(false); +}; + +type SOCKS5_Auth_Reply_UserPass = record { + version: uint8; + msg: case version of { + 1 -> v1: SOCKS5_Auth_Reply_UserPass_v1; + default -> unsupported: SOCKS5_Unsupported_Authentication_Version(version); + }; +}; + +type SOCKS5_Auth_Reply_UserPass_v1 = record { code : uint8; }; @@ -126,15 +158,28 @@ type SOCKS4_Reply = record { refine connection SOCKS_Conn += { %member{ + bool v5_in_auth_sub_negotiation_; bool v5_authenticated_; uint8 selected_auth_method_; %} %init{ + v5_in_auth_sub_negotiation_ = false; v5_authenticated_ = false; selected_auth_method_ = 255; %} + function v5_in_auth_sub_negotiation(): bool + %{ + return v5_in_auth_sub_negotiation_; + %} + + function set_v5_in_auth_sub_negotiation(b: bool): bool + %{ + v5_in_auth_sub_negotiation_ = b; + return true; + %} + function v5_past_authentication(): bool %{ return v5_authenticated_; diff --git a/src/analyzer/protocol/socks/socks.pac b/src/analyzer/protocol/socks/socks.pac index a9c4099508..9aed2820af 100644 --- a/src/analyzer/protocol/socks/socks.pac +++ b/src/analyzer/protocol/socks/socks.pac @@ -20,7 +20,7 @@ connection SOCKS_Conn(bro_analyzer: BroAnalyzer) { %include socks-protocol.pac flow SOCKS_Flow(is_orig: bool) { - datagram = SOCKS_Version(is_orig) withcontext(connection, this); + datagram = SOCKS_Message(is_orig) withcontext(connection, this); }; %include socks-analyzer.pac diff --git a/testing/btest/Baseline/plugins.writer/output b/testing/btest/Baseline/plugins.writer/output index 0882718f03..f7b33992ea 100644 --- a/testing/btest/Baseline/plugins.writer/output +++ b/testing/btest/Baseline/plugins.writer/output @@ -17,6 +17,6 @@ Demo::Foo - A Foo test logging writer (dynamic, version 1.0) [http] 1340213020.732963|CjhGID4nQcgTWjvg4c|10.0.0.55|53994|60.190.189.214|8124|5|GET|www.osnews.com|/images/icons/17.gif|http://www.osnews.com/|Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:10.0.2) Gecko/20100101 Firefox/10.0.2|0|0|304|Not Modified|-|-|-||-|-|-|-|-|-|- [http] 1340213021.300269|CjhGID4nQcgTWjvg4c|10.0.0.55|53994|60.190.189.214|8124|6|GET|www.osnews.com|/images/left.gif|http://www.osnews.com/|Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:10.0.2) Gecko/20100101 Firefox/10.0.2|0|0|304|Not Modified|-|-|-||-|-|-|-|-|-|- [http] 1340213021.861584|CjhGID4nQcgTWjvg4c|10.0.0.55|53994|60.190.189.214|8124|7|GET|www.osnews.com|/images/icons/32.gif|http://www.osnews.com/|Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:10.0.2) Gecko/20100101 Firefox/10.0.2|0|0|304|Not Modified|-|-|-||-|-|-|-|-|-|- -[packet_filter] 1412721099.419280|bro|ip or not ip|T|T -[socks] 1340213015.276495|CjhGID4nQcgTWjvg4c|10.0.0.55|53994|60.190.189.214|8124|5|-|succeeded|-|www.osnews.com|80|192.168.0.31|-|2688 +[packet_filter] 1423781675.402129|bro|ip or not ip|T|T +[socks] 1340213015.276495|CjhGID4nQcgTWjvg4c|10.0.0.55|53994|60.190.189.214|8124|5|-|-|succeeded|-|www.osnews.com|80|192.168.0.31|-|2688 [tunnel] 1340213015.276495|-|10.0.0.55|0|60.190.189.214|8124|Tunnel::SOCKS|Tunnel::DISCOVER From 062baefde09483277bbc94574cfc45793779f98e Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Fri, 13 Feb 2015 11:24:32 -0600 Subject: [PATCH 130/299] Add 'while' statement to Bro language. --- src/SerialTypes.h | 1 + src/Stmt.cc | 122 +++++++++++++++++++++- src/Stmt.h | 29 +++++ src/StmtEnums.h | 1 + src/parse.y | 6 ++ src/scan.l | 1 + testing/btest/Baseline/language.while/out | 12 +++ testing/btest/core/leaks/while.bro | 80 ++++++++++++++ testing/btest/language/while.bro | 77 ++++++++++++++ 9 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 testing/btest/Baseline/language.while/out create mode 100644 testing/btest/core/leaks/while.bro create mode 100644 testing/btest/language/while.bro diff --git a/src/SerialTypes.h b/src/SerialTypes.h index d2f227838c..e50ec3889f 100644 --- a/src/SerialTypes.h +++ b/src/SerialTypes.h @@ -181,6 +181,7 @@ SERIAL_STMT(INIT_STMT, 17) SERIAL_STMT(NULL_STMT, 18) SERIAL_STMT(WHEN_STMT, 19) SERIAL_STMT(FALLTHROUGH_STMT, 20) +SERIAL_STMT(WHILE_STMT, 21) #define SERIAL_TYPE(name, val) SERIAL_CONST(name, val, BRO_TYPE) SERIAL_TYPE(BRO_TYPE, 1) diff --git a/src/Stmt.cc b/src/Stmt.cc index cb716b3f15..d2f8c48cee 100644 --- a/src/Stmt.cc +++ b/src/Stmt.cc @@ -23,7 +23,7 @@ const char* stmt_name(BroStmtTag t) "print", "event", "expr", "if", "when", "switch", "for", "next", "break", "return", "add", "delete", "list", "bodylist", - "", "fallthrough", + "", "fallthrough", "while", "null", }; @@ -1127,6 +1127,126 @@ bool EventStmt::DoUnserialize(UnserialInfo* info) return event_expr != 0; } +WhileStmt::WhileStmt(Expr* arg_loop_condition, Stmt* arg_body) + : loop_condition(arg_loop_condition), body(arg_body) + { + if ( ! loop_condition->IsError() && + ! IsBool(loop_condition->Type()->Tag()) ) + loop_condition->Error("while conditional must be boolean"); + } + +WhileStmt::~WhileStmt() + { + Unref(loop_condition); + Unref(body); + } + +int WhileStmt::IsPure() const + { + return loop_condition->IsPure() && body->IsPure(); + } + +void WhileStmt::Describe(ODesc* d) const + { + Stmt::Describe(d); + + if ( d->IsReadable() ) + d->Add("("); + + loop_condition->Describe(d); + + if ( d->IsReadable() ) + d->Add(")"); + + d->SP(); + d->PushIndent(); + body->AccessStats(d); + body->Describe(d); + d->PopIndent(); + } + +TraversalCode WhileStmt::Traverse(TraversalCallback* cb) const + { + TraversalCode tc = cb->PreStmt(this); + HANDLE_TC_STMT_PRE(tc); + + tc = loop_condition->Traverse(cb); + HANDLE_TC_STMT_PRE(tc); + + tc = body->Traverse(cb); + HANDLE_TC_STMT_PRE(tc); + + tc = cb->PostStmt(this); + HANDLE_TC_STMT_POST(tc); + } + +Val* WhileStmt::Exec(Frame* f, stmt_flow_type& flow) const + { + RegisterAccess(); + flow = FLOW_NEXT; + Val* rval = 0; + + for ( ; ; ) + { + Val* cond = loop_condition->Eval(f); + + if ( ! cond ) + break; + + bool cont = cond->AsBool(); + Unref(cond); + + if ( ! cont ) + break; + + flow = FLOW_NEXT; + rval = body->Exec(f, flow); + + if ( flow == FLOW_BREAK || flow == FLOW_RETURN ) + break; + } + + if ( flow == FLOW_LOOP || flow == FLOW_BREAK ) + flow = FLOW_NEXT; + + return rval; + } + +Stmt* WhileStmt::Simplify() + { + loop_condition = simplify_expr(loop_condition, SIMPLIFY_GENERAL); + + if ( loop_condition->IsConst() && loop_condition->IsZero() ) + return new NullStmt(); + + body = simplify_stmt(body); + return this; + } + +IMPLEMENT_SERIAL(WhileStmt, SER_WHILE_STMT); + +bool WhileStmt::DoSerialize(SerialInfo* info) const + { + DO_SERIALIZE(SER_WHILE_STMT, Stmt); + + if ( ! loop_condition->Serialize(info) ) + return false; + + return body->Serialize(info); + } + +bool WhileStmt::DoUnserialize(UnserialInfo* info) + { + DO_UNSERIALIZE(Stmt); + loop_condition = Expr::Unserialize(info); + + if ( ! loop_condition ) + return false; + + body = Stmt::Unserialize(info); + return body != 0; + } + ForStmt::ForStmt(id_list* arg_loop_vars, Expr* loop_expr) : ExprStmt(STMT_FOR, loop_expr) { diff --git a/src/Stmt.h b/src/Stmt.h index 32b90b4190..79406fd51b 100644 --- a/src/Stmt.h +++ b/src/Stmt.h @@ -310,6 +310,35 @@ protected: EventExpr* event_expr; }; +class WhileStmt : public Stmt { +public: + + WhileStmt(Expr* loop_condition, Stmt* body); + + ~WhileStmt(); + + int IsPure() const; + + void Describe(ODesc* d) const; + + TraversalCode Traverse(TraversalCallback* cb) const; + +protected: + friend class Stmt; + + DECLARE_SERIAL(WhileStmt); + + WhileStmt() + { loop_condition = 0; body = 0; } + + Val* Exec(Frame* f, stmt_flow_type& flow) const; + + Stmt* Simplify(); + + Expr* loop_condition; + Stmt* body; +}; + class ForStmt : public ExprStmt { public: ForStmt(id_list* loop_vars, Expr* loop_expr); diff --git a/src/StmtEnums.h b/src/StmtEnums.h index d34f642594..ad99c2365a 100644 --- a/src/StmtEnums.h +++ b/src/StmtEnums.h @@ -17,6 +17,7 @@ typedef enum { STMT_LIST, STMT_EVENT_BODY_LIST, STMT_INIT, STMT_FALLTHROUGH, + STMT_WHILE, STMT_NULL #define NUM_STMTS (int(STMT_NULL) + 1) } BroStmtTag; diff --git a/src/parse.y b/src/parse.y index f74880dc13..8054718d45 100644 --- a/src/parse.y +++ b/src/parse.y @@ -16,6 +16,7 @@ %token TOK_REMOVE_FROM TOK_RETURN TOK_SCHEDULE TOK_SET %token TOK_STRING TOK_SUBNET TOK_SWITCH TOK_TABLE %token TOK_TIME TOK_TIMEOUT TOK_TIMER TOK_TYPE TOK_UNION TOK_VECTOR TOK_WHEN +%token TOK_WHILE %token TOK_ATTR_ADD_FUNC TOK_ATTR_ENCRYPT TOK_ATTR_DEFAULT %token TOK_ATTR_OPTIONAL TOK_ATTR_REDEF TOK_ATTR_ROTATE_INTERVAL @@ -1340,6 +1341,11 @@ stmt: $1->AsForStmt()->AddBody($2); } + | TOK_WHILE '(' expr ')' stmt + { + $$ = new WhileStmt($3, $5); + } + | TOK_NEXT ';' opt_no_test { set_location(@1, @2); diff --git a/src/scan.l b/src/scan.l index ae11382fb3..b13215e4b8 100644 --- a/src/scan.l +++ b/src/scan.l @@ -221,6 +221,7 @@ export return TOK_EXPORT; fallthrough return TOK_FALLTHROUGH; file return TOK_FILE; for return TOK_FOR; +while return TOK_WHILE; function return TOK_FUNCTION; global return TOK_GLOBAL; "?$" return TOK_HAS_FIELD; diff --git a/testing/btest/Baseline/language.while/out b/testing/btest/Baseline/language.while/out new file mode 100644 index 0000000000..d37792c0b4 --- /dev/null +++ b/testing/btest/Baseline/language.while/out @@ -0,0 +1,12 @@ +10 +s +ss +sss +{ +7, +1, +9, +5, +3 +} +[number 0, number 1, number 2, number 3, number 4, number 5, number 6, number 7, number 8, number 9, number 10, number 11, number 12] diff --git a/testing/btest/core/leaks/while.bro b/testing/btest/core/leaks/while.bro new file mode 100644 index 0000000000..eac6f2622e --- /dev/null +++ b/testing/btest/core/leaks/while.bro @@ -0,0 +1,80 @@ +# @TEST-GROUP: leaks +# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks + +# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run bro bro -m -b -r $TRACES/http/get.trace %INPUT +# @TEST-EXEC: btest-bg-wait 30 + +function test_noop() + { + while ( F ) + print "noooooooooo"; + } + +function test_it() + { + local i = 0; + + while ( i < 10 ) + ++i; + + print i; + } + +function test_break() + { + local s = ""; + + while ( T ) + { + s += "s"; + print s; + + if ( s == "sss" ) + break; + } + } + +function test_next() + { + local s: set[count]; + local i = 0; + + while ( 9 !in s ) + { + ++i; + + if ( i % 2 == 0 ) + next; + + add s[i]; + } + + print s; + } + +function test_return(): vector of string + { + local i = 0; + local rval: vector of string; + + while ( T ) + { + rval[i] = fmt("number %d", i); + ++i; + + if ( i == 13 ) + return rval; + } + + rval[0] = "noooo"; + return rval; + } + +event new_connection(c: connection) + { + test_noop(); + test_it(); + test_break(); + test_next(); + print test_return(); + } diff --git a/testing/btest/language/while.bro b/testing/btest/language/while.bro new file mode 100644 index 0000000000..6828b00b41 --- /dev/null +++ b/testing/btest/language/while.bro @@ -0,0 +1,77 @@ +# @TEST-EXEC: bro -b %INPUT >out +# @TEST-EXEC: btest-diff out + +function test_noop() + { + while ( F ) + print "noooooooooo"; + } + +function test_it() + { + local i = 0; + + while ( i < 10 ) + ++i; + + print i; + } + +function test_break() + { + local s = ""; + + while ( T ) + { + s += "s"; + print s; + + if ( s == "sss" ) + break; + } + } + +function test_next() + { + local s: set[count]; + local i = 0; + + while ( 9 !in s ) + { + ++i; + + if ( i % 2 == 0 ) + next; + + add s[i]; + } + + print s; + } + +function test_return(): vector of string + { + local i = 0; + local rval: vector of string; + + while ( T ) + { + rval[i] = fmt("number %d", i); + ++i; + + if ( i == 13 ) + return rval; + } + + rval[0] = "noooo"; + return rval; + } + +event bro_init() + { + test_noop(); + test_it(); + test_break(); + test_next(); + print test_return(); + } From 8e4f4b46f7591a3779adc8e21d688f81ee436721 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Fri, 13 Feb 2015 16:23:43 -0600 Subject: [PATCH 131/299] Updating submodule(s). [nomail] --- aux/broccoli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aux/broccoli b/aux/broccoli index d43cc790e5..9b6dd56242 160000 --- a/aux/broccoli +++ b/aux/broccoli @@ -1 +1 @@ -Subproject commit d43cc790e5b8709b5e032e52ad0e00936494739b +Subproject commit 9b6dd5624254de9d18618562887979da1158da43 From 4bcb9d2d920862660feba80cfb13356952843201 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Fri, 13 Feb 2015 18:04:17 -0600 Subject: [PATCH 132/299] Updating submodule(s). [nomail] --- aux/broccoli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aux/broccoli b/aux/broccoli index 9b6dd56242..420c5b42c0 160000 --- a/aux/broccoli +++ b/aux/broccoli @@ -1 +1 @@ -Subproject commit 9b6dd5624254de9d18618562887979da1158da43 +Subproject commit 420c5b42c0c90f22fc7a862fc491c8e554d05381 From b00bd7702f8962bcf8507adb0abe967c4c02426c Mon Sep 17 00:00:00 2001 From: Seth Hall Date: Fri, 13 Feb 2015 22:02:54 -0500 Subject: [PATCH 133/299] Add the ability to remove surrounding braces from the JSON formatter. --- src/threading/formatters/JSON.cc | 13 ++++++++++--- src/threading/formatters/JSON.h | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/threading/formatters/JSON.cc b/src/threading/formatters/JSON.cc index 472023e0f8..e1a5713461 100644 --- a/src/threading/formatters/JSON.cc +++ b/src/threading/formatters/JSON.cc @@ -15,7 +15,7 @@ using namespace threading::formatter; -JSON::JSON(MsgThread* t, TimeFormat tf) : Formatter(t) +JSON::JSON(MsgThread* t, TimeFormat tf) : Formatter(t), surrounding_braces(true) { timestamps = tf; } @@ -27,7 +27,8 @@ JSON::~JSON() bool JSON::Describe(ODesc* desc, int num_fields, const Field* const * fields, Value** vals) const { - desc->AddRaw("{"); + if ( surrounding_braces ) + desc->AddRaw("{"); for ( int i = 0; i < num_fields; i++ ) { @@ -41,7 +42,8 @@ bool JSON::Describe(ODesc* desc, int num_fields, const Field* const * fields, return false; } - desc->AddRaw("}"); + if ( surrounding_braces ) + desc->AddRaw("}"); return true; } @@ -217,3 +219,8 @@ threading::Value* JSON::ParseValue(const string& s, const string& name, TypeTag GetThread()->Error("JSON formatter does not support parsing yet."); return NULL; } + +void JSON::SurroundingBraces(bool use_braces) + { + surrounding_braces = use_braces; + } diff --git a/src/threading/formatters/JSON.h b/src/threading/formatters/JSON.h index d7859f83fb..04209fbde9 100644 --- a/src/threading/formatters/JSON.h +++ b/src/threading/formatters/JSON.h @@ -27,8 +27,11 @@ public: threading::Value** vals) const; virtual threading::Value* ParseValue(const string& s, const string& name, TypeTag type, TypeTag subtype = TYPE_ERROR) const; + void SurroundingBraces(bool use_braces); + private: TimeFormat timestamps; + bool surrounding_braces; }; }} From 46713fb5c705b8ff1f6a6312141307801577db3a Mon Sep 17 00:00:00 2001 From: Josh Liburdi Date: Sat, 14 Feb 2015 13:16:48 -0800 Subject: [PATCH 134/299] Init RDP analyzer --- scripts/base/init-default.bro | 1 + scripts/base/protocols/rdp/__load__.bro | 4 + scripts/base/protocols/rdp/consts.bro | 287 +++++++++++++++++++ scripts/base/protocols/rdp/dpd.sig | 19 ++ scripts/base/protocols/rdp/main.bro | 200 +++++++++++++ src/analyzer/protocol/CMakeLists.txt | 2 +- src/analyzer/protocol/rdp/CMakeLists.txt | 9 + src/analyzer/protocol/rdp/Plugin.cc | 22 ++ src/analyzer/protocol/rdp/RDP.cc | 70 +++++ src/analyzer/protocol/rdp/RDP.h | 48 ++++ src/analyzer/protocol/rdp/events.bif | 60 ++++ src/analyzer/protocol/rdp/rdp-analyzer.pac | 101 +++++++ src/analyzer/protocol/rdp/rdp-protocol.pac | 313 +++++++++++++++++++++ src/analyzer/protocol/rdp/rdp.pac | 26 ++ 14 files changed, 1161 insertions(+), 1 deletion(-) create mode 100644 scripts/base/protocols/rdp/__load__.bro create mode 100644 scripts/base/protocols/rdp/consts.bro create mode 100644 scripts/base/protocols/rdp/dpd.sig create mode 100644 scripts/base/protocols/rdp/main.bro create mode 100644 src/analyzer/protocol/rdp/CMakeLists.txt create mode 100644 src/analyzer/protocol/rdp/Plugin.cc create mode 100644 src/analyzer/protocol/rdp/RDP.cc create mode 100644 src/analyzer/protocol/rdp/RDP.h create mode 100644 src/analyzer/protocol/rdp/events.bif create mode 100644 src/analyzer/protocol/rdp/rdp-analyzer.pac create mode 100644 src/analyzer/protocol/rdp/rdp-protocol.pac create mode 100644 src/analyzer/protocol/rdp/rdp.pac diff --git a/scripts/base/init-default.bro b/scripts/base/init-default.bro index 04dc2a4910..2af8f3bc3d 100644 --- a/scripts/base/init-default.bro +++ b/scripts/base/init-default.bro @@ -49,6 +49,7 @@ @load base/protocols/mysql @load base/protocols/pop3 @load base/protocols/radius +@load base/protocols/rdp @load base/protocols/snmp @load base/protocols/smtp @load base/protocols/socks diff --git a/scripts/base/protocols/rdp/__load__.bro b/scripts/base/protocols/rdp/__load__.bro new file mode 100644 index 0000000000..baa1dd2eca --- /dev/null +++ b/scripts/base/protocols/rdp/__load__.bro @@ -0,0 +1,4 @@ +# Generated by binpac_quickstart +@load ./consts +@load ./main +@load-sigs ./dpd.sig diff --git a/scripts/base/protocols/rdp/consts.bro b/scripts/base/protocols/rdp/consts.bro new file mode 100644 index 0000000000..5d93e6ea5d --- /dev/null +++ b/scripts/base/protocols/rdp/consts.bro @@ -0,0 +1,287 @@ +module RDP; + +export { + # http://www.c-amie.co.uk/technical/mstsc-versions/ + const builds = { + [0419] = "RDP 4.0", + [2195] = "RDP 5.0", + [2221] = "RDP 5.0", + [2600] = "RDP 5.1", + [3790] = "RDP 5.2", + [6000] = "RDP 6.0", + [6001] = "RDP 6.1", + [6002] = "RDP 6.2", + [7600] = "RDP 7.0", + [7601] = "RDP 7.1", + [9200] = "RDP 8.0", + [9600] = "RDP 8.1", + [25189] = "RDP 8.0 (Mac)", + [25282] = "RDP 8.0 (Mac)" + } &default = function(n: count): string { return fmt("client_build-%d", n); }; + + const encryption_methods = { + [0] = "None", + [1] = "40bit", + [2] = "128bit", + [8] = "56bit", + [10] = "FIPS" + } &default = function(n: count): string { return fmt("encryption_method-%d", n); }; + + const encryption_levels = { + [0] = "None", + [1] = "Low", + [2] = "Client compatible", + [3] = "High", + [4] = "FIPS" + } &default = function(n: count): string { return fmt("encryption_level-%d", n); }; + + const results = { + [0] = "Success", + [1] = "User rejected", + [2] = "Resources not available", + [3] = "Rejected for symmetry breaking", + [4] = "Locked conference", + } &default = function(n: count): string { return fmt("result-%d", n); }; + + # http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx + const languages = { + [1078] = "Afrikaans - South Africa", + [1052] = "Albanian - Albania", + [1156] = "Alsatian", + [1118] = "Amharic - Ethiopia", + [1025] = "Arabic - Saudi Arabia", + [5121] = "Arabic - Algeria", + [15361] = "Arabic - Bahrain", + [3073] = "Arabic - Egypt", + [2049] = "Arabic - Iraq", + [11265] = "Arabic - Jordan", + [13313] = "Arabic - Kuwait", + [12289] = "Arabic - Lebanon", + [4097] = "Arabic - Libya", + [6145] = "Arabic - Morocco", + [8193] = "Arabic - Oman", + [16385] = "Arabic - Qatar", + [10241] = "Arabic - Syria", + [7169] = "Arabic - Tunisia", + [14337] = "Arabic - U.A.E.", + [9217] = "Arabic - Yemen", + [1067] = "Armenian - Armenia", + [1101] = "Assamese", + [2092] = "Azeri (Cyrillic)", + [1068] = "Azeri (Latin)", + [1133] = "Bashkir", + [1069] = "Basque", + [1059] = "Belarusian", + [1093] = "Bengali (India)", + [2117] = "Bengali (Bangladesh)", + [5146] = "Bosnian (Bosnia/Herzegovina)", + [1150] = "Breton", + [1026] = "Bulgarian", + [1109] = "Burmese", + [1027] = "Catalan", + [1116] = "Cherokee - United States", + [2052] = "Chinese - People's Republic of China", + [4100] = "Chinese - Singapore", + [1028] = "Chinese - Taiwan", + [3076] = "Chinese - Hong Kong SAR", + [5124] = "Chinese - Macao SAR", + [1155] = "Corsican", + [1050] = "Croatian", + [4122] = "Croatian (Bosnia/Herzegovina)", + [1029] = "Czech", + [1030] = "Danish", + [1164] = "Dari", + [1125] = "Divehi", + [1043] = "Dutch - Netherlands", + [2067] = "Dutch - Belgium", + [1126] = "Edo", + [1033] = "English - United States", + [2057] = "English - United Kingdom", + [3081] = "English - Australia", + [10249] = "English - Belize", + [4105] = "English - Canada", + [9225] = "English - Caribbean", + [15369] = "English - Hong Kong SAR", + [16393] = "English - India", + [14345] = "English - Indonesia", + [6153] = "English - Ireland", + [8201] = "English - Jamaica", + [17417] = "English - Malaysia", + [5129] = "English - New Zealand", + [13321] = "English - Philippines", + [18441] = "English - Singapore", + [7177] = "English - South Africa", + [11273] = "English - Trinidad", + [12297] = "English - Zimbabwe", + [1061] = "Estonian", + [1080] = "Faroese", + [1065] = "Farsi", + [1124] = "Filipino", + [1035] = "Finnish", + [1036] = "French - France", + [2060] = "French - Belgium", + [11276] = "French - Cameroon", + [3084] = "French - Canada", + [9228] = "French - Democratic Rep. of Congo", + [12300] = "French - Cote d'Ivoire", + [15372] = "French - Haiti", + [5132] = "French - Luxembourg", + [13324] = "French - Mali", + [6156] = "French - Monaco", + [14348] = "French - Morocco", + [58380] = "French - North Africa", + [8204] = "French - Reunion", + [10252] = "French - Senegal", + [4108] = "French - Switzerland", + [7180] = "French - West Indies", + [1122] = "French - West Indies", + [1127] = "Fulfulde - Nigeria", + [1071] = "FYRO Macedonian", + [1110] = "Galician", + [1079] = "Georgian", + [1031] = "German - Germany", + [3079] = "German - Austria", + [5127] = "German - Liechtenstein", + [4103] = "German - Luxembourg", + [2055] = "German - Switzerland", + [1032] = "Greek", + [1135] = "Greenlandic", + [1140] = "Guarani - Paraguay", + [1095] = "Gujarati", + [1128] = "Hausa - Nigeria", + [1141] = "Hawaiian - United States", + [1037] = "Hebrew", + [1081] = "Hindi", + [1038] = "Hungarian", + [1129] = "Ibibio - Nigeria", + [1039] = "Icelandic", + [1136] = "Igbo - Nigeria", + [1057] = "Indonesian", + [1117] = "Inuktitut", + [2108] = "Irish", + [1040] = "Italian - Italy", + [2064] = "Italian - Switzerland", + [1041] = "Japanese", + [1158] = "K'iche", + [1099] = "Kannada", + [1137] = "Kanuri - Nigeria", + [2144] = "Kashmiri", + [1120] = "Kashmiri (Arabic)", + [1087] = "Kazakh", + [1107] = "Khmer", + [1159] = "Kinyarwanda", + [1111] = "Konkani", + [1042] = "Korean", + [1088] = "Kyrgyz (Cyrillic)", + [1108] = "Lao", + [1142] = "Latin", + [1062] = "Latvian", + [1063] = "Lithuanian", + [1134] = "Luxembourgish", + [1086] = "Malay - Malaysia", + [2110] = "Malay - Brunei Darussalam", + [1100] = "Malayalam", + [1082] = "Maltese", + [1112] = "Manipuri", + [1153] = "Maori - New Zealand", + [1146] = "Mapudungun", + [1102] = "Marathi", + [1148] = "Mohawk", + [1104] = "Mongolian (Cyrillic)", + [2128] = "Mongolian (Mongolian)", + [1121] = "Nepali", + [2145] = "Nepali - India", + [1044] = "Norwegian (Bokmål)", + [2068] = "Norwegian (Nynorsk)", + [1154] = "Occitan", + [1096] = "Oriya", + [1138] = "Oromo", + [1145] = "Papiamentu", + [1123] = "Pashto", + [1045] = "Polish", + [1046] = "Portuguese - Brazil", + [2070] = "Portuguese - Portugal", + [1094] = "Punjabi", + [2118] = "Punjabi (Pakistan)", + [1131] = "Quecha - Bolivia", + [2155] = "Quecha - Ecuador", + [3179] = "Quecha - Peru CB", + [1047] = "Rhaeto-Romanic", + [1048] = "Romanian", + [2072] = "Romanian - Moldava", + [1049] = "Russian", + [2073] = "Russian - Moldava", + [1083] = "Sami (Lappish)", + [1103] = "Sanskrit", + [1084] = "Scottish Gaelic", + [1132] = "Sepedi", + [3098] = "Serbian (Cyrillic)", + [2074] = "Serbian (Latin)", + [1113] = "Sindhi - India", + [2137] = "Sindhi - Pakistan", + [1115] = "Sinhalese - Sri Lanka", + [1051] = "Slovak", + [1060] = "Slovenian", + [1143] = "Somali", + [1070] = "Sorbian", + [3082] = "Spanish - Spain (Modern Sort)", + [1034] = "Spanish - Spain (Traditional Sort)", + [11274] = "Spanish - Argentina", + [16394] = "Spanish - Bolivia", + [13322] = "Spanish - Chile", + [9226] = "Spanish - Colombia", + [5130] = "Spanish - Costa Rica", + [7178] = "Spanish - Dominican Republic", + [12298] = "Spanish - Ecuador", + [17418] = "Spanish - El Salvador", + [4106] = "Spanish - Guatemala", + [18442] = "Spanish - Honduras", + [22538] = "Spanish - Latin America", + [2058] = "Spanish - Mexico", + [19466] = "Spanish - Nicaragua", + [6154] = "Spanish - Panama", + [15370] = "Spanish - Paraguay", + [10250] = "Spanish - Peru", + [20490] = "Spanish - Puerto Rico", + [21514] = "Spanish - United States", + [14346] = "Spanish - Uruguay", + [8202] = "Spanish - Venezuela", + [1072] = "Sutu", + [1089] = "Swahili", + [1053] = "Swedish", + [2077] = "Swedish - Finland", + [1114] = "Syriac", + [1064] = "Tajik", + [1119] = "Tamazight (Arabic)", + [2143] = "Tamazight (Latin)", + [1097] = "Tamil", + [1092] = "Tatar", + [1098] = "Telugu", + [1054] = "Thai", + [2129] = "Tibetan - Bhutan", + [1105] = "Tibetan - People's Republic of China", + [2163] = "Tigrigna - Eritrea", + [1139] = "Tigrigna - Ethiopia", + [1073] = "Tsonga", + [1074] = "Tswana", + [1055] = "Turkish", + [1090] = "Turkmen", + [1152] = "Uighur - China", + [1058] = "Ukrainian", + [1056] = "Urdu", + [2080] = "Urdu - India", + [2115] = "Uzbek (Cyrillic)", + [1091] = "Uzbek (Latin)", + [1075] = "Venda", + [1066] = "Vietnamese", + [1106] = "Welsh", + [1160] = "Wolof", + [1076] = "Xhosa", + [1157] = "Yakut", + [1144] = "Yi", + [1085] = "Yiddish", + [1130] = "Yoruba", + [1077] = "Zulu", + [1279] = "HID (Human Interface Device)", + } &default = function(n: count): string { return fmt("keyboard-%d", n); }; +} diff --git a/scripts/base/protocols/rdp/dpd.sig b/scripts/base/protocols/rdp/dpd.sig new file mode 100644 index 0000000000..35aa8f9257 --- /dev/null +++ b/scripts/base/protocols/rdp/dpd.sig @@ -0,0 +1,19 @@ +# Generated by binpac_quickstart + +signature dpd_rdp_client_request { + ip-proto == tcp + payload /.*Cookie: mstshash\=.*/ + enable "rdp" +} + +signature dpd_rdp_client_header { + ip-proto == tcp + payload /.*Duca.*(rdpdr|rdpsnd|drdynvc|cliprdr).*/ + enable "rdp" +} + +signature dpd_rdp_server_response { + ip-proto == tcp + payload /.*McDn.*/ + enable "rdp" +} diff --git a/scripts/base/protocols/rdp/main.bro b/scripts/base/protocols/rdp/main.bro new file mode 100644 index 0000000000..94aa26b6ec --- /dev/null +++ b/scripts/base/protocols/rdp/main.bro @@ -0,0 +1,200 @@ +@load ./consts + +module RDP; + +export { + redef enum Log::ID += { LOG }; + + type Info: record { + ## Timestamp for when the event happened. + ts: time &log; + ## Unique ID for the connection. + uid: string &log; + ## The connection's 4-tuple of endpoint addresses/ports. + id: conn_id &log; + ## Cookie value used by the client machine. + ## This is typically a username. + cookie: string &log &optional; + ## Keyboard layout (language) of the client machine. + keyboard_layout: string &log &optional; + ## RDP client version used by the client machine. + client_build: string &log &optional; + ## Hostname of the client machine. + client_hostname: string &log &optional; + ## Product ID of the client machine. + client_product_id: string &log &optional; + ## Name of the server. + server_name: vector of string &log &optional; + ## Authentication result for the connection. This value is extracted from the payload for native authentication. + ## TODO: Perform heuristic authentication determination for NLA. + authentication_result: string &log &optional; + ## Encryption level of the connection. + encryption_level: string &log &optional; + ## Encryption method of the connection. + encryption_method: string &log &optional; + ## Track status of logging RDP connections. + done: bool &default=F; + }; + + ## Variable to track if NTLM authentication is used. + global ntlm = F; + + ## Size in bytes of data sent by the server at which the RDP connection is presumed to be successful (NTLM authentication only). + const authentication_data_size = 1000 &redef; + + ## Event that can be handled to access the rdp record as it is sent on + ## to the loggin framework. + global log_rdp: event(rec: Info); +} + +const ports = { 3389/tcp }; +redef likely_server_ports += { ports }; + +event bro_init() &priority=5 + { + Log::create_stream(RDP::LOG, [$columns=Info, $ev=log_rdp]); + Analyzer::register_for_ports(Analyzer::ANALYZER_RDP, ports); + } + +redef record connection += { + rdp: Info &optional; + }; + +function rdp_done(c: connection, done: bool) + { + if ( done ) + { + c$rdp$done = T; + + # Not currently implemented +# if ( ntlm && use_conn_size_analyzer ) +# { +# if ( c$resp$size > authentication_data_size ) +# c$rdp$authentication_result = "Success (H)"; +# else c$rdp$authentication_result = "Undetermined"; +# } + + if ( c$rdp?$authentication_result && ( ! c$rdp?$encryption_method || ! c$rdp?$encryption_level ) ) + Reporter::error(fmt("Error parsing RDP security data in connection %s",c$uid)); + + Log::write(RDP::LOG, c$rdp); + skip_further_processing(c$id); + set_record_packets(c$id, F); + } + } + +event rdp_tracker(c: connection) + { + if ( c$rdp$done ) + return; + + local id = c$id; + + if ( ! connection_exists(id) ) + { + rdp_done(c,T); + return; + } + + lookup_connection(id); + + if ( connection_exists(id) ) + { + # If the RDP connection has been alive for more than 5secs, log it + # This duration should be sufficient to collect the data that needs to be logged + local diff = network_time() - c$rdp$ts; + if ( diff > 5secs ) + { + rdp_done(c,T); + return; + } + } + + # schedule the event to run again if necessary + schedule +5secs { rdp_tracker(c) }; + } + +function set_session(c: connection) + { + if ( ! c?$rdp ) + { + c$rdp = [$ts=network_time(),$id=c$id,$uid=c$uid]; + add c$service["rdp"]; + } + } + +event connection_state_remove(c: connection) &priority=-5 + { + # Log the RDP connection if the connection is removed but the session has not been marked as done + if ( c?$rdp && ! c$rdp$done ) + rdp_done(c,T); + } + +event rdp_native_client_request(c: connection, cookie: string) &priority=5 + { + if ( "Cookie" in clean(cookie) ) + { + set_session(c); + local cookie_val = sub(cookie,/Cookie.*\=/,""); + c$rdp$cookie = sub(cookie_val,/\x0d\x0a.*$/,""); + + schedule +5secs { rdp_tracker(c) }; + } + } + +event rdp_native_client_info(c: connection, keyboard_layout: count, build: count, hostname: string, product_id: string) &priority=5 + { + set_session(c); + c$rdp$keyboard_layout = languages[keyboard_layout]; + c$rdp$client_build = builds[build]; + c$rdp$client_hostname = gsub(cat(hostname),/\\0/,""); + c$rdp$client_product_id = gsub(cat(product_id),/\\0/,""); + + schedule +5secs { rdp_tracker(c) }; + } + +event rdp_native_authentication(c: connection, result: count) &priority=5 + { + set_session(c); + c$rdp$authentication_result = results[result]; + + schedule +5secs { rdp_tracker(c) }; + } + +event rdp_native_server_security(c: connection, encryption_method: count, encryption_level: count, random: string, certificate: string) &priority=5 + { + set_session(c); + c$rdp$encryption_method = encryption_methods[encryption_method]; + c$rdp$encryption_level = encryption_levels[encryption_level]; + + schedule +5secs { rdp_tracker(c) }; + } + +event rdp_ntlm_client_request(c: connection, server: string) &priority=5 + { + set_session(c); + ntlm = T; + + if ( ! c$rdp?$server_name ) + c$rdp$server_name = vector(); + c$rdp$server_name[|c$rdp$server_name|] = server; + + schedule +5secs { rdp_tracker(c) }; + } + +event rdp_ntlm_server_response(c: connection, server: string) &priority=5 + { + set_session(c); + ntlm = T; + + if ( ! c$rdp?$server_name ) + c$rdp$server_name = vector(); + c$rdp$server_name[|c$rdp$server_name|] = server; + + schedule +5secs { rdp_tracker(c) }; + } + +event rdp_debug(c: connection, remainder: string) + { + Reporter::error(fmt("Debug RDP data generated in connection %s: %s",c$uid,remainder)); + } diff --git a/src/analyzer/protocol/CMakeLists.txt b/src/analyzer/protocol/CMakeLists.txt index d0fa1ded66..783f1e7469 100644 --- a/src/analyzer/protocol/CMakeLists.txt +++ b/src/analyzer/protocol/CMakeLists.txt @@ -21,7 +21,6 @@ add_subdirectory(irc) add_subdirectory(login) add_subdirectory(mime) add_subdirectory(modbus) -add_subdirectory(mysql) add_subdirectory(ncp) add_subdirectory(netbios) add_subdirectory(netflow) @@ -29,6 +28,7 @@ add_subdirectory(ntp) add_subdirectory(pia) add_subdirectory(pop3) add_subdirectory(radius) +add_subdirectory(rdp) add_subdirectory(rpc) add_subdirectory(snmp) add_subdirectory(smb) diff --git a/src/analyzer/protocol/rdp/CMakeLists.txt b/src/analyzer/protocol/rdp/CMakeLists.txt new file mode 100644 index 0000000000..445e853f2c --- /dev/null +++ b/src/analyzer/protocol/rdp/CMakeLists.txt @@ -0,0 +1,9 @@ +include(BroPlugin) + +include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) + +bro_plugin_begin(Bro RDP) + bro_plugin_cc(RDP.cc Plugin.cc) + bro_plugin_bif(events.bif) + bro_plugin_pac(rdp.pac rdp-analyzer.pac rdp-protocol.pac) +bro_plugin_end() diff --git a/src/analyzer/protocol/rdp/Plugin.cc b/src/analyzer/protocol/rdp/Plugin.cc new file mode 100644 index 0000000000..770bdfc730 --- /dev/null +++ b/src/analyzer/protocol/rdp/Plugin.cc @@ -0,0 +1,22 @@ +#include "plugin/Plugin.h" + +#include "RDP.h" + +namespace plugin { +namespace Bro_RDP { + +class Plugin : public plugin::Plugin { +public: + plugin::Configuration Configure() + { + AddComponent(new ::analyzer::Component("RDP", ::analyzer::rdp::RDP_Analyzer::InstantiateAnalyzer)); + + plugin::Configuration config; + config.name = "Bro::RDP"; + config.description = "RDP analyzer"; + return config; + } +} plugin; + +} +} diff --git a/src/analyzer/protocol/rdp/RDP.cc b/src/analyzer/protocol/rdp/RDP.cc new file mode 100644 index 0000000000..70cad773fe --- /dev/null +++ b/src/analyzer/protocol/rdp/RDP.cc @@ -0,0 +1,70 @@ +#include "RDP.h" + +#include "analyzer/protocol/tcp/TCP_Reassembler.h" + +#include "Reporter.h" + +#include "events.bif.h" + +using namespace analyzer::rdp; + +RDP_Analyzer::RDP_Analyzer(Connection* c) + +: tcp::TCP_ApplicationAnalyzer("RDP", c) + { + interp = new binpac::RDP::RDP_Conn(this); + + had_gap = false; + + } + +RDP_Analyzer::~RDP_Analyzer() + { + delete interp; + } + +void RDP_Analyzer::Done() + { + + tcp::TCP_ApplicationAnalyzer::Done(); + + interp->FlowEOF(true); + interp->FlowEOF(false); + + } + +void RDP_Analyzer::EndpointEOF(bool is_orig) + { + tcp::TCP_ApplicationAnalyzer::EndpointEOF(is_orig); + interp->FlowEOF(is_orig); + } + +void RDP_Analyzer::DeliverStream(int len, const u_char* data, bool orig) + { + tcp::TCP_ApplicationAnalyzer::DeliverStream(len, data, orig); + + assert(TCP()); + if ( TCP()->IsPartial() ) + return; + + if ( had_gap ) + // If only one side had a content gap, we could still try to + // deliver data to the other side if the script layer can handle this. + return; + + try + { + interp->NewData(orig, data, data + len); + } + catch ( const binpac::Exception& e ) + { + ProtocolViolation(fmt("Binpac exception: %s", e.c_msg())); + } + } + +void RDP_Analyzer::Undelivered(uint64 seq, int len, bool orig) + { + tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig); + had_gap = true; + interp->NewGap(orig, len); + } diff --git a/src/analyzer/protocol/rdp/RDP.h b/src/analyzer/protocol/rdp/RDP.h new file mode 100644 index 0000000000..cb5197cffe --- /dev/null +++ b/src/analyzer/protocol/rdp/RDP.h @@ -0,0 +1,48 @@ +#ifndef ANALYZER_PROTOCOL_RDP_RDP_H +#define ANALYZER_PROTOCOL_RDP_RDP_H + +#include "events.bif.h" + + +#include "analyzer/protocol/tcp/TCP.h" + +#include "rdp_pac.h" + +namespace analyzer { namespace rdp { + +class RDP_Analyzer : public tcp::TCP_ApplicationAnalyzer { + +public: + RDP_Analyzer(Connection* conn); + virtual ~RDP_Analyzer(); + + // Overriden from Analyzer. + virtual void Done(); + + virtual void DeliverStream(int len, const u_char* data, bool orig); + virtual void Undelivered(uint64 seq, int len, bool orig); + + // Overriden from tcp::TCP_ApplicationAnalyzer. + virtual void EndpointEOF(bool is_orig); + + + static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn) + { return new RDP_Analyzer(conn); } + +// static bool Available() +// { + // TODO: After you define your events, || them together here. + // See events.bif for more information + //return ( rdp_event ); +// } + +protected: + binpac::RDP::RDP_Conn* interp; + + bool had_gap; + +}; + +} } // namespace analyzer::* + +#endif diff --git a/src/analyzer/protocol/rdp/events.bif b/src/analyzer/protocol/rdp/events.bif new file mode 100644 index 0000000000..dad76f801b --- /dev/null +++ b/src/analyzer/protocol/rdp/events.bif @@ -0,0 +1,60 @@ +## Generated for client-to-server RDP requests when NTLM authentication is used. +## +## c: The connection record for the underlying transport-layer session/flow. +## +## server: The RDP server name requested by the client. +event rdp_ntlm_client_request%(c: connection, server: string%); + +## Generated for server-to-client RDP responses when NTLM authentication is used. +## +## c: The connection record for the underlying transport-layer session/flow. +## +## server: The RDP server name responsed by the server. +event rdp_ntlm_server_response%(c: connection, server: string%); + +## Generated for X.224 client requests when native RDP encryption is used. +## +## c: The connection record for the underlying transport-layer session/flow. +## +## cookie: The cookie included in the request. +event rdp_native_client_request%(c: connection, cookie: string%); + +## Generated for MCS client requests when native RDP encryption is used. +## +## c: The connection record for the underlying transport-layer session/flow. +## +## keyboard_layout: The 16-bit integer representing the keyboard layout/language of the client machine. +## +## build: The 16-bit integer representing the version of the RDP client. +## +## hostname: The hostname of the client machine (optional). +## +## product_id: The product ID of the client machine (optional). +event rdp_native_client_info%(c: connection, keyboard_layout: count, build: count, hostname: string, product_id: string%); + +## Generated for MCS server responses when native RDP encryption is used. +## +## c: The connection record for the underlying transport-layer session/flow. +## +## result: The 8-bit integer representing the GCC Conference Create Response result. +event rdp_native_authentication%(c: connection, result: count%); + +## Generated for MCS server responses when native RDP encryption is used. +## +## c: The connection record for the underlying transport-layer session/flow. +## +## encryption_method: The 32-bit integer representing the encryption method used in the connection. +## +## encryption_level: The 32-bit integer representing the encryption level used in the connection. +## +## random: The random value used to derive session keys (optional). +## +## certificate: The certificate containing the server's public key information. +event rdp_native_server_security%(c: connection, encryption_method: count, encryption_level: count, random: string, certificate: string%); + +## Generated for unknown elements in RDP connections. Used for debugging and development purposes only. +## +## c: The connection record for the underlying transport-layer session/flow. +## +## remainder: The data to be debugged. +event rdp_debug%(c: connection, remainder: string%); diff --git a/src/analyzer/protocol/rdp/rdp-analyzer.pac b/src/analyzer/protocol/rdp/rdp-analyzer.pac new file mode 100644 index 0000000000..f95ff9f589 --- /dev/null +++ b/src/analyzer/protocol/rdp/rdp-analyzer.pac @@ -0,0 +1,101 @@ +refine flow RDP_Flow += { + function proc_rdp_debug(debug: Debug): bool + %{ + BifEvent::generate_rdp_debug(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${debug.remainder})); + + return true; + %} + + + function proc_rdp_ntlm_server_response(ntlm_server: NTLMServerResponse): bool + %{ + BifEvent::generate_rdp_ntlm_server_response(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${ntlm_server.server_name})); + + return true; + %} + + function proc_rdp_ntlm_client_request(ntlm_client: NTLMClientRequest): bool + %{ + BifEvent::generate_rdp_ntlm_client_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${ntlm_client.server_name})); + + return true; + %} + + function proc_rdp_native_client_request(client_request: ClientRequest): bool + %{ + BifEvent::generate_rdp_native_client_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${client_request.cookie})); + + return true; + %} + + + function proc_rdp_native_authentication(gcc_response: GCC_Server_CreateResponse): bool + %{ + BifEvent::generate_rdp_native_authentication(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + ${gcc_response.result}); + + return true; + %} + + + function proc_rdp_native_client_info(ccore: ClientCore): bool + %{ + BifEvent::generate_rdp_native_client_info(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + ${ccore.keyboard_layout}, + ${ccore.client_build}, + bytestring_to_val(${ccore.client_name}), + bytestring_to_val(${ccore.dig_product_id})); + + return true; + %} + + function proc_rdp_native_server_security(ssd: ServerSecurityData): bool + %{ + BifEvent::generate_rdp_native_server_security(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + ${ssd.encryption_method}, + ${ssd.encryption_level}, + bytestring_to_val(${ssd.server_random}), + bytestring_to_val(${ssd.server_certificate})); + + return true; + %} +}; + +refine typeattr Debug += &let { + proc: bool = $context.flow.proc_rdp_debug(this); +}; + +refine typeattr NTLMServerResponse += &let { + proc: bool = $context.flow.proc_rdp_ntlm_server_response(this); +}; + +refine typeattr NTLMClientRequest += &let { + proc: bool = $context.flow.proc_rdp_ntlm_client_request(this); +}; + +refine typeattr ClientRequest += &let { + proc: bool = $context.flow.proc_rdp_native_client_request(this); +}; + +refine typeattr ClientCore += &let { + proc: bool = $context.flow.proc_rdp_native_client_info(this); +}; + +refine typeattr GCC_Server_CreateResponse += &let { + proc: bool = $context.flow.proc_rdp_native_authentication(this); +}; + +refine typeattr ServerSecurityData += &let { + proc: bool = $context.flow.proc_rdp_native_server_security(this); +}; diff --git a/src/analyzer/protocol/rdp/rdp-protocol.pac b/src/analyzer/protocol/rdp/rdp-protocol.pac new file mode 100644 index 0000000000..ea68040314 --- /dev/null +++ b/src/analyzer/protocol/rdp/rdp-protocol.pac @@ -0,0 +1,313 @@ +type RDP_PDU(is_orig: bool) = record { + type: uint16; + switch: case type of { + 0x1603 -> ntlm_authentication: NTLMAuthentication; # NTLM authentication appears to be flagged by this 16-bit integer + default -> native_encryption: NativeEncryption; # assume native encryption, this should be the value of the TPKT version + }; +} &byteorder=bigendian; + +###################################################################### +# Native Encryption +###################################################################### + +type NativeEncryption = record { + pad: padding[2]; # remaining TPKT values + cotp: COTP; +}; + +type COTP = record { + length: uint8; + pdu: uint8; + switch: case pdu of { + 0xe0 -> cRequest: ClientRequest; + 0xf0 -> hdr: Header; + default -> data: bytestring &restofdata &transient; + }; +} &byteorder=littleendian; + +type Header = record { + tpdu_number: uint8; + application_defined_type: uint8; # this begins a BER encoded multiple octet variant, but can be safely skipped + application_type: uint8; # this is value for the BER encoded octet variant above + switch: case application_type of { + 0x65 -> cHeader: ClientHeader; # 0x65 is a client + 0x66 -> sHeader: ServerHeader; # 0x66 is a server + default -> data: bytestring &restofdata &transient; + }; +} &byteorder=littleendian; + +###################################################################### +# Client X.224 +###################################################################### + +type ClientRequest = record { + destination_reference: uint16; + source_reference: uint16; + flow_control: uint8; + cookie: bytestring &restofdata; # cookie value is a variable length field, so everything is captured +}; + +###################################################################### +# Client MCS +###################################################################### + +type ClientHeader = record { + type_length: padding[3]; # BER encoded long variant, can be safely skipped for now + calling_domain_selector: ASN1OctetString; + called_domain_selector: ASN1OctetString; + upward_flag: ASN1Boolean; + target_parameters: ASN1SequenceMeta; + targ_parameters_pad: padding[target_parameters.encoding.length]; + minimum_parameters: ASN1SequenceMeta; + min_parameters_pad: padding[minimum_parameters.encoding.length]; + maximum_parameters: ASN1SequenceMeta; + max_parameters_pad: padding[maximum_parameters.encoding.length]; + user_data_length: uint32; # BER encoded OctetString and long variant, can be safely skipped for now + gcc_connection_data: GCC_Client_ConnectionData; + gcc_client_create_request: GCC_Client_CreateRequest; + core_header: DataHdr; + core_data: ClientCore; + remainder: bytestring &restofdata &transient; # everything after core_data can be discarded +}; + +type GCC_Client_ConnectionData = record { + key_object_length: uint16; + key_object: uint8[key_object_length]; + connect_data_connect_pdu: uint16; +} &byteorder=bigendian; + +type GCC_Client_CreateRequest = record { + extension_bit: uint8; + privileges: uint8; + numeric_length: uint8; + numeric: uint8; + termination_method: uint8; + number_user_data_sets: uint8; + user_data_value_present: uint8; + h221_nonstandard_length: uint8; + h221_nonstandard_key: RE/Duca/; # &check would be better here, but it is not implemented + user_data_value_length: uint16; +}; + +type ClientCore = record { + version_major: uint16; + version_minor: uint16; + desktop_width: uint16; + desktop_height: uint16; + color_depth: uint16; + sas_sequence: uint16; + keyboard_layout: uint32; + client_build: uint32; + client_name: bytestring &length=32; + keyboard_type: uint32; + keyboard_sub: uint32; + keyboard_function_key: uint32; + ime_file_name: bytestring &length=64; + post_beta_color_depth: uint16; + product_id: uint16; + serial_number: uint32; + high_color_depth: uint16; + supported_color_depth: uint16; + early_capability_flags: uint16; + dig_product_id: bytestring &length=64; +}; + +###################################################################### +# Server MCS +###################################################################### + +type ServerHeader = record { + type_length: padding[3]; # BER encoded long variant, can be safely skipped for now + connect_response_result: ASN1Enumerated; + connect_response_called_id: ASN1Integer; + connect_response_domain_parameters: ASN1SequenceMeta; + domain_parameters_pad: padding[connect_response_domain_parameters.encoding.length]; # skip this data + user_data_length: uint32; # BER encoded OctetString and long variant, can be safely skipped for now + gcc_connection_data: GCC_Server_ConnectionData; + gcc_create_response: GCC_Server_CreateResponse; + core_header: DataHdr; + core_data: padding[core_header.length - 4]; # skip this data + network_header: DataHdr; + net_data: padding[network_header.length - 4]; # skip this data + security_header: DataHdr; + security_data: ServerSecurityData; # there is some issue / bug where the length reported by the security header overruns the end of the packet +}; + +type GCC_Server_ConnectionData = record { + key_object_length: uint16; + key_object: uint8[key_object_length]; + connect_data_connect_pdu: uint8; +} &byteorder=bigendian; + +type GCC_Server_CreateResponse = record { + extension_bit: uint8; + node_id: uint8[2]; + tag_length: uint8; + tag: uint8; + result: uint8; + number_user_data_sets: uint8; + user_data_value_present: uint8; + h221_nonstandard_length: uint8; + h221_nonstandard_key: RE/McDn/; # &check would be better here, but it is not implemented + user_data_value_length: uint16; +}; + +type DataHdr = record { + type: uint16; + length: uint16; +} &byteorder=littleendian; + +type ServerCoreData = record { + version_major: uint16; + version_minor: uint16; + client_requested_protocols: uint32; +}; + +type ServerNetworkData = record { + mcs_channel_id: uint16; + channel_count: uint16; +}; + +type ServerSecurityData = record { + encryption_method: uint32; + encryption_level: uint32; + server_random_length: uint32 &byteorder=littleendian; + server_cert_length: uint32 &byteorder=littleendian; + server_random: bytestring &length=server_random_length; + server_certificate: bytestring &length=server_cert_length-8; # arbitrarily cutting off 8 chars so the certificate doesn't overrun the end of the packet +}; + +###################################################################### +# NTLM Authentication +###################################################################### + +type NTLMAuthentication = record { + type: uint16; + switch: case type of { # there may be further type bytes that need to be added to this switch + 0x0100 -> client_request: NTLMClientRequest; + 0x0300 -> client_request2: NTLMClientRequest; + 0x0103 -> server_response: NTLMServerResponse; + 0x0104 -> server_response2: NTLMServerResponse; + default -> data: bytestring &restofdata &transient; + }; +}; + +###################################################################### +# NTLM Client +###################################################################### + +type NTLMClientRequest = record { + payload_length: uint8; # total payload length + pad1: padding[3]; # arbitrary 3 bytes + remaining_length1: uint8; # remaining length of the payload + pad2: padding[36]; # arbitrary 36 bytes + unknown_length: uint8; # an unknown length value + unknown_value1: padding[unknown_length]; # arbitrary padding for the length value above + pad3: padding[3]; # arbitrary 3 bytes + remainder_length2: uint8; # remaining length of the payload + unknown: uint8; # this unknown field affects the length between here and the beginning of the requested server name + switch: case unknown of { + 0x00 -> case1: uint8[7]; # jump 7 bytes + 0xff -> case2: uint8[12]; # jump 12 bytes + default -> case3: Debug; # debug if an unknown value is seen + }; + server_length: uint8; + server_name: bytestring &length=server_length; + data: bytestring &restofdata &transient; +}; + +###################################################################### +# NTLM Server +###################################################################### + +type NTLMServerResponse = record { + unknown_value1: uint8; # 1 variable byte + unknown_value2: uint8[3]; # 3 bytes that may be static + unknown_value3: uint8; # 1 variable byte + unknown_value4: uint8[2]; # 2 bytes that may be static + unknown_length1: uint8; # an unknown length value + pad1: padding[unknown_length1]; # arbitrary padding for the length value above + unknown_value5: uint8[3]; # 3 bytes that may be static + unknown_value6: uint8; # 1 variable byte + unknown_value7: uint8[3]; # 3 bytes that may be static + unknown_value8: uint8; # 1 variable byte + unknown_value9: uint8[7]; # 7 bytes that may be static + unknown_value10: uint8[16]; # 16 bytes that may be static + unknown_value11: uint8[16]; # 16 bytes that may be static + unknown_value12: uint8; # 1 variable byte + unknown_value13: uint8; # 1 byte that may be static + unknown_value14: uint8; # 1 variable byte + unknown_value15: uint8; # 1 byte that may be static + unknown_value16: uint8; # 1 variable byte + unknown_value17: uint8[6]; # 6 bytes that may be static + server_length: uint8; # length of server name + server_name: bytestring &length=server_length; # server name + data: bytestring &restofdata &transient; +} &byteorder=bigendian; + +###################################################################### +# Debugging +###################################################################### + +type Debug = record { + remainder: bytestring &restofdata; +}; + +###################################################################### +# ASN.1 Encodings +###################################################################### + +type ASN1Encoding = record { + meta: ASN1EncodingMeta; + content: bytestring &length = meta.length; +}; + +type ASN1EncodingMeta = record { + tag: uint8; + len: uint8; + more_len: bytestring &length = long_len ? len & 0x7f : 0; +} &let { + long_len: bool = len & 0x80; + length: uint64 = long_len ? binary_to_int64(more_len) : len & 0x7f; +}; + +type ASN1SequenceMeta = record { + encoding: ASN1EncodingMeta; +}; + +type ASN1Integer = record { + encoding: ASN1Encoding; +}; + +type ASN1OctetString = record { + encoding: ASN1Encoding; +}; + +type ASN1ObjectIdentifier = record { + encoding: ASN1Encoding; +}; + +type ASN1Boolean = record { + encoding: ASN1Encoding; +}; + +type ASN1Enumerated = record { + encoding: ASN1Encoding; +}; + +###################################################################### +# ASN.1 Conversion Functions +###################################################################### + +function binary_to_int64(bs: bytestring): int64 + %{ + int64 rval = 0; + + for ( int i = 0; i < bs.length(); ++i ) + { + uint64 byte = bs[i]; + rval |= byte << (8 * (bs.length() - (i + 1))); + } + + return rval; + %} diff --git a/src/analyzer/protocol/rdp/rdp.pac b/src/analyzer/protocol/rdp/rdp.pac new file mode 100644 index 0000000000..1d0f7f6197 --- /dev/null +++ b/src/analyzer/protocol/rdp/rdp.pac @@ -0,0 +1,26 @@ +%include binpac.pac +%include bro.pac + +%extern{ + #include "events.bif.h" +%} + +analyzer RDP withcontext { + connection: RDP_Conn; + flow: RDP_Flow; +}; + +# Our connection consists of two flows, one in each direction. +connection RDP_Conn(bro_analyzer: BroAnalyzer) { + upflow = RDP_Flow(true); + downflow = RDP_Flow(false); +}; + +%include rdp-protocol.pac + +flow RDP_Flow(is_orig: bool) { + #flowunit = RDP_PDU(is_orig) withcontext(connection, this); + datagram = RDP_PDU(is_orig) withcontext(connection, this); +}; + +%include rdp-analyzer.pac From 2fcddc6441fc202543e6e932fc7e09b92ad855d0 Mon Sep 17 00:00:00 2001 From: jshlbrd Date: Sat, 14 Feb 2015 13:31:23 -0800 Subject: [PATCH 135/299] Update init-default.bro Commented out mysql --- scripts/base/init-default.bro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/base/init-default.bro b/scripts/base/init-default.bro index 2af8f3bc3d..b4f0769737 100644 --- a/scripts/base/init-default.bro +++ b/scripts/base/init-default.bro @@ -46,7 +46,7 @@ @load base/protocols/http @load base/protocols/irc @load base/protocols/modbus -@load base/protocols/mysql +#@load base/protocols/mysql @load base/protocols/pop3 @load base/protocols/radius @load base/protocols/rdp From d0e2d64cfcec9c8de81e453ac63a184e59989041 Mon Sep 17 00:00:00 2001 From: Josh Liburdi Date: Sat, 14 Feb 2015 13:59:59 -0800 Subject: [PATCH 136/299] Add btest for Wireshark sample pcap (native RDP encryption) http://wiki.wireshark.org/RDP --- testing/btest/Traces/rdp/RDP-004.pcap | Bin 0 -> 142910 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 testing/btest/Traces/rdp/RDP-004.pcap diff --git a/testing/btest/Traces/rdp/RDP-004.pcap b/testing/btest/Traces/rdp/RDP-004.pcap new file mode 100644 index 0000000000000000000000000000000000000000..a26dd5637f82d2d4ad3d74b79c84814644c6ea53 GIT binary patch literal 142910 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 From 7773cd6011191c999b0e17c6267bfc5f63fd9f9e Mon Sep 17 00:00:00 2001 From: Josh Liburdi Date: Sun, 15 Feb 2015 23:11:52 -0800 Subject: [PATCH 150/299] Wireshark test trace for native encryption -- generates a binpac error --- testing/btest/Traces/rdp/RDP-004.pcap | Bin 0 -> 142910 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 testing/btest/Traces/rdp/RDP-004.pcap diff --git a/testing/btest/Traces/rdp/RDP-004.pcap b/testing/btest/Traces/rdp/RDP-004.pcap new file mode 100644 index 0000000000000000000000000000000000000000..a26dd5637f82d2d4ad3d74b79c84814644c6ea53 GIT binary patch literal 142910 zcmbTe1ymf{wguX_yF+ky4esu4A-E^F1cJM}2X_hX?!gHVT!Xt?fS|wOyxjMadw$0F zzX!W1s;lRkYwcNUR##Os^`$x+6aWtR^>}&$06>6$bn6pK*u6yr2m_a&2LO)|^41Ul zk;3~C01XfW0N4h21ObAOq;jPg-pH`)*lZAl2z(03P!Z1?dxJhbH4p&+py1$#AYfpi zARyqNUx0|Jum3@NL43jb2lpSuJ6!<41M-6U8SNPrfe(U^Tkj+oh#KjQDhQA@ct(V% z1tJ1|A?5%P*?|$C$A5988VG*j2>%DiNFZv6BJ$Je2oC`8%nOzi05AapJdgiE(HIDM zMj=`HR}}20Q*J{501NKj(_-PXC1->mT&efb`md zmHjq?{SV6F-%$)+P+0yI1swqRx&;9A0!!rq3jG%)PUt*mghTtEjEE5Uk|?mNpX>Up zL|v=jmH3PZxBh<+|ICi<&iF-%r2mH(5$@S32N1R9S&67Gi2tj^XGC=%;{Ur6C(@od zV&DA1u?C3B`K-h^iGMOx-Gu&&5+~AMPzL@L1*k-T&HpDQJ{RbjI#dtL-;`+bGD&!U z6oV63jP++FTHpVsL{Oj-e*s7I!2s5QdVd~&?pGjF6ARDTU?KjI%{s6qGC<%1hoEO* zkW$c7Z!Z&HkSuWEP7eZnJTc*bf{=iMfP#VmfUV_!o`Dk$T=o1CaLs>yUN!&)CwhMA z73lLep!ZlWzyEk6xG#tR00MyS4gm3C2d01w0O+F}6!;s9lU0zd*l4^RaDmIsKwU@?GYym7F2uSsL{Mpn%T<%PtrBt=_hm>TJC~zYcu^-8-lU`3POY zeL=*4{Sr94Ot>%84YZM$+5G1j4DfUVoK)bn5&qA#(V#oQf`B|vo1BrTwGT81Jp9YF zJ#z+wM+O{#fddY~j=+w=PQXCn4*}2Z64+W=Y_>XCQ%>*FXSq%J6%=EZRO01v=I8D}fTT zfs=1W7LO}6Ba&CYJs~@}F)Xu0jFfFFu~g!P(_!1Shrq?VyP(6A4P3E0|8%T+hI@WM z10(_B%^)}lt~-T?kl?EtSxRal=rD8mKdpbz)w`}d38yDCOWpOoMTdW<5a2gaJRb`? z&8GtX>09xcZn|N8zd0NH_VgJ%VC$V1Iirw!!B5ow^*;yKJM@)j2d#MyW za9Y5D1%5%aDS`#?1JR$ypSw1IX%hPd(eV$&lwXLjoDpO2ni#(z=dxO?|+ zt@f-Cm}oh0{{SZg`VLUCNI;6fx&tBq0ZmSZ{~OJeXPQ{Yf6y!k(yRy4jE1=Ujpo5K z&1r9hztMdELKF9oO4b8uHY;L3+%V|=PV)fl1vJYS;cqnOUuahRL9-c1vlmD+9AfD= znupIcZH*ECM$_VjCe9x;dx13jfHV*36n>|92=)TXW%{?n-YLu%n)ZLt>;uxA0@4hD z`0^Xgqdp)_GoTRuGU=8tG_n7nIR&IS0i?M~BltVbBd`}xCZNp<1C{hV{%Z$hn^JnA z$?*rx2_Vf4Ak6@XlHX_^Khq>w{wqzZ7n)dq(A)shTn5sdqGJA?<}ug{=;g{^X@)-2 z#C-gN<}#4xIgq9gMBHyQPo8NSw1WJtn$|BgG5?@>4y3scq&YxA@;l8Fuouu?K2ShB zu#nH=zp5EHwemt!;}4qqxM$}eK$;#7F)yIH!2Qw~xLY3BDuMt_K@tcRW}c?jFo3K1 zLC;Vh@GhbJV{?N#sq~0RmBF#S!1Oh56fy^1bfzPhzZs*wM%1Vv0q}i*9UX zKL-1bT|=uEugRoCDKGmOTm8IYRi`{P1Rg5gXUWn(^atJDz*j%awZJ%7!Ec^1oAT&0 z+7^wCX>ZG?)IVtwi4bg#R9E?8XbP*1SeevWIz)C1-d&yS8+*+@Da{O*4Pv_|UuBT$ zjLk<~iNH&{5B^Ny@5#EQON1bD%yY6ub5mPvJ7n2mK9nLAw_a6e%J}|$u=0~)=>7Vt zPhqH6TLq1g`3-rlp zj6bG`W93$QxC%~Ays`A8);|R(3%yH5W!hG(_<%NcDTvaEx5gPdKD*(S>E;H&h0vQ1 zZ0lvY;|?)X0IR>IRxszG1ns5k?n;5BTkeU2HCA*E@Rq~;c1U(5U-zO~Zm_>OoXv}B z$$nSuIY<)4s}4ovyNej$f&DW|0zVi)A2@5zJ-xG1&wi#0C58ah13ix>a{Gy>)JJpPOaQhaCk z9IqTU^3_Ca~c@fNZs$wwfdLRKE9!(`%>cE_1@-m zfgRG&_MI;)rO3|wCJQ%9-(z?}-z%Z7uJ9M6#Q8{WL9blY?W4P=aMLq4$VXO|;55Ku zxm*r?go1ty1~QO)|Cna~xZ#8+28P#PRAA1jvq!D+Q?bIY`pqI`2)gtsBB+3J(UOjG z1^K>Lyo9}4mPV+_#j1ErpiYc60-OSNf&^W}+yWvSc8GYChX%grLnTc?mkguFIAN}i zm49@GLAds1!=T$PNiVv0A}fNJ=2%E}ZWRzs>z>qV>sZuIu3lQ2MI`y3pF7Uoq8n6=fw4xL8G z$7+d2Zu|rZQx(`d0h=EP7jt>nAEIn9=v!UJZcb$xF|||*6W<91h9VIR(O8Jcjq$_( z6yHs%12D;W>YxzqDJj{}4MM}2>u%AuMjcxi7=zqgK8 z9k^!~Wxx-osNs44sIO}W44?>XY|rD*1_UZRP56w0{`Ox{u%A$quAbEZiu<1i&tvAkY00e#h~7jqQ^Jzh=Bo@R7YYCX)HA!35sj(ROsZ6g>+4_57p+_IC`_Mv%**X&ei^ zgHG#6m7>ok5Za*O>YoCcVlIT$;bYlLkw&b+`eVs_XlV^N+jlQervuy zr6>rz0IyF17YDbTzUHNE~bwG;_e29W+tO72cGyD^Y zx!gl!$Ogwpqz~T_Qf^mdF#}Vr`&Tw5UrTtYYCXY{a?XXR(F5tOxi z_oalNfoz6@57*&;?-WwQ~>36oGa$Q%ZR92c)ft&mAOSP8ZZ7_8@e9k;pPEpDJ&&?P<4>YFI zc||49D&^P%1DFCO@jU+27eI5G=tZTXzw3)&{zaugeenT5X9Ew8nf{|M5-K1}Ie~B% z9u)@cBHvKetVlDAnP(bI(#8SGnImqKz~pn(IbhWHz%8H0e?`@vCVq)3@OxB3m|sx= zUN2GE{u7k~8VHkEymsFpap_87vwkp!C5-IP7vT?54qeZ!d?&r_Fyt&|_#9bo|F4lr zULy1V5t;8-!{b*0=Wc{eCG7NGtR>eJ8#{co;T+rD#GGgal<&Mp4H2b7Tw@ z@V`}>^d+*;ACc{TMYh8Kv(nEw5Ae?F=T<$ru?Cna==&@HdPDfq2U|-Y)sztk$fKU4 z_ELfaNFV?&5PFUZ5%Hf&2Y5mQhv7E)nV@tCkVgQYGzu1Y69WNrFPKvVdnx_O4p4E@()wqZQ(N8_FQKLdHdPhYDj+ znm_E&XA`0x^WAED96oFM{Ra%*lnGTxIp9LhC0>sWY-X=J=mUP>Dc&03ljj zu3l)>qLMt@VXeSq0=i!0C;t2;Qe!j4Eb8*Nr=ONIklrYnQT8zSUoVmm($w6|nt1g2 z%@U8u%1`gf@hnh}tGNhAPU^jQ;9Y0DDU_Yw=N>J*J?6b$r4P4cOW#oS(8xJf$R=Uo zLYC-a5fzL`Xd72GUCmY@a{#HtHM!$%%h^3HY^$Q1AxsOvm-R#HDWZe%$;R32-2*HO z`g@|#6Mt2Mr!Q&D%iNhbg^P2A-~!qCC|Wy2LNHl9st&`{W^KqL5A7@+aC=s43;rsE z1Odu8a9%{&=3~<=Et^6Uf>ewUZlL0%>s#mUVDJ4T7%PIA_;A>hM^wAFmO^b~k5$#*+9o3DJy{&Ab^;=d%DpKs}lL|>pdlya5=PrbZ9)PYrY`{R*5M8qN?pfkRTqYPh8 z7|a+!JO3d}qz{tH_F$1{Qy;I_LPFmqI0}0(JZI3>{;FyfY{P{TiCsKT=2Og2>q$pQ z=X(sO{>o6zDmp-g!5J*CH@)mgO6O(ck-B^+aFi?i>u z53wJ9;NnWWv4!2nUATbifzG5i+4<^@T4xECOiLacA!T+HZ{PkI#paZ;bO+O=yuf&_ z=r}$*l<1YlEdr^!h=@=dx)G0XtiVIN1d$QrH)Ls{mZo4T12IN)l09(Mer#P->{6%n zIeQq()v$s#tTGTYX#~?i)@k(~D4xlt1HynzdkfLdMr5VNLv{^1G8^cjry#M4@XOk8 z*A1_rj#O0F$q&LxUZ~oRC*kju?nqnQ9rNrvhC-IDuPIbOMG#?-T{Qa{-p!R`O|*L@m^^>%;k_&$<3ik`TIHzY#B;|?rUC~5+x*M; zvtt4Vr^%l?CdTOR9g{5LSDzNh0(MO5XxYrrC5r97{T1#$swVP>P_eLF{>7EhYxD4j zVxMAEq6-vGO}K-)&u_;;enLC3!eS1kG+NR_&ARnV8BiBX>hvfZH~RLx-cimBWZF&% z?!ntQd^`S~$`BUEa!&Nfty1)G!;7`+Rei_zEt&Rkxvg)RU*?+gH72;w@HKtkcgenU zS)evLq`*_%qP0)#&}R7zUDA}2WYPSH!NTqG^*K!{>c1KT#fvfc{+=c|!!Kjt!v0rd zFgIQd^!JV+#J3E@qct06sj7CzhYGftRrbff$d;?%)6!-Apx8sg=K$twb+Ap!l^&UJ z1lthtMvQas#$qVxyh*BfiBhFc_^vfy|D!?~vZp=x6P+l|+?OEbQS_6pdXZZjxFcxB z*&|xNJo!zZtCq>dp&eqOgV=et56&vp_|3gf47IKtL;-^xw|I5? ztAgNDhNCR?PG&qE*>!CCJYMv!&Lb2vTr;lZPB=~K=9wCXL4VUu4jWDJPH^-+CGqHv zg@7UfKE|$tI+Uc_vcvDNKXoQ|B)^-9QJeJ3LodKCtlDAQtw6k~)@1jR{rH(~S(Z<& zfrhEph}NS3p}%t3jrTZWX}``x1d%y^A(gg-Q3wZn8oVejpDeao^xiBuV5!}&#Mrqqb@n^8F*=Tzuy%C$l1DUlG47aNW|Fzs;MR}yDIg*kV}AP zaAR}v6-r_757E6smDP|sH7?DV9d1Zhuz-mK^gBZkK^8|t778;QW@`Jng*Bh^@F_P~ zPVhSpL{BP(iHmADaP-D`X6qV0Ww7MyM`R5&M$NA~fcHmF73itC_*y}81P_B?*sE?I z{UWjOv_}}SV2p>F!ccO?cFiV1F7|hk9-?UqOK0FZsWe6qXd+5H((MV_6R$NNdk$dd zQA=A~CyKsr1}II~a>RZ$eHZbmHCZ$z)Ty6Y~pl z=lCgIt-@FCcd0v+`&D8aGU7&nDT=8Tqun6HB6h0~SXDAwi^9*k3O}y=b>V|#%LBx% zK6ni+4oPqF4>VbFl|Qo1zl~@S^o2tq;Im#IW_E^ z*n{F79mDVwL5S&cB^$i(R zPtP559@sJeVhofo#_;v`j!A?2%NVSt007L!y3W&gF=wdN90l(>`Ddsz@r_0H@mJks z@C6o*S|LGhC^Jd;`;RixyJy-%j0U6C+G0{PY=g`wbQ%t<<8p5S~US5V}Lvc0B*<49Z1{;M{_nBIPnX;lfvzmw` zIu1WQ4sCR!aAo2nZ1pc^9NxWNiJBMdY8O|eBvea&PE(KOuf{<2VhrAYq}lSz7$#Z% z)fnnb?pKKJ#QfQr20!tjS`>bGht`akAne19@)X|1VDGKVE_8M}FGbJ%ahNo>8K1}c zX!&XqW$)Vjpn+33AFXSJ;_?PGC2N9nVu5CF3K0tm+iKEQRAGO|5}yx@ed5D*U65)Y zi3wX@N=b4hi5cSNH>?{=7pfP!^jgip->IBii7(2ygM;?gm>R ziimo;@UU1iC7)4?BLLpJzNYh1AENQ_{wQUGinxI(+@nL+i)mfF1{*{I^=gldE})T{ zFnDpVppN%sV(>#-fT7GcZJnq)scedfFEmGh7UR|h@Nay3Qp(c3>DJJz^V%m+6z&!k zq4TI$tuQP(q|wW4+i?a|Zgy%~Jyt{?oQ_jMsLC9TH<7;TthPodpuH^ zN&Cddxd=N1fflM z@UnEFj~Caz((*aWvJJl~$j-|X9tbKqe!XkmM;kUXL$_(sImnX6+B_S-?4tX)?l{O^ zT?Pv({{0qZf+^7wSAhJs$o{R+HnK)&R?}=$m-`H)4evF0F%dhcos4SejXjbf#k}9` zsT=mBW^4~^u=jXWg0n7nhkTCl8b;b1|Avb08B9P$PlMQ!S_TH%wjaCi?dYAeYygIS z_Cfkex1A~Y;_SYX>pO$R{f*D86r#wm)}Gz^oRheIf{uJ3W-WDqs?o^XvA*rEG1t{& z0@NJ_X`9LrTbK+452upYkGTCtDKf>^-MJjv9-rT!&{1KEZcFnP{y4RG> zbK34_^|@$&fvk*;SxYyiweexgOj-VPXry0{f zdP9H@p`P7Jh&q&qmw9DT++CK|5U)KJiog=wMr*H5y9UMeZ>*)H-B&;KCd`F>X(zF8 zR#PbO2y4mAZN+mJl5@ox+Fg=Y*)w!YB`x6=D5UQsXMUqCcN4CoY5v-ZXRvk(;E>OX zH@SMHO>c5fhN8Kw0V1-seB4BEz5hnO#d@?WF3vWHE4 zcWh{{v(giK|1YbAmeHXEm#;iNy=vO0b?(Jq_PL6DN7`V1eSN_npkeAaYE%i1k^+{05|7R^_%HRjj*~u@c2k-+14` zzMppK8;7{e@Qa2pS_Su+x^)p_7>yrnzN2_VY!x!g+Mw_o6c8peOk2N^#b3pbO!lPg zkk)_hm;lxF&6$ZhIh#TfNQQQeNH2^Xk7)m53lCJEVq)ggcQ3^ zT#Nmdr<=sU%{OpflpK=9vKV2iT6fs$J(j3%lf6q&OINv1c^Rk9-`so4z*`h$w#al) zCj;&#O59(kT5Vm*-!dEra`46vX9dmQ8^k>^A|&cbY5C5P1^?JF;OM*HA@Xm!%< ze?}pxiuX>;W^**NE{}5}+E0Vmu z;I*)SPH1a9{i`tyhG1gO$bwsH>cmWtFGmk33Ris(_!c%^K)6E>!6bzqKA}{0(-X@L zxO6;z8#B*@kEh0s_$k3TwX8Q7pvu^^lcIG^rC!*X&$y)_Ph)1=GJL79^b}*>Th=w- z7pN5#(>9DyI1~FIAD0SY+;k)u$V84kCwuS$J9{E*NkH)9$5l@j+fwFiFmoRn^<%k|>ZrZHJdbs=}u05a&^waBGFdVm5 ztncMnS8v3rF2uu3m(1^f#*Mb^I>*O~7!c0xPpjZkQ1fk89&1fKMU16sdyuG=N_m$) zZ6r*M%5x{7P<}Ng^eJ1#R$KCdoN*y|BMu@7dPI1hrdbQ!ZyAa^bzS~#QtdlzhNu(X z?N4|U?OQWccyk$uHh(_l_Y~nY41zd6ypL zuNBbs3HNx zg|K2Rl&R8^z0pJ?=B^Y4NO*)*?$Y{0^$ItZbxW^;DFWqGxCN9)$P_>sMtre!zc+F` zu66p`X|4Ap-BiSB-DZ)6Zep#RG|x=nwndGQr4G9U@y*fgB%sw(RC>oK)AGrmgc-7av{8Ru4OvO(Sz7MU zR?OOtWO>P~RQBPxu|fchlCjR{@h*7;x(&(>0F~mnX(PwHWfDi^xp?r|SrT(&tW+4m zqTe$%ah6WubMY{L$(i?EKCzzhgr7_w>d$phswxcQYsOs$iLK5R#AXk1>ZX9!M*W)h z!@Fse zQ@Hhh6ogR!cn#i}3GwFI6t$ISBI4DmyJk$dwTPt9W-7dH@*WtzS-ST8o-}jCWBZ(% z_xJ1jZEVMy!nv>kNC|D1DpM)hf5A=`qpjxX| z5W!98Qc%Z{=p{Z*wW#RmFFblqjar3GRH-~XDj2=Fs$H;iM-;#&Gha+MEOAEjX|6TZ zha(u&h2ngte&|FT;QzF+yLlZ2sZ$##z`pYFbcgXuo>^n+^aeM95~ofpsTXgr6jAoN ztl9p!QH9dztNtFL0EK*wcE+M(Rac&1z8>72P+HC5@OV4D;`=xGU-Tp?CK(Ggz{klP-??`P)P36>X(;XF#Hy-IAbucSlPjeZTYumam`UB$G+*Z~d7A=CF`g~W zg;5`MO}+nZIaxns)F{nZ#~UyR|8J4;`){W6AH;HjiSCdv+h@Ndd|?>P9f9k6K? zK#8@Rki5Cm0dIBIBzVBQJiosQCa|y)V^fZ{jT>-YX)N$?9L=yG26v3hG4s#cCJ9$q zP!7$FVZlh+k6?t)C4CIv6xqzjG+#5E0koni@5r$2PYm+ZQjWn{?hdG~`+1maVTwY& zW#LBK-DJs}q-E;opO@j7AlVx`f|k>& zk{le*D)MDom);6I1aBjmL#rsYjNBH!GtSCRqUBja0%&@@>(a3R;ABBj2=GlYpAQ&G zCfFNJpT2g#Pk@04>we4c8$9b_s^PN8J{D3UW4?rrr$9Ue;?G>L=Me)z7hV2RAeA0UZ4}Rz6j`q*M6n z`pJ&ckvV5JN;kP;W+bsSm;E?!yG;T2IVHDk&J^2-YMRgthZsLC-JC`pSL#FBzYjH; zWFS<-F#endNyW`_?&pC8k8bTPpYrGzLx`&#m6SJa$!JgdBJTVNZoP4Htx?n3T(XKZ zFnFS^X81|943zZA_6(qbx7sgQ&Sp*XjxKdbpN{K$B z*OuG;{9y3M`Zv3nBQbbVsqLnrFf1z~FLKo0C5|2l9ni9&K&^g?FrT%2vQ5HkRu~2v z2H&|?t@DPPdQ9|R3MT4WS%!9S8M6JoU&}LiPH?tbDSD;+l+0iae5j2Hs5S@Qo*_9i zL2+^SRHoBq^E!{UDdtP$LV8}huM#xapH4_Aw0sU{Lr4*;@Qd)-Os$rYOSmN%A|#bk7=Y} zC%QX9uMS2NrK0VH$)LgLWW>)+B6X)ffM7VA*|(UHSXl=YmQtUMQ^|uWvg+r3SC@Oh zXIqM^JB@{kiMD}Wo-R@MiT}d#Hk%%9y;m`e$J){7+BqVB&)Bt9QaEO|&bV0UN-Cg( zx41Pa>}x{{YdkHivW8Lm-uXo~Sexvp?rW~4U>cg0MC-3>ZK5CE&NvtKD=NF-rbqXM z$u;FNC+?aE5IdjRd?kfG$MSw~c94AwxO_s0GfqyqMN_kIhI9!x3(vN0oNqUw#4z8H zPiTv8BL3V!%(e|FE{QoP)76P~6PM^}yTW(QB^~4?MS;RMg3vr`dlqRZPa^on~rnD^i3uor`6W*#!9Q+Gz4d zfxMs*uTs?M!|O)$Fc|X9E$^S_)1L%s?4)I1^Z046_-*y2M#u5=^BM>do*LXNbH9N~ zI%j5dNZA!RmZ5inGV)<|G(is*9@(yWvLx{x-*Zu$IxiuNJ;RVA$5SVCAb<3dfkIZH zuK$LW&;5{*+pt#JeqJox#%<6CLJZ6M4MGa?^8uuo(oAGFRf3Vac_%`R)8Ko6Hm)M5 zYNWtufE{ZqecOZg{)UklLW#ajPV*g1V*IOPY~n{^SK-(mJ-eehJcJ}CNs_*r(VY9# zW(|Z-3*Nb_qnr=n-b^^=S`qmEOpc?v7`<>l+aYB3b7GIBS3=)ZMVOvWD`%3q_TpBv zw>C7qlN7R6awG6- z{v~yHKt5YLG%-U3xE^*;-){jD7vQUgc1rbKsn)J?vzh;*y=#?5k)ne zx2LbZEsOak{PJy9Q~y`5_eAP|S`tvKt_+`OoCOY_)=5>kF6PoD2>r;6RLd=-p`YqJ z142EQu8PJ^cu1W5Fbu6*vgr-MID8n!gNpkhr4Y&69G`f5R>WF;wsGL$uhEI99_{8 zL!Xul$-TLrRw#)MNPt-hNCH^Y1?RIThEPeZ=_uUoak>ecjK27B4xggLe!Eip`pIKz zv(i|~%U<{_yom{n3i4GTObTj>6s;)Xa^{b`*zq^ zVmJ;3eViqKh8kEN(IRh7#cMjgV||rr7W5I9zCsndHg3x?M5}F^CSiM#p&6DPE1{AH zQfSUz7)gahh;dltR3W7T9@(Pd-Pg0*NTMHeE}x~|WBKR8^N0GPJwfof8WnYg@c3cn zd8o|vzSX@uDzgr*%D#UMt>5GytP`AvbqeOq z{J467DIqItCz&~r!EI*A$&Py$0>cmd1E;IGB!xTH);Mw=uX3^md1pDUkoggV zs2KlxybD!KkvQd17qz)7i3wRq7(K|<*2T61nkN|I2Rws|#@TUgTKk1Hp0JUASe^b( zRT#e_DB2XV3)6P9;!5YY=tVMrOv3%xhGLad{b`tUuP7=V-!}qnm24{HF&2!IC1%=1QB83%yl}6Zk+HQBVp8WKXwg3VyE@Qiw z%0rh^gKBQU7?eoLsGB&>dmxIP{ccdVO^ z3_e3!HQ`~%$=|7#gSv6!Gm82waqM{1&qmTQvJ{ZwVU*Tnchb=y6t0U;Xl@pIJBD3N zyE4s3DmQZQW?9?Ltln&>M|%Jt4XsCcV>Q)%OSDi-sc&kATB8$+c@|XQ8RrA-W!j^n zPsO!n>$Ga*ATvB8%<&bKqna$j^;a0*wM;>ibQ7RHQe*c`O)apAdRyK@WkBj};84r< zeBAi~+FkS%S6fb!d9Yn15rYhw2oaoACR=^?uGit3a0`@)12m z*BoSMYw7qnd&mewdWM6AQz8FK#?;TT6{+BWE3jzQd$iyUUWCY@9 z@7r!Eox81>f2yBxGHJ+9(a4WR4+Ol?DoSSfvTZ^$!>hN?4&nLM>)PZg9SD@xug%<+)*Ee49xgt+HFp!&6h)M9OdzMCqOcxfuQ2Y08ZhD6u^WqW>+}eI z@QF@L(o6N02JQDT=&4Smml153k$k_f5j$8lt{0jbgc)Ks8^MEm*;kNm7(imDJz{o+ zPZxU$hkKiR=}LynIP9n?M8Y_Az0i><8;2@i{px*q_1F&vtn7gyG=e6p@yHv8n_%P` z@DG_|>|7}`PByiTl}ZV)beFrO*tk_Z$%NdiKDtgp?2hlP`_M;@hl%F0$^x?OQZ<`aCf2n~_7E{ML=P6kCnoJAxg?I@C}Aa&~bt{YOv1sJ?c5w*CF#oGsVN`WK`lI z&q`d9V|jGh{0ZSjUltn!(=C6Q_aHQ(GK0*3Aggw^j85+%(kg@5As-qWHx0V)dp&r~ z$aYp=zDWVj`j%`=^J>cGYxkhi%4UU~w5!N;aW;nB`^wTt(2hlcfp-}UTljJQJsaWPypm+c$n5VcdAdrE=>L=IC=0tc6;^yQzhCyMjZ|kEU|!k7Sj1 zCKEH-0jj}eaBEZY!y(SGJiN~C3ts&CyogV>VNh9#nB4^s^H{D$Bu0>ZRau8!k0Wbw zs{1v)p>st_`Me%k3DKi11H0RO(*=4=8-Y-0Sxlp`XM-U71cGCxO%$%m)SdW0spkPH zMcKr94{}0}QtqUy9d`&-5rQ02tMd6#5nBr!ecUjwH|n$ssBtrFpp1q4zmB3ulh(e~O=1x3(A;?u6Ia3%MbVOwo@ou_+kk5kd! z1)6zl+jks8@v8rPB85ko{p)O6_YTOZVrh)d{ko4o{J;@3!Uy+x$~2VOa{X_{m~1J0 zA+Ofnm|tt%l@wBhlE5}s+p)FZ&K*(0PraYz{EHf|q;IMuR))1VMDRID z9=_7&OxBibADAe+A=G?q)n3O$M0p$EW2|w3Wca3At`B)WGdnMylNwx8Oeo*@N$*;F zAqqoLrRbh$=Sj=(yg?YGBsT~?mjy1}HYOj862Eaefuy6x_sN1OZ2Sj(tt%sD3x*r| z$UsMhLvkm{*=<`e#pfu<;$kB%1P7JiWvKoyj;oBXjrR6sJ=DhUZr?$SVY77Eg71C0 z*Vui>Cm=#v9g@+1n|EIb2@-ttVa=`n+N#V9u0-WTp#1TYXeVGk@GD*T&v~&#O?4%+h@TI!X1>~bsTL~2D?$xt}v!C902z=`euWX5> zW8M-7!Ip!GK?pqI_x^5ECEGvT!OwuPK_%c$kz4PHP5z!_T~8k@A46a2LzTT*Ti5s% zS5pL{RhwocQfcbulvr7iFu8kGVN+>?^2K-U(Y1rKK1g1*eA|-W34?x&53yy=(m+%i zl|tDG`W%J##qEuy2Bv4qiPd?hug);z@)k+^s0SOiIe$ZtxISV>wiqEUb2l?kcDaVZ zfm1n6E#imhwp7W#>>4zmb7T<^Cn;XH*zAh$NtF!|OsqLWwVZ)XWw+BOz1;2=Px1f4 zIPAZP`5roMAd`R`CLpP9DJH&MAszfXmdJ5@hX$kj!n6oRP^|4w8`9w%vh~+tDP*7A zPOD; z{dV%spyCMU84n)5$A-00r;RGE-(l>G(k$aVt=rHui1D+vo|f%?5uYwW5QsXH4^xZlL6w|=>jpK`L6zw_^xBckE@@3T;GevY* z^#bbF*kx$LA`X(DO|g3FLBj@XouwTZbO2vqG@3UjX?u$}Pi_xL9(Hp$di(-kMXq-i5D6`Oa#b4rT6Vn3vAwyAdrGg(q-bg0$I& zZRDz+)BM2vS7Ts%F@`_B`!4&>H%5TpV_+!!S7V?a*6|*v?#vh`P>L8VIR64^ncnz4 z&h?#tL%v&^s^}ecpOT`(3W{;d2)xEp=Z2XK@sFQ=Z}V%UN~LDIc%iaX#6yb9dR|pu z>FKXM-Ye=ZbS*C+bc9Bh8aIN|pEhtyNrZ%?uV&$t7T*6L29q*|hL*s3{-CTjd`$O0aOU&FZetnRcoSWAuF>?;XheqC%)ArajqR+}&qZxEuh2q#(# zPEpp+Wj5z`eFu-`3t|Dj1&F5XD{pC2`~qbcR_mm(9cU|6H9Xf*F(DZfm!P~qOCODbHhpsL$XKdotL^7FRQQliwxhs~ zrSCS1mr+l!smOz9dRv}(MQADe%U21?K10*fjflDtW!Z?-2`R_7A`bz_ud|s_}Swl`x)LO zF6regAmu9uP^Nj5uPMawm_|p7YRuuI_4(qkN2#AgR?v~zXMWPz9dwxyg)FWoR((Y0 zJ-dP{?fRt3MJrcNoc;#80Fw24Q`eQ(w7E?695;v0Tap0Tie)}`I^yd_!G+I9+%R~V z;VE_c>F;>4b@^x=uhlvw(K)p?@l+y281M}%z*N1ihHG+Lm$Rwf)hCsha4OT2TZxTN zHjj!-s?aH>HMJ9av2KlmoAO5%S!*o&Tg3^5!%#0a2Xrk4AtpW;9VXX%%MLWScQmDk zlZ=tiS!^+kq)N!Im3H#b@e5UaV8^co-g04gm2V}wi|fx}@@ed3u|}IZ$pVKbx_$Nu zsU(D7;|_#H0Ax|cW;8t{Je`NRwur*rS9k?%l3vG_WMzgR14!8`zS_>xDQ=oV)QM2)4}h#V`8xU)fisC7(@H-#-RA)mocdC z007Ea_Kvtt9%{v7^GU&7(UbRD-Yfjf4yLZKI?TXMp5q2v&E~+yDv-!uynH&eXfA=@ zv2nHw6CQ4<{>V3_b>v8)RV^~#i-P8bh=(gdkY3pAj_X3U5>eQp2D@8ARTdF~E@yE6 zN%=IW{lWY8L0v2y0kq)0mPRJu3!xCHTTNik8nhN2fu?Q#UV~$DO-LeB*iV+lzj=`H zOu!UBcL(h`%_Lx&f4TU~{$dP&{Obs%#9yBnlnwsX7-pm;)2E&_erGmG8&>Zp_Jmz$0V}EY)lC3Cp9-1ozl(NP0@7G4DGIT-aS?o{2EIsq5SG50uTbN%v8N$_ba4+2^(d(w7hqd{B>!=Zzb&LZOMo{ z#atUSYBpA|t1irDdN!nX>Xa=HWhlSAiodS$l&0uScZ~Cu;&{dUpx3*>_w_P>`$>XJ z*=aRz$C6iw{5~kZ5`w*X_TDifGOD{)n4D}`u`jrpw5{^4--NEAigdHsBxM}EdHvM& z@z`@w+*u4Y&Jd-IMd^(7>vDg$j^>6Kv!XlPktNf?2{rD}e7(c4*E=YPPwtaR~+*-RzJcH^b6&8jX@)3={$aOWp%Rn5L;Yo zQX1bdUxv@*GB9kDWR5;uzQZkJuXqut!6XHK1{7r)5Dv}9UL_5N^UZJYma&~*?SaRT zT67|88AqmPEIapw+b~A_KYX2KSX@i6u5ot{9w4~81^3_(B)GdvkPw2qy9OsfaCdii zcXxNY13CMivy=VgNB@1RR!z@WH9ftmXr48L@r=7GT(7=)ii{h!Gm=K8k<;3|;X4?H z#n}~b;FvEy*ppDaGrLRs7$j@W0rr7=t&ONrxH|AIz;Gea^F7Q`yRVHdUjrvZH|4`= z_H%?)w@{0I2%~qHiCa_iCz`x@P0!|X!HygYO^5DYl{u||W}__Jk04!LQr#CzlUpIT zKGbDaqm$Ia6dB1Rjul*IRo897Znn9~;c%_V*rqd<(-|>loYY#9L4_xJ$u{h6ih@_Y zZ{+73{hng(Z-b;{`y-qWSc7H+M7Ans=^Pf{YBNlpt3Vxm6ugW_XF^+nJ8UtpD2!dW z#x@Ffp6UnfS^Y*oU;4;X!<+jBzkI@UbyXKy=_wTLe&qusF3`yHhpuQX_)6FtCgRz^ zQ1ct4A#IPS3Kic3YDV_cj3f5ZME+WN;Ul6P-@JhqQ2)4fAgV(vKJq7;V3Y16Kr)cYSif{e?lmTMh!uL$owjWseR@xge?MXB zhhR<#ol&{K0MlmLPpF^QRoQ35I)_#gd7RHiXUF2V*nOA2jK<6L5jN5J?v`mWs1E4B zH+^=pc@K$A2Tltuc+k50hDZTRY9rAwtC?(@lNb>**4_AKH^|fG3oAvV%tb#W7csXp zPkcX$#L#s?u4kO5XXO$}kWHZ+UtMR;TVm|~g+rPWJuQZ%_(8;K2_S}HH_FJX zeux{+GBHqF{X0r=VF4F7vM(W*x~REOVVZ~k?*LU%Y@OGBr?VC`@8s~*^r1-Od{tM3mDZlzJ zTmmS6NiXcL>Bair!BQsvC%t5x{%3lrNPMGZu6k8nzw6Q&WVOLw7-RL&epP!1Wl~oiHdU zjU}|{)5RAN?<4>->~9A4{B6Twe(4;_H4p=F{p>u@Q7Mm`Cd^Me;99%Ro3dnj{yC!-u!I7EvQ-E830$_Su`tlLF8Q2pH#)5Q+Tb0?ZF zED+TXgiX?e+(b@Y2X+Zxd~?N;8dh5WOjnp?gX9kZwO@*{7i5^xf$v}Uc@lxNYsFU^ zOy+ClxBAP$YC;mTVFzBJ*Ox?NhCJN)Z7m#4MScmhzR(Sru!4;ji78! z{1D#<``K;wqjE{^hMJeDx>B%x8}*y4B7=WL>euBk>cIg>AzMaOHtgH)scLJb1#g=^ ztyt=|MUSF0is&56_+-bb!gmSBPb!nu3^*yP#3N|&mHr3uU3&OIpXt-OvW zB4)uX9nCuYTdA_pWicj}y|}NT@NT4F2)r;?25A%c?YXy;Ud2gYopS}()LP&TwpA0b z7O#NU`~xIA@FVQ0Vr-=_b*6`RQf|&zvA_bEt!3Wv!W%;iuc=Tb%$a#WWO(TzOyzIl zZtkskG)Iuy)RJ+4#pFGA?I3V&-_JJ8N_>fD>k=05yWwpE{9w*aTu8hZ4+WYLW@f<~ zH;mBUS30^HL#jL|pMk0$=nwhm=_*+$Ty(1H@GvMkv()Akd4>c})o(h2EY1TK@8>2B z^S$qTlml!@ORO)8D8ybAKE9-LB;BRRh}n8lMibKb7^K-8^$RaG`#5 z0fk6APB;Xw`CtSly3jGXXTms5!0bnb9Z*=azWGywOj8MTFNw$_e7NbpPVTWY;a1Wq zxUKx!YAmF%!x|!Lf>&wf^r?Z$3D^J|9x^A;5Vq^_tLun*FhAiD>zYaDl4jyL9gXvo z#2feiy3Z`<7kdF5O*j)#7pxOL~@cnv*&0-(K zNcao?85w1*#~)gt0`FS<)N_<#Cb{N7hk<=*bKU((5c>MqvvGdawQ zV!n11`PDCp2nq@WR0>m6#={Q`Lv^6$l9_nu+ZB|k&SQ_l5WfMaj;h~_)x;sN|LrL9 zyH)jj`&X;`zgb;;ReOL{{@qurFC9gGStWh3`usf%UP_mp8%x{9ngoOseZ=!4lm&im zv}VZZ!7yI%u2lYuVJ{TQ zmH$Ke-GB0Xhu3U{|8M^16y;(X0kmjBLH|KkhR(=8GM z5Kv!6&e*XF?gQ}A`;ED9$+UY3M9b)ad35V#ga}5Bt^kX=cR$GT4Ht@wG+Yl`@Xf7; z1CY--V-py71|u^I^7v06L2&K^nc{!MG_y0tH6=(BijU5Q>qW|>sF^$u{mmrd?MQ7h?m zrW#bYM|!&ak9H*81J-wD$Tl@z9q4)b-%n;~Mfx;0gZ6h>f&)6A(islYVX$muD;<*% z+}vV1WWl#DOEWgN3daQ=eUSDXt~gzCYYAxna_D`nqm8H+3Bfa5 zLv}Pa;S`q6vde1n0Y4MHoFa0Mct;VwA6=Ce=&3JPi{wRSp-bZxYC$qnNhaUmvB!QH zKvAzG9CTQ#V#s!funRV(#L(~5-C(K?vg@=Hck`l?IbD=F)`r*0} zG`Ddz^Q&kp|HicR7h6gm-C6+xTZg2;b$SS+i?wX%#EP)|u4^HLZ2a4{Eu2c=vwpsb zILBayk0j&w5oIp#C_}mWdadiR!@sV*>x3?xb@?>T5aW0x9qwOdgKXc42qU7u%!Wj8 zH-B6W&g&8WDGXg(n7sb-)=ElWbSG^5zK# zuB*=ww5oc#>b}|D^M;50uz{bd0k3 zawpE4P*$#FT`(-&$oJ8UFxPYo+|(jv-ovSua7NNTEAqHi?z5lIhg z`!KrPCEqqBXzn+wBbuZmi)r|adX!X3EatmU@P~qCA zsJn8P&Dl04OAA`Al1AUL7g9xP3H-naf!o@j$V_Q7SwO^wip`+&9HK&V-hPUD-Sjzl z1@rD8Y_`Y9J!?IfDEfIvOWQovT#glf3ieS=YGTUO8&k2z8O zv6d_U0?PM$-KB7#tQX7JYk$@1*H-_?7XQ|2?R!Ap5C*iRJrDu(z{-Osbktt1n0lTO z;I)0+8fam|<+#Pae`mQPFya!7$Z6x^oX1hVp%^MvSz^-|-^<|5((MU{ zRMY@^ca&plBvl8D1!Xq1m+FkYzrik8bQFua&_^?sdrSy0Qn$IMpUTlVpzlnd{6vNqd|@#rSEsqHGIHBY38tP^V}kyyPw zw@Y?FJ6aJ~xw9k=%^137*yQA;@Ovp)8D92bVfgg-9h;)G_ue{MPiMAW2APp458QI= z#~Q764W-mkot4TKKZEVooMOuqEbD`)9;!!_3BW<|yM%_Csz3INX`K~+Za`1c1-hRj zTio*;^K*f z@Gq~0D1IqG#ASajBU|duX|zJYVLcEzU4Y{^T#Bv6B0}b@AT5#_s~4qBB2dnJ{{s?H z>8_9J(#FS(yD(4DOu(*_gtN(=mabdOfIdsl`pAq8mO;pwd30#Ip<*noGA*f6q*)wQ zU3tiX$=g6NpCYAJxq0Jg0c4;CZ`o%u;;pn^${9zsY`uR^uxT@=aaK`H^`4J*(7>@1VH1E;a4VB^R6vS-sK) z$;Hqt?}p?RzToL77~(oPJU%~?9<#zO7Ud-q)wSzwt0!!Ed%~(8-cWVQi%=dKt&`7$ z(ZYR|B4hGU#MaGae(sZP)bpakHlP>hMnH#KHG{xO?hA5=A{@e6tFgf1y2+mGN#GVM zGlLwLY*(DpJD8hF5$fi??E6e;6hepa5q}&x_U$s0Xk=#45Xx9tl38Ja(T{%TdV#)7 zkaxpSY~cvLcX>$rw)aRgmhUDDR~A-Iw1OwSPVGyt6Mo>j6(EMrc&$&p5B7@_<^PZY z(dw=BhMhl#`w5;8$jIsaNS;U8mbf*>08F0%Y?Fm6`r3OGn+a#}CsTw`%P7zrMZv5C zgXR%D%H_0{qf=h=_s~s#B)jI><3I7;yg~gAdj$=SKdsmQ;8$<3ZH>GQaeM$XnB1HF zVgGKTwc0-xWDn1Q`6%|Htr8jaxI5>B0g>MDl7^c^iNIBn`y$6kMax}gn(Cb=qgGXZ zbpy&7ZDPB&W+Ixv`)aeFQv!0GWAuo3=!v6KFi64nt2Es@2+Jl2f(oL6}4<`bT{Qn3kNPTAyw%;IdLU!%hy=)-Ee^=+^bxqGaxZyiEmFJ3Mxi!0LQ231X(nA+|W!FU!a}_Op4Ql>9B<)Jiv`=m2h< zuVlnSvYsO=ua2h8C<#6t{e){`fsbEKEuRI}{Vi!nSt%Tw&fpW1xNeG}uX7hgYKQ%Hh_}^+|?$;XG{NK83u>Ftj)&tIkWS-`& zw7NdpC^(sU8H%PofSmDP8SM$flVEx3bd@Qc&h*RVCPO5JBk#bQV9AQlDuuo#?I zaFVb4iS8uWiLf&HCH(L+VQ3K)WO5X&`d|=32-%~K@kp}=NYU2D`!blmKE{R)Qo~0Z z!|CjDh)wdr&}2bFSu|jTI%G7(U{V8{x>tkGk9O?>IDM$WHsOM%(WExUoU^w3k z9@52uHt_}o<~t|H*!-7Gjow6p(`cKD)u!CD5jIJS@iQc*cE_gM(KwIv-iEw*slu3U z4tE;n6q-1XI*!c}_y0%?c%$r$gQ#0k=g${?d2scU#MqwV<_gwhvWYZ7Y~~DFwN9}5 z%Mx&gq?%P$r;=ULu%q$HLoVA0??*RfN#XiigYWx|O1@edB)~)tDwPXRx|8{m1m>7o zHiodNMQ8>Jd=+)pUD(&(0q+yyWhT?rye!kssR8z!50j_UdY`%LgJfcoi7$L`l1iCO zb>z^@*|je;5)x5rFs^kh;iNG``aZW2Ur-R#eA}i&159EpD1AAE)GlE6D+X0g&d;** zXBznMK$9F_84dMxcJ)cMf#RMx8AUq0-qji_<<7EYj0=i1b+DEI4`g8J;}lIrwaQaM zr46isr4@6YXW{z%x~S@R_(TbJ{9M=!WtQx%zDw3i{_Pg5K6v-MNz9j&Za9_817F5k;O{Rwn zGPF=oJ4`*IR#6OL@K+?5u4H0=NTo{&*;0IO;M?=NTTg6m#mzj(3Zju+h5oqR5rp@A zo&|?ONt*d{O+wCvkyf{T&LE>-w%jsf3dS1K15pEb;%rbh1K}?gWyd6B03!qLA0X|K zG`h{1ri7FqlK2ZS@>oD5&4nq(Mb36913fjz113&BL@dhm=J7ND%qwwNxOl=SafM_@GALBR7l9!sVh3N*kC_yGE>m*k9QDz!o!%; z>0J_k9NdV4TqV$8RFMnI>m3_x!=2>Io+&gpB|?>;)1EXaMP!D+^gPSB$u+$kBj#0Tc}iG&4N|4e(dy-ZicX+1+ERw2IWo~019uHN0tZ7a8A2K#W~Sm9+H^IR zPp^)bY6is{n_`&HeUtTPVzDP3H104LTJR3AH31*J*lFi3NCb_KRQA+a%vyn6fJ)oF zp1#C{AgMslFG6dFguzt4IFl~|W{g-;pdX*W`6{<{qb8Y9sOz&7>Yl4ub%4T{CrC0& z=GHO@gDbrP-HGYs9A`O)MJ;$RWHlZ!-1zWy{SB0R+qu(mbr{Mh5J%fQqO%y31mje# zZ!UE5t<^_chp2%YS>HDf@hxU1bqX!_5PH12m9WX4v(U_5Rt@GLxgt}h$5?x<`f0Ek zXcWGHi$JuY1sfskHdeb|c01dn3*Xk-m#eSewOV`JYUdRV4Sk1-w75cU>!3iBq69H2alfd zNh5|Rzffp<@|ZH*!OcSOb@b4TUYr;)?8}x5)nubCQZ~!(qC_jfiE%=!KUasOeOuHe zpUUca5i9;tQ^00OSSqsR{Au%3{jRuJ2hJM%DRMmm{RWBdkngdnCNb<5?thYve`9;sx@`#Z zFt+xp7Xpy&3ZAF(T#p{ZbEZeQx9!s~Nerrk2Zca}XOUb2v3gdne1>UL|3Yr&WE37D zpWYoF=7Lnvxc+B!Gq)c?G^7EEIOUp1T%4S#pPrlGhW8{dN~=>2^=RAAeOAyv0h28k zHypz^ON3^FT=E_0f#eS=HHzDELCmik&3|>~6mR;5z;YFwGemTV6D_417aRwyybsw7 zMk0JigN}b{@g2EcozL*dGKau=%*H3?nzqm%_UVdOPmw4N9h2-GX;?rnLIZ$(B zcEROzl9SbknH#Ml*Ic%br=MXXjh!jF3jwbWEEfv*YxP+-Dh=pm!{TYviRA3@qMQtP zVpVRY1+Wc{D>RkprZZ`3&ni)hpS)g5!QlUErTY1`QpNr4RN;Rr)w>S?fPT#{x{GY` zSBmp1<+q72FZC`Zwk*zv{G z3lLD;yPuoy^YZpR-sMr~;OGS)WT|egf^exE$P?v91X0lGG%iCzVqt;aL81MSmb`ls z?fb%t6Wh~ZsLFp+(np;Vb>+uS8g^qSvk;@iR;`swmEGsqp`0Foi{Wc*9v~NiEZKc3 zuMl^_kL|=;-7_^&2q8I_t#&RkLAXqyYFEOgaKdeyqgPd0kJf&)8TyeuqS{b@ETXX# z;V2?HC0O-2ElRC}Y}kMQOV#dj-SE+rzZ=qrqS<|j$1Y~NT<5iF^~9%E&536bK_Y%A zt)Gt%%o-?|3*{6e4)V3~t|2cFfy#dPfLsc}qPkKkwO(eEOq-I*8PxDN`spQ9i=|J^#*Biv>Qc&W@Vyn+5Qro9^K>z|EkY2y zcb14**SBdbN_lJ`P6+b^B7DFlUtb3PBuD1&yiVR+=a0>$*S2YhgJ?t*p22tG@;!a! zIay^7mbcyyWLNftqM|lrXt^%)@+7Ol(y*JXLv;uz-Xs|sPI z8ouY-$7e#OX}HLDs?S{;1edgp`1l$}87{PQK_8~us6gINeOB79qfgO@yOD?{l{Cs2 z9n60R`LO4zu;P4-`UsQMBLNxDoH5w)c7cBh{!4AQh(WW!X3CpcFb7$#F8R5hq)qS` zi5NRalEjVZ=UzTQ2}cTgnnYuGHIks zP>!Bah5w1nTbH5`O>}~Gl~UA9Bj{4O;;F_2Gx+=^QVZ~>)#U$glm4yirA<1r70h?M zNoIV<>4xsro5#K7tjlLZew-krASK3f1XkzOHp~x*2rR+c%%LkdQl{y|A%(_9{@wDt zeqzm68kwgbfHFpezBrB{FmhPEa^OS}gte)@a(xub})v8?V$eHa|18p3}$dq20r@ z1at5LFR}SunZ~<>xPkCJodt_Mn&j-~9CZ;jSTGMs5-Pw${ogM8fM?p131w`KK;z^k zO8#=ezQ_5l6QiJaZt+1?rFc4gb^h!cD+TSpDYyCm_@amp@wej{&sPfapAO@K=h*D+T4hDYyCmJW;jm zUnyR%l-C)$FNqH@^%gK|^yM=Wo`3m_{_zD&{;w48R|?{vzPwPL*>wAVJ(0=hzfyc& zDF}a3J{Z6F@((4|<*yXqR|@=pQ=Zv0cz*ekUi?>z-zx>~Pm1j2i!c9B@=E?n@qeYj z{x{{BO}X@!FNTYMr3AcEVE&{idA|7a52bJEuav-73iN+dp4sF{e)$3m0r|JR8}v$n z`jeuL_~Oey6bs0|Qi5M8kpE42W|NHj<;(TEzfwY8DG+~BwC`VbooK-?iX$f^kR{+o z!pr)->;uxC~8)k`=AkC|JY*x{~3+)C80qy=Mqt6*+tB;Hrw8_@<|IG zdT$=jDMQn$eM3f4xjk)**b9uHcv1bgT#&D`*u?(JI_D(ct95~Y7oGO>|5$GWywKEx zvO^|iKry(6FG9o}mlex*{=%IT{%dSPUt^IBKaijqM0hSBBUoF%A&&8XA6T+8^H)0vnK>D}y z(x5eWs<{w2mL^h`<(JhxofPlhzP|GYT>O7x_UC9V{@2~5@Z0VZ0PHS6HSjdSZ15{) zqkyqN=YR_5W&Q3s=;nR&i|54xzj+Rr3;N6Ri*mqNUav-SKzCxorCgOr>5xDi*ZV&0 zaj>mbBJdUT?9g;X=S+g?30(cBy=&Q9RZaLe0ezaifwA5XG2fcni$eJKlnWvaK`Pfh?)6!~@0P8#~W-cX;oiZ1Ide4*3)}ChO&ZZN?7Unw`P2HCY zQ9DR!k(unjpI31ohdTJcmCNJUhB$Zag3{Toy!piI$HMw_3DZVmh2)5%kXPg&=RHnY zuQg{Ex}@E_wj0j_o`+Ydk0A^hx>Q73X@s@VOQgk`OP37E-|I&UEuv;-WqwRo?^4C* zI!zD<&(u#VU|ux8CvvSHF8n;ZTf(B@FJpc#AsHZAX)-E2w!7a^DXy4TcWHnftxn$Zen`|{2G%n z*>DHf!jz_%O8VdzuZ#8L;@AVhZ9U}Afriv3JmCth#uKtFzV%q2}ZAQlmT8?KC9t=Sa* zAU9>P{uyUOU*cRU>3Vc~LF52w-V&4IkpAGr%Qb=0OF&Z7jE;k+5)YUOwlgNbdBG+7 zdmt%Sy)<^$jnV2BjOy$jb6z=DByEl=ZKQA1SGv-WFXwBt?xwN;CCC0z-! ze4=(P8XG-DwwYq@GnPQoGyB8scMb8DETPT!Udr*svUx*~yk>#J9h#Y@6zLg0?!@Io@*G40%Q9}W*^+;nDM7%y2UYJxLKbzWFRAYp7)KjoqZn7?@+2?X{2q~Kfd_5 z$5kGv^&-B?lu$siV1U_Sfc3isfcf9YyhuP%>c1u6M*klP=+g#BfVVb(U6K_ux++mP z-&qe46qhB6c$qvbo0R-dp7nd>!5J1HD-CLNrKJIhg8A~wM;vu3&ojh|kA6(7MpYK?~k%k|{)RTFb`wIT9T9IuM|c`u7p@g2z!VD{HM)*&3Qwd@JN>UYcswcl79%Zj=6s}O(OC8(#+1vVz`Spz&IqID*8T;r6T zOPmJHP7;W^hJXkk-;6`rPf4m7C8DWgQU!fQx(%tLB;)JPMr znmxpjIq^97P|FRlQ#Ww%zH0ZyFeoVa_F>r&ZqGStZhno@6BGex%djxU=krS*9$3AJ z^>Y5Zc}S_ZsWxCZ@M_(`Q2AD&NQ+2oXd+peEulm{@Cp!3%LL83R&(@M_`dZI*0g!T z)FsTA4*Q|+u-oL&C{k1&Q|VitL$ddmvd^9c^YPv0S4B_Y1mT*Fhz=X4Q?(*ie4GwY zHcrs!^hXG?-{_%XJ#8;QT4KpXv5}m)jfCjD$>H*@mhXJbtgH9veyp|{wZf0sh^@)jZp5lHw-f}j zpze~Hi{%T;we;r${<}g-F5rT8=f5 z_aISbmB%Mm$Omv+Ukt+JWCYr%MRKr*= z8}OTWCK!D< zLWQl4*)J<9ts%L@$BYh_k(*Y-w9#b81-)(4w5>ObN$Dbl(xS%A1SI4+6jcD2N-w8g zyx&{d6ry8eIddY&5{AF6(K%YnE4pN_n=`V;JXF;n0d(kJK#nxK&pS`&Xn7s zBSEH1oirB8&zszQE^{`g^)EuNH>!&OD)pBe#saV9j{KkH&O-;FL_DFQ{#EXPDE%MG zpSLGsUkkOze{0=CM;xiW0DzK41vm$OQOuQ8C?Hi}px5=gVgi4-k9(n%&i*F_>p9Iq z0HBy2#5~c_@ZoQ5pN3s2cu&k}B?Y z?aFfXVfB;;>*V|K6oY!Iv$TXqL9$uR=egniJ_;F^_2ZvyKbOAxxAuxtp1Lbi1z5i7ReH#Luy z6T9ykezL#l0*Zm71&>f4v9UDs#Xt9rAdEZ7*PU1vX-$3!^rs9cAVNT(U)CRiM!AoF z{kVQRO?ljY{kQ;uHj4rT`rF=2(e}xlSgtTS1gCLGc9h<-OH&i}OJg)_l2ouE9OF)Kg&}w>XJQ!AHtn zf$GdNt5vC7i6m+`t-_%PQ51mA1YKa`pO!tnh?k+2p^^Vc!n{_mnB)2NTfDyoLT{jy zR1JMx@v)5OJtm09ARZXnAe@*vsvk9DGIjKQ<3L_^;W!+PG(ws3PZ7y?<}-r_q0+-oXfnIuhB> zY9=G-vVPo3&SAy57vDNyp@EbEz?bz0Rs25jl{)hqb@d@@_Ee}>>01P}skim$&y;7|+mzdz`H z|8b=>`Qep<^Pd!~XKmu0|Nn#TWi0!FHQ)yw5YW4kq=D+&z0>A9#4}9xDZneKDuW~9 z*NCPl`!Fw_Gt|KV0R-lC{c%`fIVt&S6XZ9W*4U>2&jB{kXJ2iK{Lf*-GD53Jv(zwQCHf9-)Y(4&9$K!G_B(3IL}2}<-d#W-lWIA9v%XV+q}un$B>(kwow zTI8%K3Oq;JTBOWd!1~>CJC0tiq9TCC{kGVZ{y`C-FTZLXFvI8jsr$AngF$5 zb6m>PvSab7+p1|~dyuWpL2BiW_pPuWhE}^TVLl%7R5Wr^oZIM(@H;XxH3-3oThZjt zW`D#g(NbVlSR~5mp_jf%A4}NaIa?3+)*(18Ji!-5El!1*!5)Ric=}Oxo$0yiHSi|) zR^6K*=OqA_fB*pG|8@NlSLR9S*SK>27T4~VxPAld&I&_72Qy%B7;HdLu+PBqUWi6Z zF#mg#@3)Kwj&`5+64IjIj@zC=UPB7VXcbm~ua!@(%U5&*agaE^t>ZASa^`QJroI`y zzp{SAxi}6qQfmo<7c!E8?p6)-d4EB?n^saxys#nwBkp3 zeDTl=(*)t7J38jLbxON@ldXz<2go(bx=Y zm(TcFG-&PBwC0oU4OIu&^vIhat3!<8%3ar(#ZiGFrA9oofjkBjrnFMcJVp5~b&2oj zIE`t;y1cUcRwhTQ_|v1TXHQ%3II#9LBct@#X{d;TN9K|$ZSV~g3QJ%PoqTjP_LmX{ zh}^`n#HDGo7Cjwo>NR^L$Qaa8R8LC+*%ww(HQu1o*q_Titbcq2c6N*2l5d)-mMj06 zC6f+>uf`>3kQF6Yv0LLUJ^TiajylL6?ngWy`(>+^)IAWI;K*YXk>jEti}Tdwhbu4< zSaCi!5f9Z{OB3w{w!$J#S!?HSKaC(j_0Hz(4`9(3Kf9r;)OUTGe3%tlHaxXk_&!~u z-frO6yuu6T!)R%~;cuFn^g~TaMUQBl3H)o$=L%4W&$FfTv*K7|zwENxGIbnZln?ra+k9rh}qC^NEdz?lQ9R=Fr z`EKK?!{exlBL1{$H}=DYR86aMhl_pF?nJ*eJ}E!wa&#l)=ORJiG=sA#3z<=6#^(9_ep0+mc#?uy(s4< z)EU5ph%$NhNo$|qWf@E^qm0d~@qt>zPuM^yKv(i0l%p~ z9F`#BRz0r)2VC6Xiq+!pW*E^aGAMvhX@0N`UUBK$wC5_V@^?#Rh2)gDTQ-BH;yYuO zOq7)uLXS4F(xmud^c96sTmVc6up$Cx2Y zx!e|33Jg=D#{+lGJ$Y+}D6oI6Mw06srPE~EQtP_7V!akdW;X1#HaDiyi7V^$qL_p$ zFn_BMWL`B`_zw*R=JCg{Jx0e^nFf4! z%3-QcMFnG3kux)fy)1Q`yppr>1frnDklf*-G!HaOBBmI8kOjL@htIk@4o&@8c^u*x zR@+T9ssIaH3D0|2_EIOQ715r^0%Yfk$#3!)=8q|=(J&GuQB9p1 z?=zwrM>3K$?co*#A-G)hWfC1 zVWz{;@({E&M9AJM$tXVmxgm@voglmsfjehYUxOWfbnH*l3Q5g6nIgymIF%-9qZ1SW7eW7?n{V?sI?uiW!=MYZr8zr8p zMl3C3u^k;6!>GwZ;OXvl-b&tx;QzLu93STJ8gA{Q4|UTVpZSSPFEj}JkF#kJ@06;z z(#;Im1k#f!iSAE(XDOje0w_p(R;_u;4aLs%`1C)D6Xm>8tjHDKLT~Ae7q{-Nl|)H( zBIpZSg@R9?dJld-mkK_Fv7|J~oauzdNDr_l9 z?P~ZlG&kvBrVMpcDSph`Y;p765c2e!n2a4oIV;5a!_OOzhuF;O;GAIIcK0a@PqKQ> z1w3rP& zoBdIyUH0m1`IZmUrT!}4t(`|O zdmA^`Ytuw0Hcbw)pnwCEKCSgICG?(^Vs=dOXBkwUd zVHM58*0i4L30!z{$_evzZd_Y`}dhIidjta@8Tai=AYtUtQv3!uj&g<{;(Gq zudw)L>JO;r3X#mUDxiX$YAzUVjFmqAzW7UxBPHJka-B_v9E5jSv{yhKwK3bJ?zZnW zZ(4g54p`zl<3hr+vzlr+o*CKd)XieYdyp?a)5SGbF2(&Ia1ek9svljh-my+O%sadp zahTmU&i}#XZCKu|5D(eLqSf2Q5)Nu&sHbTbmXSn8hq`e&X%twd(7HM-hw!eEZo@DcqeklVM51lUTttfY z@5}Kos%A8ySE}uqHnJrKQGu<8gfpk&N7FQiC?o@7CS(Ydsts^FEssMtpe7Ttx|b>8 zfHW~zO!F(Id||o^MCiZ7F(&A|LC~V?Otq#yM5+_o*7kkhuiHm*K}H@Zd|6jm=25r% zkhrkwD5kZ$wlqIt=Xb53$m)JsD`dWxt5PmvOrXx2+nS;zRB?x zB%5%~X%;vhF2d_VYQ~Xv!uk^r-z|?U{?YIvLFB3Vq-JAje0;1#h z&gL6st-+SaM-hykQ@ltl8)@|HKs?D#6L0kfth@SsUB;cjW12YXdHK5lXniLRM z!wN$p_o)v(sd@ctl9#Egw?y>x-KfiuZEbm(2Iik&FY1zG$n_HJ^OZ2pTxH_hp^(fF zbL0H_I-=B|XlUZs76M=p5xs>ebND9u5>5hc;~Pq<2|4u5P)7VK%5pKN*GLnn6-D=U zw_uq4m`*1yy+?20Wx*i>}`CU8d()1vnA*4XVXS@?6zGy2<+hcALh-(swAU9{vdyW7%!9GYW$lIbqB1_@^1dQkS>rzx zmtdRrNjx)o+!noghnFoN?jwv9MD4I-yKwEnR7s=Aw@qOY)%qhZ4Z~7?r5R}m(kaAo zC6T7)3BqN1ZjWlKXSrJ{#NqL+VCO>D$rfvroBz(2Q+!zdA+xKt=+L#5BrPuaPx)oN zc`H6=tqj=AD!dapS+eao6DhdTP-HJcumli-znoIZz7}9Ye->cyfbj*-C)lXJ3b5j% zzZPIwuLan@Zyub%tNc@d0ZvnZS^`A_7mt-g6)$T|c`S7O_qw5LW^hG1Mzc!yss$w3kS+re#fodP9)D7pDM9|&vlgSL%06DEIEb&{^Ild8+iurZWnDI}UQ)e^J zH-sh$%Q&6!24|W}T^;^;F|JEY@AQ>2fGAYIuj+aB(U=HkDSC+?U=TG~_p;g-YYL6Z zJY6sY83-R=c)6dvJJ7|-q%yV(IxhK?Ce}4P4?p6f{tar!dWt(g355E3yy})ITt?Vi zh-BgPJy&_s7c~Fa=o1Y}`7@>lwaie&vW8=BzuMhML5IeYb ze_!C`rIac0rBq5+^6%r9>nX!Gctg2lW*`q z=j^-X1E9j%3x=K5|Hs!i$A`HD?Z#2!UW9ue4_k8!B zbG~2yn16R?_MLs6*?nhbDf6@&JQ;1%y?Zziz>*Ds_RVjK(TAmzS~c3WA;qhW(Z^xo z=R7vS9Ubt*;F0Q#Qdc5pJ(>{HP8DxsISsh$CoG2pi{MWbvXm1GF|R~%MJ9&>NTEeq z8L3nXw{AD7su00WPj*d=E}DRSLnVWbnD?z#D?({!e_bBrUc-=Dz#>36EQ**SLO9T_ z*x_f$2ZQa`4IA9yZ?@V@?Qf_ohVHrNM>n%3hGR(-(Iy2E2i|Rd+szO1lOfBr`U_}`mw}L60AxY3{8gtDfd3zfKFFhl*#wG;PA6CYs$hl5^3~!(eNw52Sgr%cxH-Dbk4% zcD~ALBJ?1H;O!M?&I0Yc-oR+t3DD=rr2Rg}l^u*V-6$WRd=2JUy69qG^=O(uVXi4k zQFSZeCSg^2TwrV;2qZ7|=`t6tg`o7{npveta}oa6K0M<~u}BpIwnEQAhvdngQhR&G zYOuE;n51@AMYFa$YO%p7;sdWx;IyVLgPAC-!OVhF2@=B1{N`RjN5Q$0f|s5b>n!c! zD0d^zT66-eX#v;cKwlXdsgU&eQh=+bd>gt%O59p;ec*_JW4o+qvZ)yS40}g!sk*xl zd^B|GRsR5q0_DS0R=0fWrT5~YTRK@Sn>h=xSmeefLlaGRj@BoQ23S!e(tMD}z7Txw zoUrtjGS%AvS7N)(m^6>(0*kL`NIS?oRM}YogOWKBvQ?J^PYA&uJ@10f{)m`GABg$S zEUODd;lB~H^exLOkoS<+e-v_v1Pa6pCLU3;N(aT_*hp6J9GQJ~g>8!8_=v|mzw9tA z(9$)=ad>cp=o_D9IXx>-!Q_wt0kV#Gp|Ie+c^VNf)k?bY#xL+s<=VFG^iuv=#06$(A`(5acyuA?o5ZCX`vFzRv9_o6d45(v^C-5Mg z7rei(4uO?Kar#=@gz_Z`I_4H^Xz}+HwsbeVTh7wwkh}OzY-7j~MAqr&$-}tm!bE6m ztO#;7y`rG%_O4?G1NbD|XSWnXO({HWi%Q4}@S7Lo?sR_+Sb^^@t2qRxu2XgvSvvH0 zsihjuytX<~&kTftYR0;4WDWdQdje{g}BA$@MvmGdUWQ%&v|Xr1MJ#`=@Bixx?w zP`r-0w|_}Cb}Y8unygrP{t0}jS|L>L^eviAQYaJR*E}x>mV?7Kkv>?}_6_?-Cxe!8 z>^}Lq(Go+eXyUMYu7mt56b1LSelOr@Mq#Um*&V)Tpg83AEeU}a(5G8H(B5|KwU<~< zQSqR+ujOw==oJeuWqkp)7QaHhKa5c`iz?(WYr0yX<@9*GZlEu+i>Di(G+@k%P(=IQuN+(0mH^OrTP|LxGI%5Wi^Js#zYu?kn!}ZS^>_0 z^yx@ATzJI{pTYM~`Oh;nD;MTV-JsBjsRi-wDMn|`i*ZC=n z$=skMf;NOj)41|5<42=uvV}20w+o(TkENo>FRF`ChVZDcjx{fb(t2+m!j^m#HVz?wdWBrb@dTdfn3-_(sVZ$AA_{uZB=7NcBx;pnYWIz$;IGQ*kZ`Dbats-t76DC>F2suW9)nwlXY#UoOViT$*s;V9l8}E_w-L6wYpWrpA zH#vnE;TK!nHW3IMzL2dFjo_n1Q51P=I-!kq>jL?eyo&XEiN_EbyW94~3K`m3;K{;D zR-=7Jbf%zh!uxKVmaWwsivb9eF(caqGCs1@`*n?Yp_Y7ty5sTlUE{;Dsd$oKhP^_~ zQN>_ixZF#k5QSc{g&OdcLsJ~ADW^MPTapz5?zKC7<=sqZ=^PABX2Hr6jDT%FSH8ER ztQ09AIM0J0;=C&{%+sC$Czzm0^JYKorrT21d!*XQ+nM)W{(bNTNPR4R$K5)ge;$0%=>I;WvBgAEy?`{-;5*vmnPkHCK92)aoBRbUQl5s*; zn5AOn#p5c>RP9ryf?-EvL7nz@XQ6oWsXCXok|0?ns^m`>oQ*Yq`svbb+lZ&P4Z5RJ zwMUa2@-%4{#m+1hXUtw(%+=ozVFRuZkM~f?P(!w)5*~dRz1O+F70IG}hJDG-R=%Of z4ygT2VT+B6cDuxRm#V_}7WR84?HIuO&EKpR$i|cV8u^FgZ6)wJh~Ek})cRznVK=nXhzWV`|C1BhuF&#*Iw8k)5q z_AAS`yGxp$lN4N8r0ARU!#phJKFPEx4*LRg`exCl0L;%S%gy$QOC2`i631XT2=#YP zwO^6@Z+Acy%hwO%PnVlWmMo<0_a;bt27vwCSOcuIv0w!VfHBhI6j`j7A2Xchpkri` zqAG-X>N}v95?PQlHMlpVvb>(?5@383%z=?>*>R%oO%uqFlI73g6xQ|H6rO(0KoM(- zip?#`{qnd1BbmMnFFZ5Fm=2pHF^$nrRZXlq&uJUp-bQO6YI{B-9}G49p-X!YZrZNAFUBfDvLR0L${oU-0ZMU>Y2clqC`p?V$8Ti>}OZh zXKpY!4jkKrkJI9aJZ2yt zW8^mWrAPa;>1E)7-|Fxh*;o029?l3Q_!ny}?wT7`D>w(e>aLrq&OJEtqqz^UvO( zda*-KelaPLcD=j@R48>B(CJ)`+sirVfcrV>yud+K#ySkK-E}~ls(3v3qxA}d)5T8} z`b4#2Plje{WKJDj71ugTQrbyI=wVXi)QzI2d2$X#S#ZM!FZ`WkY{dA5{6C4aDuIz) zhh1JJt|xmOSP!5JRKE;OXI`3Low?=<{#FO7+>ceT1Twi@I3vr!OMnUfafeNVLE1*A zmg^l?FjHOzJVC)eYb{8K>|S_*kJ~qZ(2yZ2G=wm< z=Ab44I}G1fz3$)0a}8x-4_rrQvkCC&2Ta4?>{o;6i~1=R-duIUeWK&zpDN%y`0dJ2 zWwoJzsU^$&1xCR_4U{aqjVbcg;Cvd|uoDu2GR5j_=L9eS&efOjGfo)(vhFp0$>&}f zgz&x8gK1l$hUOx8^F4om>TS-lg*Xi5RT8*4lxpeF&^#m&Z|tOUlrKI%lPs~&XBf7} zSeC|d=c@tPNmiW=S5euq@;Dg#ty~%*9tEdKc3iZv$~7-qS0SbR`(ZZYwb97i^75CG zblv&!>WZm~6gAWE5^@{EW{Do%0b^CjA?j_zQx;^YcQnCz$)2GH z(~$z{X({(kq-H#8v9rcc-esB%YlxcB6~VH6M@+-3Kaz#Q2U+~P++zUq0z2}ZEFd@j zB8#K9Q9Ryw;$8kj7FLgCA7oMdPuwMN{hKU^-fl@xF1t|lno9%vCGeg zC`Z#Ci$$50tnLJUte1l7%gac%14Vy^S1V_VBpa&xv!R>Gg-TGhSmylQxuaSzznFCp~6W8sJ zwvqxCe%@eMz+~znQt!}C!$cE)(u0fTR{xyG;;vKXJk2k`%SM^sGcGcCba(vOnE+dW z{~P!M3|~w{lUI836VB*Y#5N_KE0`D-by04qZ=Vl}9mTP*SCi^f3$|K!N;WY|{?^SMtKRa0oktGx-v#2T$Dd>KR* z8jY6G!E6Vj7~`12iPub^WPT0g8u1qwEz3hZyQw|QC-~UWxQb3#;4l8|0U%Uq=w%hF z%<_#Pm#gz-lxiGgF#{N8G8w8~r!MsxNo^P5FL<-3gX+3*aCiH2%gMH_;7SE8iA1tq zd_>rorQ8KvUqN+=n;OHPtrb^Fr6{d6BOFK;{3kNACXq~=Uw3oXi402=3~x|;VSk`X z!oT(3gfmQOyZMD{W8T!t&Jb;o)KGwkeVj>=><)hovR@|c6G|Qm7%jv2DNhCok7yz4 zhy|cE{X|<(sp5EAYW(pWcArW3zU!u$a%d*WuH&H}GJ5H|qJhtG5nv9EDg~UJ(bU!6V43JXjfSn7tr1 zcawFQ6#9Y!l_BOE1g5fiwgn8{g6%OGbr$V(d3k+Ap@p+^K}?tRLX)bonPxqr7VZZ1(_%=j1k^)JT7s@Y-DY23YJ7OE}tCpxzfP)Is zyUz`l0f7lA3WM;MxWbBMkWGO`!s#8DbJB6r0aun{OdQEl-XUtzkQ3ZtPJkx(q|p)w zauqG6fyKni+oZ^YGpFGbIye5NCh@0>G41p^;;Za(I9W6dGseJ=r>{Rd>4n(bfaXKL z6-`GvGas3o@sB+p!prV0^2g#wn1&ExK8x0e!>hUBL|mr{T_W;^EL~72^nRV`mHyPO zK<3VuP`xV>n5xT*bxDnNnbehFv_yLuitO7!o1TuBA~j-%%)AAwNUN}qmIz`y!vjiEde|* zhg6MC-d4#zcuUY!FeEpA=p8W`-w^W;2N0zX4CenY1{1Wsa|&q9Uz}2U_eb0<|G?dU zW<9?l_TRYs$^9*skG1A4UPzS-c_0vbvI{CkPSU?)9_zqhhI$P6MV4I_o>QoD%@5f5 zEbf%1PfF~j)_OQ~hgJnU*u3NcQnwycpU`~LZtGmgs7mpwIB9}E=qPFyE3-}?kOVNx`bcb*gA0w{b0%3E7dkTH8%e7zO*}&;TBiv%kUPO zZJ!=negzG!+un_aZVxO@4FD+dF!>A4&;?dZ+H=m+sW?Q&lVXjhuHdf=ofw^43%$%! zuiXlJ!p5|OCGn&Dbo#8EU`-l0xY(yGcPp*Bnl1(vjkwOk!&^^$!7mwO%kbx?``dsb zGS3T-Z?7+}_ZUL?qJk|?JWN(&7iNq;)}>b+Qn#QUq;4hGq6&Me7e;0mMVV{2AmTa| zs^cJVz}K{#;GUL)t5W#65NAuJ3fjVt;C};QTbslq!@e@bPMHQmY17PQ05tlHqrD$# zuUJ5j7Xy6OJ&Vp_pV(G6I))nBmtKYix)C=%_DOGSWRy3=9M6jVJ!1F758CwW6a zfGRQ;>hay#dq*91u9~V^{#Vh-OH~K_G>KtB(t6nuLEpeY89b>@2PgmMief&j2y=~z zq;=OYt~Ha-EV%w;tnKsZ6G`+rmmveufkWjpWQn^%2|u2ts93$!Wkjbskw_eIsr1WL z5+=;zo@|J73*Lg&_I1zVNsct_NjQfL@{ z!B4yS3h!FT0v-2Seiwv8-esWEI%{`LLQM*SXhLX3?>xQ0JS8_NDQM$#aF%pDv~#Md z?sNC^5Z?QgNH!rE*D1oLlZ%g*Ak3s~2o30@w7XBUqe$7P6Yo-e@czIy!@G$n3gv;6 zs52H=wXw47^@!&JfxXv&Q7>0w3zmZ#_4Mi^t5)+9)St3yD?SkO>7S7*>04RsUv(nN zg8%^IC~8{U5#1V$@jb^Dw$Er5Kq z3=@v^T5&o;iBp-*tAt(jiX#-E6%t(2$K969w#Rtimd>IjQp?W4^zE7FX%OP`9Eh$@ zxish6A|SnH1Y_37Xq*UayI`U(xS%el%uiwb*Hq>| zGUr<%SF5*9zm-?RHi|z6AdNvfC5|nnFjycvt2y^c$1OApp7ARtxj|IQq9T1mL4mZqh2|esa+0pCPntux@SH_ZI5e=is}&fA@(yYR zVfPq$YCdmBZ0O&FQ8SUZ*9Nw%T+Jr_%ELEN;h(@|^jy3C2K+;?|c>yw5~jKg(|`MMfm`d3q|gI;kU9&~nt zm2jxxBKDvfY9aXF=NQ`TAy9@TF2eDGqp4@Ps3J?7`x@AC9Th4r5girWzb{CAYVp>j zX0t_?6Hr<83=Wo#|QdUFDUIbz3@}!fpmfG=$BvhSo_^;{tL1Y_wzQQ{Z&Y??S}I~&Z`S9(0*u|4pl zhxpjld9bkjs*-&pSe#KEK?3wXa_%V#9cv&$YjkBfmT+s!RtZ0@M0f@)7f5Ud+vJ<@ zaMbydXCKnr0b$$tx zL!O#>JUOf??L&FfTas$|EH|++emqq!m|%!s1_Dx)bYf7n8_oblL^_z^eBo3fB9*W* zS`oWKtl))H|du}Ywo1o90W6`UXkYEw3YSmk!t=MV*VjgRsM)n`TiTJQYFg4KYP4L{}riL1%YSY^CLgOlZa;YbiM#Z^f8T!KvO7aHuzZacWzuO+&P zqvUIYo`sg~l@t*95@ftH?{@Hb*;ec_&F*Me^)DvBEus0pb$gV0i@qBk+>>9i0kp63rLmtH*uMc003|x2^2?y zk+h$dqV*Z}gHDVLQ@8KuWDW`v#AtpOGvERDl+{6b%g7xcLhUuV4e6zyg22igs)gCI z&``@6A$%e##TeY&XgqOc!won~SYLUJd*aP3zpNJ*^W{C$kRY14qodbX+L#Y2FT7_L zkyyDjL4v&@hG8U5_+e~EAR4S_cl%{eLvmnd*qL*vh0oaeyH6+yodoT-SoZdGPTKIC zq2|id6UoQDDudkUWm6G728<2X$&|s&*rm}KUZ|+=@X;}VT$8rL0s@Ns=OG{{_{A+e zKa8FATl7lu0ua)e+J@(SZ;imt)W0~7DwvT)PFvLMf)p_z@T|?=SHykVpx3XY$#nv4 zptex(lA?IVQ^);52_pC7aAO4c_a#!yfYP({j$YRrCrVs~xvGs*Uo?!_dv@x|c|_c- zvd#8O%w^MI4NiCfULKp`!zOQ&BRHd^KvNHKme!SX>AQ6)lyl8zl&xSBNRd`-{eBoT z9v%hMSC>v&x?Oy6R4vcnJhQmd+2D0En?M{YS~drzK<1ED6c)T8_>m8P$ibAUED%p-pJwd<1Wp=jgFbHq=ax?VPP`HmbJH^bbEyA@_Ubz}D~ zrec3%IDXPrEUW3wB>pVVm0vHxu6&=nxsn*_N;~sZI0M;!*YTnrzIKuOy;^?dCM1dM zH&dY2Kz^C&nY^0h22xSQxP$h(vIOhEJ4a%G23sQmh8W?df8aje?GGpp5Zy3!bCl?N zeVh%hu6(aUK@s~5dyP7(6@6|%hglD5FB@KvMxJa zdzf&tI#7{LXs6`N*b`q8)SgW7tI+Rt#{GJqJkS_!2lEMUR4Eu)a@U1Y?ApM~&e=n4 zB}+uwXA^P@Z4oZ}*)_hVjrO}$;q%$VMEA2IgYs9)Ilv?u8w7$yJlnSbX0lM3G?>ci z5HhHqcj$&gIxnE1Z?SpSeSio0d?f<$pvOQ;cfd2y34!m-Q#lqptzyuz#_s@=HhugM~ z47f$wWjf-;f`caIgZ0~fmK~MleU;Tvp1Ho6xp}cA+5mBbK0pazy^`f zttBq=2!Fs1mXkOR3j@ipWi!CB;0VEkHU6`n4zD}lg!YO5UPrWsOx#xF&>`bTnRg0{ zn5P@$YoRK)B8ureJ-<$D1x=i-%GI-Gv(Ejlbo$JE?7ab2nIVwKI1WGhROPi1FbiAa z1=qM+y;l0|c`s)Bz@Y-7#CJ3}J)BwkH{+oB*`XEdJ52i5A3 zrM{jD$Zve>I`RYtGM%g`u0?2=B!|^WvrazFR$JX~zs;&Q*Q3l>hK(RJS-!5u` ztj-UZ4~B=6a0JY9S@ouG*bM^x7_ON+xG(i64^GEMkFCfD z>a5H-jJ-XiXTCny;gZ!&1}dn^TI!JDZs-R;M+ke9h9XVyYR z-LG7c(lM4p7KfbY(9@rk+aC91Vy*rWQP;vM67Yu}AB?PAnt>ggKyHW?)sg&vDq37@@nt@7Pm<0Za;Hc0@dSqIa)?$Zxar^1^rb`4ml!_u4YqD8b_Q>@oG0%RbO~^0w*DwZU?hc@ zXNZo|s=Ayn0$*!T(bFMlmN|zMpA|`W1QqB#X<wF zA>Cq2ra5E_$zS*Il`{D&Jd8_|?e~;lc@dtB8zVa=2karuP~DEc>fG545V(b_10_Z1 zdrkroI+{W}Y9m)b6Xr9U^(}(*CC^4l8O{PDnVbnf(_TMvVL^zlyueOq#aEqn z+50;uoWb_HCrt=h)I{Cl6_l0Y+`+}I07AQx31VGABJKF1BDdf1yYX1}*#io0=FuK4 zRHanuV$vJ%8&yc0#c!|1%6qJ9UFH)7Ozdy-7D0m!Vhm;+ur=(PUp-iGx;o0x3Z0G^ z7i<0+kh@y4D>QC1Sl}fn{0UjtG#2;+nO2R@Za-Pq)w$NxewUXLWV$_>IgGQyvT%Yw zwIey(f92Thkv?|hj);ob*g(XziGR-m?^`{x0IN7vzR%mN1fk}XLhzI(X4 z5iH4z<}A*8cjU-1-99bc^H(e`uQLJKG@Q+DM6;^KSQ%9UzA{6YWo#ruzWr9!MG2{t z^2z<6{dns$(;FQ>T5-;A+vS?%_c}Rv2X}5M!@r9Ic_sYbel!h}gz6}_?ubS9K@Pj! zhP0Ys?iwN)c*Hzhz1(faDn;7;-t%H;B%elT2d>jT#;H-9cQ?{AZws8cp4*-%e#jeM z$OFJk`{OfafwkC=qD4jURzqkzoU6Om&u zF`TnnT#yqa&e&K$SS2xTzNlFkLpdd5V9}Ftiwjgq6-`&;B%eBI@Il06SF8mDtQkX_ z?+Rm+!XUUMx~X(!(?u2mu5<+_^a1JpzDXrv=nUL&49J~ifLDU$ehT@{`E6}Uf*-?B z7TmbUzYB4a+Cbok%@?@)`QdGy4uMtJX`v&t80k)69zeO&IyiI|WTGI6Ba-&q)~7Yp zh*z!1qjbhS+|QjMfdlBwGZqa;+=SVUWl0P6@^ehg`QuW^|)#~whcF zCK)uB1MA`rvv~GgRxkeQ4_aq_d(xv3pEOy8^Mg+mDdQRV$MbJ-`C=shT%K+E8@-f< zs|FAwn=gZdZ1BHSk#iDpxgzZRP+Op!;-EuEPfin3-H3ETlKT2PFX6QzN)NvtNd}wd z^jA1#sG{AdY|)_1?09-a-hN|>x&NJedKEsA0PdCnK9_pyP7K1j^9W@kPPGppyhJ*a zy+?i=VEC7D0fangv)Jnc3|rRSFUBh0V^;V#{?80>@C z;+z)UM#IeG7BKcWYm4?nL5XDJ+eE#i1H|J3HthDvoM1!NVu%AWiD?gf^ZIxdXG6iz zRN5F4@5X?OH8hnT8GqEf@8&c}?KjIwebO=qn?E%K4Y3)TXOq$_NVf_UUu zp8ee~DZDQn0^heN>E-_yn*6($1^KM{M}Wii-vEdGTn_$$@anAv#QP|}Z~u6(Z2ve| z{_}0o3-Xy8w#|D+*MY z3|f#0b=e3CjiQIZ$bmV#2T=l5S=5#j9)I-4<2t|>q#8W0bO=PoRf!-8?bJ^D;;rSe zl*H}HvF9h!-V*_tCsU_0bb`j}9}I*n9+KXH_%K>K!J_)mZL911Oo^u`rR7WCLAKOE zssg!uZpj!KvDUHMTQ20A)i1AqrU#0P&?|c}Q#Qu13YZZAz$*+w!Zpcs;t3g#B&ExS zSa+P+J6skLPRY`PjwSl(Q`hDXzl^>8106V&_1mVo)>kSn=`O_fyF{}B*YSrxN#jE% zh~WN&8%*eHVX~B}a3#Z&8Rmx)bxr}`>|;+Cg{vb_GtX)-zS8X!`%_oLF-*N*vv(lLG4Hv@dkxS1+Ckc z&bcB2uq;jMb-8g#uxx7M9^zLD2~n9fnMcMh|rMYU6X*^gdVls5-* zSnVD?Q|2a!DMM%>Xa343Jn))K&OdXU)>e3&608-1lQBsb>Vlok(d4P;{JObbcpu_M z$4y*)v9H7AV0YyZRy1{5zv}2f39V3Skf>?Xd>$A!x67oW$k#PaSCL9o@oC^>3tZU} zQ5Ut>&7aBix3OSEi^JKn+Y9E+uV8D}A7FKN-V^tQ`}*3OaI2UiJ`!xiRzSs~y|W>8 z#+<+FF}vn60}QDWmIfVy=dVKI)Bz%x2VEwEt#YNAY$9XK{bTOZ7NEhuDc=s)bY>8Trvn6;^=*kUAWf(;grQm3k_fx8x{mR%ksu>g7Bu;!i7Puya zIk>QezT8Oe^fCa}WWxiBwJwy=yNb{Zl~?W^+eSMjkU_Og@TJ&%g5=9qEoHK%WWx9% zxELkA$o=r}FpG}gY}6-1Pke3H(8lc0y=++HjyqAJp^BLnPhWOo?}Q|-=TBIZO(DD( ze{3Yah$RxQjz2h>L^JKt)}@Lfr%j`MqVph0??pW{%)(*b?A*oe>*r1G*`$N}>6)|mJiTQsZ>XdL4AVlN=Wj!#Z5E!&sabTV1=R^u0~ z!zeht9_sDx5zsc)1PXN#xVdNw>l$uYR=t*i^CKh~ga_mMGPIl)4Y1(%)zmIEfTNT| zBlr9TSMwK^&#-hsCol7<37LJGT_`1k5MmyCNyq zcvM?DnlxJ<6sA6h}3ov#hN`pFpiB85S4HEtF(hp8^*uFni<`I!f>SF5Y2AnR2(*&E-^>x2fx1Hr#fhghfeqBrLlE;t|!KbNF|aOZneDf5JUp*Ij0IxnoT)1S^*J1xhZo`FVD7OH224WI_J&qKOCqvKKSOp zJ#H>|W98uQ+Rotr;+ystD1eu@WAMBDd-Xuh9y>pd!Z80_J>J;ASI;`){U|*6geLG} zWwOi|RMe`f2yq|zgSowR%?MyCgQFo;95XHj+DiuK)`9aBJjL>P9!N8cVE6I$ zqHRYTG1wQO12Re z?eXp(6JMF@Dfb6s=ZH4u?=WY<=uhs{;D6qjhhw+7Z0h9D?yd&9l3fjES{-sVV?be; zA~*V#Q|zl$5szZ{sZ*6@KD@H<;^o5U zJ7lw#o)01*_3C(Q6lKyOB$|T`Co=rB9^i})9JR*LWGWjHIUtp?3N|nbyyw-W4yIyP zY|mHXXNvCgducO$?533XJQyr|qj{}a7e>Y|JMc!#{;v|h{eR#L)Kd#%M@Z9%rRPps zHEEEUwy*nnkTB9Xs=!iLZ#(bO2>`KfinYbx_4b?OtlMW7F@BcxU%at|kbo=0&kR1a zHM{55Y16HL={kdG&FSV3@ZVCurTIk2zB#R#P5&xY0}G8dgqwL(y1R;(L=0^l)vYYa zr)0@7p(;RRF~(aVoyyiOwdZEJoEm~B)hxvE`LmxQ6boFOEmx@Ct6V;Zqtd*JSad7q zxLUU%wZ#miUOQ?do=Do~v(kILaVg}c5T4xvLn2eBVx)$rzKPEcmbQ1UvB|$G;R3v8 zk2ddrVSp5&m;1{M-SYF(#Y~j`XfYM{vb$v8vh|i-imXA_fhMHyRJ+$5u_qJo3fE!F zt0cT=a9Si4{J~xy7~FZ0Jai$r9y{mtvp+J_IKFV$VT}_>iuPcY%4sYf>{7{*kjZ2I zp)|zrPi@w`Kpl4sc zMBW*k3=<2}D=VR1Pnz+h4z zC$0U>IiJLv68lY?jpD&KPDajV zeujaJgA|%f^y6YeM&9=e+ssroT1Gn+T)cA$Z5Z^QFjeydQ#t+{!wadt=Yehi-;(Lv zWoUqBK!6YVH>LufK6brhYVE%;RY>)3OjUWyE$;RoK(m4WxoSGIjHP7}mN1Q5NXBH+ zyJoU9b){WGKlSr+r=bXzfE*;H@Z$GnawTko5wBH@&;(Ye2uE*Vvsm+trNAeKnCev; z+)!C%vSbD-btlnA1<59tS z0OkmWJm$_!yn=e=hM#wZi^$7**N4RznU*BBO6CB;vw-wSDf8DOn~DrCODk<05!23b zK!tnUCpkg_Xqtk`g3D|=d7d?-ltz794ns0aOETRPONLc8qB>DXO*!}NS#Z@ee69_5 zdV7a!8Tk}`**zZ>naI}+(mk7y;h{ynzj9c)3hrv|E%)uvAJb`P)QxklEkx zLAC$sSugS4v)+(xXIK0LHGfrr)!^v|O|PddNnX!Fde+8}8e#lTIFaL>tsiU5wrkjP zuV1=wr{(N@Q7uLTFYx6X^P7k(S##{|xzP;l-?ejj)9w#Jgz<;N)BV@sMO@w--fZ)& zkK+3%CF{`tuZ`osZ{<}Udp>L|{AUm$;_|nRA(8*n#?cG_5cs1ne+)p2;;k>P&$o@! zl21LbCaSYJoQW{|ExgqEdFH+EbNc;)1_*y!(s%h!i$Bjr4jQNEO6w=K<1c6a@-B{Rs8Qa-1STJCJh$&ugIH z6KdTLiYEwu3tKH3U161fFQzn#Z2D!Kh7I?w7~2*!fYX~!@A4nTOlD0#6g&N=V$PVD z7a|{urFecQ#``~t(Z4GOzbeKEI2#nvAHpHKv6KP>_lOAfiy>m87XG0N?{wTc2inVJfNfTkD6Hh|P?@K5N4y%%48%^T#M= z|LZ8|FA+#-|J!Hv-8W5^a;YzrZ)V27$8W~l-yath^mBzG5QBDq*PLh?=1+AQFCTNt z|7*^v56$1^G*tbVQ}}PBG1t1%b14s2LzhjIy-{Ym> z-g9Ln?8$K)_xx(2$Nq8wHuG_=aC+0^4_o7{DejwtJ^kxDs$pJyMt$2F{J$LkTEiGY zSb!2BfDifi_6JIPqI$RI>+L`7`HcFvJvx@}_5j)cUwe-10u0^_dPdRLWYz2f&th($ z3)>-2qbm-!p%vQFbt9uKe^3;V=AkaK)znF66;Ij74Bw*b+LfJVP;mh@OC4Qy#}Z`A zhJNXQ-HyGK(dyURNzvj>zoqyUFisgU6gm?kwOYI%)PK;~5(NhBZtxtHp2fY@RLDoi zpMXaS#g`k#l&?+4SD|Lu_jQ-dv)k(+28!vv3VEe1ngf|8Djz-aw4lPv*zM7ovvNR? zw%1{6z0rnI$Z`-chLS+lJ`$HW4d*jm7FaTIC1WGc2d?4a3LALU2AL&Ggu{Bz zQf}|lje5mxKHnr*7V4&L$X6V7i}-9%4TvP^nQdo_w!!c~)j>2UK4h=1nRZX^Ucp)Pm%Rs(uH; z^~q4KwzNDeKDVIh>nY_O0u_Cjj=jf5b@K_!0gnK?55^pulGz!JQS-DmjHVg_{kr4O zJ<`K2>mVEYM9|%A{5?AA4T1_!osXqpCWQ8g`P_8P?A!(b>dS?ZV)c4J8xnx>RsA=K$E3VL_8JxzQO zL85i>ZiyAuwji77&N!g^#+fkvTgE0r@nIcDsfZ~>Vq}mCiiN3`D8-0b`bA}Z>9G6W z<*yDn(XS!4rK(z^vQ7KxL$(Nf^u9=;Dc!j&2C^B}wvaAgKHpF(iMh1Rz+=$?*kcv% z*QbBw8IOskm2nCmdkvzPJ<^v+M;rU_A{*8%`|q2(Ou2xyMzqL$Oef>0 zY_HhJI8cf`?zXd2U@<^^egiZE^h<>6I?Mcwlk3f*{}~mM2U903pnd|S5Wk$=czS_l za0Vhs)Io(F`{1?gfm`wUA_Z|2YXnRPBac5}bSHITC*~-GkDh)N$J1!e4da0kSNAtK zbd4K&lv2&FfFs?}2iK$XD=Di|LZ+@A3U| z&tI6n@ivOq+rD_0e{T)|nJ1bLj^h94=8#1HdvoaSd~g)$|8Nxg``-ACr*`kpw7HRt z_ifslJ$Wh!+mY{GW4TsX0Kd0s-sL}YIXuyQ%!T>Sx#TGSo=fPhKHOXRQG+EgjE_ss zwHK&w`4|-}u>OvEtLeFXWuw zN4)5Q{X-7;6YaZ&Rqp?^P?GnEM#K|Gq@V;@$V`F`y7i@86D zm*GpX7CO2bBc7}K{R3BS;r-+(e1RXMXy_O}qXa*D7FOt#qkWbs+`!KXS?kE`w-+Xe zXghp2<|S}puBBa52Y2Z!h9^?=Z%`lIZ3;{0Gp<)9X^Mkwb;K_<_V0UV(q8sdvwvy2 zEO(%i44=Er)fPQ%NG2i_s+r%JY9^<(5 ziQ=UCS+dZx^5ZJw<$BhP&aa&mIrxKqkI)KxYcd|ABT6o|)7@897o?PYnqPt^fzDT~ zajSJi?d$rIh)<~nZLrWY#3%a?UqYj`G&iZT>Zub5U0y0ug(YR*QFyL{pv1NPSlWt@ z+OA6|jpkOEwQt`$pDmwlsqfa*1Qn<13t=`#n(8~HW0 zck9(OYNZ$&JVcZpAt0jpk7c=Eyk@R7lDsjLht}+Ia8$uRF zc5k?!zO*aA-~5hT@>nv3@)FrM+_d@YNYA?B$YfL_0{vvDfQ65-9a%u>Q_k!gx?ZI; z+blWM>ZdTCg|}2gvz0q7arWWrH2gK25T&?Cmu<9QKyIpx;px z|7wwAuI6qO5xkEj_dsW2UgXU%fmK$n!l3!!Rv4n99ts`VLaI)fI0PDtcQfVLY>0~n zausdwj_j^sU%FaQS~fa!Mw&by+huyNH`{PP0*|XV0-~0o@Pl@&Osrpxw&k7MKkFr% z57!fR9Dgnn9J`455XaoF^C=LCDJRe4kmXL_c|5(4vZK9Z>ZL z+x3+N*#OD|#@!O`|HfT4x2u(f3Mfppgzz7>cyZjX;AhFf`4VL96R6=6Tb##NYe@G% z){ZroMS`sNvQJp^1@*SO_Ah#rQHSZH!`J{+W7p(H!6yEPwp8f#cKhb9i@8|BINH7V zPR?pfK79G81kDlo+>A$2;YUds(=1J2T&WrKkflYZ8h_#Z)J9SiM{eOV=+y2wL)~&) zd@e-z{-?Io+_JjJ0+T0--2=k54$sv$ZYdxQ_f@7TU%FV~80A%za~SjDyo#!s9o1BKLYaN7GcW?A1%p zB^iW@vWH%Ft2B?-P{|4L|DD%C^3WGaK@W7HgFHzo!4qT6@<*7-d*4$4_t>N{aTS!8hO|hAP2; z%AD08H`!hLVV}4k;+fmw2h33vgwE-4GX=ytY$wJ~XwHP5F!=|T=UFqz<-WJ&?B%_z zWZ$A*;Tp~cUToV4T*dkuc@6jX-<){!oOtuo zDe*m-7iGAJ?GwU;>JPgxOb+}hrvjC%+M)3WlP9!R{6rm`x|Tf&v6&1+cnjG9BT4BH z*}DQ^O$fj@@RiP)t?`9RE2i&q=lpC=0~?SY#Hpk!c?s*@KPnM8_!&c4lvUuzK2^lm zA`kJM(HDJmp5*9YNjc`JfB*P{;tRr|V%?lJ;fb%1v2LHbTba)0p?1{1UuCUZ@VE5| zJLKf6@1Ko5#Bw)Q%+jxoNI@~`M|`!w^1knT2NR+`w%;O?Lmr}{s>dB^tM##!%e)-& z$H?uk3fH0fx3L9@#nGBNf1MgZdMjc*=*Sc#k9vp}vNt?}5Sd)x)yYA5q}#i+snJY3 zX~Sl2Xm?7MQXxdohnLRKv?LTVFob9$`v)H%lVJVM?zFF9yZXBdv{lV786B458 zs`QcO66ZCfG}qx`mW`@&y%-7l>uaDjH~Rj1zVYFv!o=(*@+K~=Vr{{X-6nNg=D=?` zZ-J|*oc$cpf`v#ziXukq(g=Fq))CbFh{!a@?9kZt@|+iw%_;LH8a1bMhrTapbl1sH z-db@^m~1?f(EWAeQ{x;3m7j6`tR!TF%I89b0q#*0SBj6>C#p zzqAlpQW=gTzWW7#$Ac?Z^VLu<+F&w6k9FCnhK3ZijHIvI{jUZjrY6j!ytHr`8HS*p ztOK;u`GcLpu*jR$?dgMWT1my)IPZ{5TBaddmRpgNz z10qil_frD*rC+&;TMPb<#$vT^4B2j*bl{t9yN7bv&p3&=`C$%*D?YD3Uro%r<;Z+D zGzKjci*+$Q49nn`cTKfDoACWd>ovW$zMnOHNKZ?=n#QXtMi)9OWc_zXN2qW7;5k7b z*?&qwjhu;lJnAQIStB-HR>mmGXy76X;n#g*rKeY}CS~_R6{&PfeYhU8J>%D`i}18) zbENa5l}=}RQYKfEht#^B`!}I!Nf)@tYlDMDL(KLF_`JTl$-8?-W}1gp%ZkFO#>q}= zHBX<>H-E;qy-58+g<4tbAS~|5Si*qyeeTP-rE8_YO!peaAOXStq?O4q>(CQ*qlP27 zDY^5fI1JPIwwyY6vZ6Jn3>i4BeB)`yUAMT!jJ#ar6VEl3(wgpYFd$52Q@oGYpI=)k zuFB6Qri>eQJw!y(TWk3d{NAJI>38-Tvz+~A-7(%ZN7=qaw-9LkMD~=uO&%d6i<$Ru z{Y{{4$(e+M^1Wz>s(QYcygzF{rpM$`cg1(Dy=fAy{>5#u^r_yBGx?+#Pw_#pPn7Q| zVT=Te1mj1>l3SeL{R>V6%o@DHEWL(Qa2-Pkr1KE*%w6+ZzDL*ZxN>C?^b?vwCd%m6 zVxjlq*FDfEbw9}680S;?O|P55L!)q>-bAcaE;?a%U~H(nM|IASORZ+V_>}c&?lCj< z&5Y>gY&_a|8HL?s6pI%%17zZ)x|yt#knX19ZzS_Uif<~iX>0Dj4=zvTpp+p3ovwog z7v9z{6Sim`{k>>ve*+iIA;49^UYyx+;cZrK!!|4K|8-sMZ&#ZY!Fzz~h7koOO{G!l zV1z#oofvr4eo&adW$zN%XXit8W}BGwa9+0Q!2+i6z**nEABmmF@7~9q7=~*WKTYX| z`-a9P@!vcl1;Wu~n@^Uo;{Hv`FhzWO*!iNv`;toF<;)EU7=2Fp>b#T*I3EM zIrNK_8oTdP!btyPeLm@tG0P5tpPz5{=M*thkHs9Bz~^iItriagmZ%I(Ilg=fE`GG~ z>wGXH2O2ot+}?dkK*aD7xYZk5@f&mlC;DEZwB^0 z)N%PnN=z+FjPlSuUiyU{|fI;juSy6K_qi){tk%YFD|9 zIBDZfU6x+aN!C;QH3q|)jG(UYl}<@^<^tW_gH2o$WCL4o=7=;Fo6x?7oD7dPn)ysu7ZS?vD!Xfr~n+OS%o0 z4P)bt_lCo7iFIjCI)Wo| zNdk>hGmPv|L~V{NyAMO6ukdfR$d|~uD3l&MzpWbMP%GZ~=vkV;%WjPtIsf4>v)aqF@ZfS3FEoM?rSmP!UN?v6@Aq_2M4?^*BF%mhg+Iz>@ zScpA?q_Z*z9fe> zizo5Fq_m`oamnLKpg$MggImu%Tj z)Dn7G6e$$Fd=|oUwJc2)SEFK44(F0+w}QSp24OsV?r3iooP|sHwfS48fzZpC#1>L| z7Y+_+tbu!wkO@}!jomh%53lG9F3h-x*nU~5(1qf2n&#_DbiY$C=79)4KyNN4bKMO8 z>NN}lX_pA2Bi-q;Lw@SodkP{ciAZ9K6h=|y zTl9iiKm7Di!p!;SIUNcV9J;L1=kceNIPul@_NZRnE%#Iw>&y*QEzbnkrvpH3IEPkQ zuvPBx?^VuV6I|tbc7TdM&`YKruq^~uJg{B28KMD{AC{o8|JP;)n^!i&^#l_%{uWi< zE|2|p+wHMDm`yI4rChe%*>+^O+QHU(Zw?1B^mfs{JSXm?!_)SZZE6HQ`bm^&lR zDCr0-5xt(y!hSh~_1XQ)Qsfq~9?2pSz5(Zp@jS2ibIN{18ZA4^8o_D|zVq}AmUb7f zbsaXRIHZD;vXTqcJ+af1*TZG#T-%oEq1c(NNq`DCjz{iV@JY1DIBhSOURC^ zUXc9I5EA4)T&=*=WCWs@-6Z->ax`juP{(=bO*shmivvC{)VY*mh#O0{9e-x>JH3e& zrKPfH+_ZI-V%`1bx2zYxP3@v3&=dL&CmcV_oItyNTLbL(jgdh(c26d7$?jEQ zs_QKaSYwd~ythJKsV71B?SU<CTm?vV>Mr6x=@rX~xIDf(a0eZiyF?wcVp ze4pESSK3J=>bo$XX1$T7HI-vE)U(jM9e=K#zujVq`tF^b6JAji|AS>0){v0yU4sTI za&F;da6nvM2oNX``oMO5Dk1DZ1z}U^+DW`I?bTGu2dC1ru7d7S#A)F;6%#4s^z^Lf zl6_=&N*qW&6oUB2zQ*pda+Gfm7Sa0F$VB&&IZru7E%HoECK-p_!>G8a{5^z)V$4fV z>Qi`|rAh-)Kj_J_E>MJb{OWK%eDr00)9;cn(`+cXTT-yeJ^A=Y&j&J;cFCKX1=^x{ zcVaXiwq5v)Ip8=B2y4rfJ$*1UZbI{_$v{s$({hsj6U4RFL9ZMX@-^lo+A$JQ;DympGBMIdpF| z-@7k<%+0?Lj9C#$`xs0yvn-~YDMBb-GgA^($TIOsZWt|Q!d)ImoX#@t9j!ASa~@O=Z@8u>}0;~qb$J{G6=^K4=YmeN3H zQif7d8Kq&4aKpE0UVLezq#;>aRgp_&2-u)6M0q1XfNKG;cHI{smQW$k7ba#lS97LX*dS^-l*k@GZfC?7KJC%1o` zqcSdkA7!~~Sz!Bv1PetjNMR130B3!egw3z|Yx8TFV&9Y=xEQk3CGux|P>Mu=b1?*Z z2R0DS{|>~I9vH~78rk`!nGkH2a{bQ%80@#LvZRL?p)6u<8+mse@6J|DmMkc1l6Xv1 zD2Tt=1(}OQ!^ISaVRBri|qRYE8k^s{4mp0f1UxI*?#n0I0TX2%mlow(&1 zY$feYB952l1aeEfj|4dcoC$1yIn#Hj3D|4-wVkMG^i{9e-9WE_O_p+LqSIjtlF!_9 z=pGkz=_}A3OJls^^(Pv~l^SD3RGlun&tmw*b)|$!c^vM~EGnN2X(e@hxL1LHU+8c5X zc+a9x+`c^aJ7PR>?BypctjoQT8MqE=@J$*Cj(x^uVD?e{+dgJWV7Ai%WH(?hOtMH2 zRv_UM zq_UL72C3qpXZrQ-2NTPok4b!P(H&2JFHptj7n-0Tu8fjM{H8|Pgj(DF_IoeOEJ~TI z;|-KlZLC;MqT6r7TYDdIA^lvUp%q!2dlG(YsDQu}O0K}I{~o_>+lzeDUBfQn=94vZ zBf9Bxe3JN38)BZpxO=)^t)tq%7$4k`M-)SCJEwq135*}4yl&=Fxv6{ul0!bZI%2k}&M`R0cJPG?{$16tF>H9P2b6QB0WM;0!L$3bG`TH@O zx@_LqUu|r9ltJC%H<2OF0m;C2T{nm~)DouK?0@~*96Y#%MFHj^KsWVLf#;8EY)P9% z66SekS2K*BOy^wIEt>Q%0#=9?=SKn6NWwaQ+JY4F?Ml*4&uF(m&5|9(nMu(Kp7#~3BXlRzgj33(8XK>P+g1#Mso9$j+} z;3w4XhrlASa~l{%i{QFltp)A7z}2!=x$ms`Jdqj!ls0nG0^4Kv&wzqEj~szTWE*8$ zmJROtD@&$s!!L3ISgs(p-aUAKC;xU2rl8g}1rI6qEf@hG@M(+r;{!CGZU8T_0ox!@ zudxhgQmn8nXJJp#<6g74gNM6(>h9DfGgR>feERY64+9HmL99xC zcqgM1FJME!xHbe~U1M-jPPI2weSWQ3j^vG zatZ*9S=xQH*=Zg+t3K}D#^x5_p*p%#QM;Chvf06kT$~o{=tX6#iTxS`TN*-uCy&EF z!LqKvjsY)eVP+EkP}#)Td&dTER;7biduSY||MFD#{(}kos5T-A>x9wCh7u49V+3A4 zGpP#7LvZbvydI!DU4T65t;2uhDGWk_$Ji0Z_}VWkEV`~37kmR5H)_iBA~5s3OuFB? zBrqOh_2lb%c+=D;lug_wXgBpxly8LVZIE%%ED}Tuumafrnu4;E10ZAkcmFWx7T}X| zO&SnLAFXloo8h(r16%59Rq=s_w|f6E!Rat6#0XFXY=3#E8q^ua_}aUfmIPOfaZLcm ztP-|*^@zb4C}k%O9kCnvXsLVx9V< z!_#LC)CaHlANrU8`mnc(|Iz0I7Y4i;{20dgTKu#$xng`73ovFOGV$%QIJzHC8IK~F zT>sN+$6_s|xDIB1qY76ce62PyPu8+?D;~J{f+XJd#=7HjmzY zuX=DM!k>yyy4FNg2f_3%@=*%P>7VyNQmtGM)Bv$weTDbY=kym)9^9UP$Wsi+L)wb+ zN1oSV*zg#;!WjR$X3*j*#u`f?V`JU;<%C5=lFN1lJ=TWsmA%JW5i4UWbUIr$y4cE2 z4rWKZfgodEpdtxpa=F2<&aPpNUSW-vfLQPE-IC^iVAo5Fv7PEhE=!{NJ3_LX?7?WK zC+hjelB*?>-%J{a<;n@K`Z@CwrVjc))HwsxL2l`TKze{)a^n%;ExztB#=F-TpItHj z=?gICwuyF?KO3}XqE?aSkYb4<`w-oE*Rci5809CANsOp3U%IT;rdJd-VT80V*>@0> zUjhl9$9lj(kN*p51w3r^$B90IPQ>EO9*WTymS&dDOMx0k+k;6XGU{*ALt{sea{4ss zu5+>I?LfX*5KLDR-o9xTh~N^aI4pnbWTgu1Yj;~B|LCN;iwuvlCyes71kXzKit<+) zkTMqUuzFKhc=Gl;oRs~i#x|V6R&Se>MV|4_h4>gSMvc6Xu(i|C7~7M-n}#;Kf7br@uDA|u?pkaQ)5-BITy!dst65Y6iq zyeK`V1PX*L_YZ+G0D-1k)cy#>2_)Qb@(AyLa?ZHo`~q|&mVnRHjIU{Kc6hkE9ohDK zZH@+Tj1}G`7MLB_PSL@zayp)s)#?W+?;PHM2j&9<+rK6dklp_gi1P*rmL(#ZifB+q zJ9R77Foj2~_)RKJszAn~-C1yqbM4$z}$6nY@6y+be7y0;6uX>Nn1n{R_D6eOu%bac>R> zKs0Alml_@GipZ(LSQ&*g&k^s(3+N4j~4}n`;-+!#mY793* zGzNjUss@vTl~XZ`)T7?In=sx239F-`!>bAh!Jw9}8RPN_D%=Zz0&Z7e&oJ`ZBZ~+j z?kh{x1M4V^;Ps;YE?#vjH*l(BP$uOQA54aQ_y`h7%B} zvT^;7NDWmt;2Aa;#{9>BncHw)F;_tc4SUBUNHg<6uS#)yS7gya>t$(|`rC@His7Wm zemRU@B8Ev12$gX2LCz6IaKm26KhdVyMsW=(_;lFG|XQT zo#mXU-9;)Caxc#6*s3xRZmwQ($=~^0L!ptR=?{VpbRoh63x|QNTmu812>Y|rqbq=5 zu>|)*2?U}J?h`>I8B18?;-NNfYC(=EvMlO@4!tS_dJoL< zG1Ta8Zu{Xp;b_F4aU2&7DkiwdWXwb}zxM!9xs;{j~3ZSR3~tPB;fj&)n| z0r`-R^RNtkcFu`F2iWZiHQWpos4W3=VDZ1Q+upkx!@J+W7+8JA9X01H?iJT;{`SMY zVH(-el)T?hM>Tqr`VGSQE2Pk~%50M)KM)Og3kzOIiiDvpTtl-4&}@O4g!d62h~~e6 z*`RALpIOZv|?Kr}=gG&olNjs=q}{F;?* z0m;Ccxn9HIA1iMi!7VVqgmIp`#u=!Y|KTir0dQs$Yx;gmga3UZfd}Py5oU~EwcO2$ z_bLTQgAT7)Xpep7a-&?gM6#Me(n~oQ@Fa@*CtU3n+>#S0k$cbgo;2FZc(<>F?%$m^ z^s-}9*xJ5TA~n!1LAQ~iL`;2{QAH9U8de^>Uqr1Tff6Cd{X?P!K%xf?@qZ+GsDcG= zIgb9PSr@K269c`pF#XESbyISgBzE3@G0N-=J#yvUEx@uyicE2s#>VL|kyf`f1PQAy z{I5;^_3E|C+6+h-DgJN5b|}C!xGR4SfebOx}l&fJK^8mx*at|I4xH~>3i_$*N?J82< zN}g|1O5ocR6|vdPnqL^c6ev%1;{D=lLX99R0*EQU2{${Ag<($r*W2v)0L&O*hG<~+ zgXUkh9R3wk%o9SHe)>`?)rs1SI(RAu0$c5^R8AMA2a`FE=4z69o z)J(djW(>u?ojzcGmIgea5DD}moCG(=j)QTYy2e@migQ6B$XUGeMKdp9BB~b;)o*30 zd6tL+9qC{rBg)hK?XV5%dF28~Y@JR&hz7X}H#?4pp-o;x3%)|L_zt4A7^fL(OZIpi93BQ6UHpn60l?S0$LO95Ob^~s9p8!*9;+k4TfLiT~bHZID zUcoqz{a0suhAYmb)F5X=+ro`oUXP@!@GhdazP5%qZrK}~km&w0WL9cVlyOd;QY5VG z^8qoqjRE3}Wm^_mve$BhlccKO?!4fQU%YdHO)er(T5QF11=YJ3_6 ze*Z2oZwOI9TjB=M-U5Z^|2rma;=&XP{f9!XfI^G)7=IL^55$JY`8AC5=rzu+SDZU{ zK+fbf;|2E4MQtTL=HfJ4;sQ9Se534I*t~0IBP3Cudi_nc>tjj8LC#KWz;$3?D+;#1 z_M>XhH!!r3YiOxgX!NkHRz5-`qqWBS236a~^`_gO+n=KHx~9r}6;~X=&Zq-!I9BoL z|IWz+(PD4GD+q7?1Qoi>`G-R7fI|Is0}x0*&I?ejG>;PUMsG%4`Bl(!G{K9RW!jxJ_K39i^>rDI|co5A;%x~=fJR@zPn!fc75 zyi5Y(?{zp&9qjMtJAtsc_uz%^tx-^_i z2}|KEh_?}k3U4oz4#OL~hWFqK&m6pDz_NevI$=sW6?ewZ2MMn%?|_@BN*5hzNx_>F zg>^kMN7HgV4yO(TBS?T3MYpR!iO%Q$A(11n!u|XNTrUE9u>`8ZaArWpKP7U!qWl}S zDktfqQ~7E#A`w?6#zb}aW0vWN<;!^aMz5W2qq6I8VzBHf2Q*B=&^B+tyY!I>L;G?~ zprk9bNf$sMwsZyc7h^%Gu2kQGoLMD!Z?mcehL2f$7!WJ!hh1)ZBiJ!6rU>4%1>q95 z;1z~rc`-enLfJD=^oqr_yoB%hBX8n_N(-r9$F@Q8H&M~L73sP)Re!2RGYKFwi zaC+Ab!Mus0Ox-xaal_Gj$r!pv#K$1#H@xtQwY#tq3H17@IIuW6drhGh+x{JqaeG_)-&1x+oqC5;}oS6Wo?pB zKM{~~)&|_<_bm*q=f7xz0mBogI4`L^tV+V1p$Q`YwaPB0ic&y4Y2-i%FcM`j8P>EM>akD)l%QecUFW(DKyUT27PchkeWTcqAljK0yx_TK0aJ+k zA3ih=D3tW^;EzHt5#R>TcQDQ!*Eo+~apr9WInOEZAgb-r+k*;yorf%%@4S%X>C;6C|vKhz-#L z7J0D!wbCm=-~SUz?n)bi+W-_}%)@W*l>54pex{|f4I*%&%8A_-gvLcP{SIKK@uK!{ zZa4ywf`Q`m{~dh}SV3t{?f)T-10apdN1(V0_JUi61CMXfzdXmu;fgPm9prnEU~<6D zQT=UfS~+xJ6VY*ir~oaLVVnHBw>aUFpP#n%BM`}c2L}=ihyu31lyQgF!jPi>>!VJw zS4c5Hp%4iIQU0IX@%O+N|9*7e8u={Vn?h1d@@ziLUN8D%E+P4|JxJI}`sPUe+?{+T zn=L7!4hY)Y1c@8Q_%}zkr3g|j)p%_S?tzqMci{!_gXb^_vHl_9EFhugN6tSITI}Qe z|8Fy1uP8YjgoC}DeEw%IWS5#jMX-T^Z!`Y+I0tx}5dvuh9#~K?t>q?tI+fP-lpqJ= zH%4lxSS68!{lODR>yajRuLiYxKgQ=z)bWSru`lh#YzBexl#nApP5^bm_Ls!#&`&Ul z$*)Nay!q~lrbY&xcC81D8Q2=3U`nYNIqS}gc;~hr^f>sAYSEa8UW@x>wAos?U9taq z^qEqZ5T2(@I#j#gGtI|HTjYZ4h2!>DX|-ZS`s0VU7(&{@930T-vLCoIKE$kQS&F4> z;H=RW++F@XuFBEF6tNq)7b0iC#m+`@ti?6qmX2SkvGYuo+cMz|&Nt*U`B={L=e75t z4-JifyqlidSwPFEL zE0|t)tR3*iKKl5#E>0R*LL$yBGoz~ua$wDdxAGmz!<^z; z!NwDfV&8cNa4n*mTfi@zfnJ7yUVthCto>zBZD<2*B!$;TGIKQ&YM}T6%$ch%N_Kz3 zz9&6G`^nX+Z?+^NH!rBMsjFs=+Noka%_0lEcHA>Q z{$h;&Mt~LKN&s?r4R8Pg6s-MqztQbb5#(@u?U7MWnJW%{0x%9r|8Gj|x`=~Qs{E+$ zz>Cgb5jq?(T&&yiV=B^Q3`_*xIWydi})M@mD3sKL3p&+QDf zptDerkt%Sy^Z;N4w!bWK2igd;#I+p61>?#R1#f_s+>~$gX zf9)>+dXN^{1nc!$8ODVI=rs=b`=5F((;qLP?L&g+CC#v2^8f24|F0J^sJd;=zr5se z9kNBd4e+i*!I|s9D|^AG$^9(!5$;e+Xx1ReoU{LbF~9C5tcS2N)bZIhFNp&zBnRw^ z&#G1bFqd&ghG+wVg6%I=<)ED~<|Wsd1FI!4et?h5deXu5J~=F6Gc8{!k| zmCwO$d&uFXZimA#CvE$O1i-E8i|y+FH^p;C(BM6tF$I$QaIG}p0^F+p;{q!00df&v zRZULgk4{wLSat{x5ucLR97FRE)Vm+d*XtU9$S?u01pe^_nujGNt50T8HP?}%UDw*i~dgMxc`mN;#ZBD%Te~U+Wjs89F@zb z;^b|zN3LzWS~21&x7+hrHQJ4%G4Q?o-%9o@c671t-|l?k%X5lYc1XdrXnxbKmF^HV zQJMUk>o>zg{ED0)F7-ENbJPo=>N5y7LB$1l2C0<47-N2@5l*jCB*osjPB{kjm=j zOTXxVonevjd9mK-Pt!HpH{D+8pT7MBIUxV=mf#Nmo<_LT#F(NrL9VBdea5A>2$Ahm z46j5fW_pLc#0$A*@`Z7#a2o-lATF0kig(0xq(=@h#z}Vix98r-xAY{JY}L4WwQ?^) z*@)_DS9d}VRH6%UQd*}3XHNL5iBiXK9TAv8i9xE{9xaymW_oG(a4SHvJw~SpC!IGeyaY$B{ za3%Ig)LuhhUk}zI)3K*k`ETYNTa=h@MD)9_s-&j+Dqhh$mDnv+X{o2D`U)SP8~i?VOsb90q_c=hRv-^N(zDmcG|+7z?nnMyV5lb#rr|TM1;2PS)?F^%jQnMo>+bIBQ>UvJD{uhP1PGk z8purxFQM^LKSl27%ysl0p)~dSM-F74Kzm@> zlSrExUlzkAuy*vKcH)RR~H2Z4b&oAso_9hX+=bGM5v7` zJ(-G~JT@e7W>8NF?I=lC4?e_=3b7(T4Sy~mh9YIzn1rhHrXfZ7{?Ce-vx$^CixjRS zb*qeCWAcyY;#dl8>EY3d>S^>u2@i$~sD+04Z5PK04OoBu3RmIs%U$>WCN>e72jSn2 ziN5>w*&wo0d57DwqJjxZ@$83+fHD>-&)Y|~OQBrZdKr9$najl75j)@9i8b|tYF_TvQC3&!0QgBz;L|MTHj+JTbXH^jt44H+x^1k*# z6S>P#^a|%DH)STaC!tycGQhhY~tq#qV<&1$|or+!WhQ{RG zurLNfm#D9?cJ$;M7d5vd9PKX#yD9J8(1~6{yh-~78#iQu{hR*Jm%4FzZ@(pkaEex; zE2KJNDsQ4lgb$pEx5w$7%8}cK@LOr^b((G0DErxde;H}OWpg1iwWItQlPy2L;#=Fe zB=-w)!!60PPkmDu#w*X0IS((MvWFoe?=<@H3s*q-lwUo}ruR=rl<3f5Nq?C6Zr?k{ zeWyCjji5XuEV;EJa~RQp;V~mOx)uA*R(zD@i&I3~ts~bpV&UKQnyifX+BIS|*gCcJ zpq$cm-Qt9UXX8vbB>`31taF<|$w~2JPfquDMpu|b-CnFPCKlq-**vnUiA$aK+xXxx z9`bDbyMATymN2%yXdKV`!E%(WXNx`8M8nS z!;cTMRxdBNMDAy8KP_5$()_l~+yNi$b=s@Z(RhVmqNM5lBH>+r#iX`d8;H#HjY%N} zIF{;=2<}G%he|fec0UQ~eFGo!S|fBucd!+&=JANpVh-g;IylgvayWp!C8n2-0nB&3apFP-y?G!Fx0A_aG@ zzP-97TX=+EjPrX_I6fkoj@uTY$QR6P)QX?IStWzF-+ry4R=-&pA&#Gwow=4mrsJ4@;wyJ@7)*kF$7_4#-;u=xle`(k(HvpDJxtfD!6mzZND=3-fm85#2*aJRT(x9;k=h> z?3N%`peu4{H&jmz;{J5MQ(q>#+*#$QPaiu$`+Fowe%XQwQowSizI}?FfnNKoK_mh6 zL}dK8l&9<=L;|`HRGshYr}$+z{8TXHAH?s?$*%)d;I?$pPf{<}71`>->UE%gOK6T{ zT@w|^@zW>6uRkovMU^G31SY@yvU=v%9|y5+@gtY~M7jB~Jbkqc#rPtK*Sy)O)Qjy# z^8y|@h5+i*ZY4_0X@U-eUrDPxzb}8ScvsGORLi!nGDJrixFXiJsC$rBQJ@N} zV5M}Cx~FqyYtT^4r|fzT&U2$J7vlFhOm7JdX@k)+6o{7pcbVuANPdB>RO{bas*f_T z5&ijI<4>ZV=yC({2*}aFc0J}oQt%4FcqQ^>7zyd?$3S}XWx=G?O6QnEND1>=mxPl z9U=x2xoaWcSwcViyqY|^EKAFgm>p4|CqxxwT|=;a?>qw4mGlr-`tfsNp_){j_XiH5f`8h7)51!nHl<>5@~ zzB!-}A=}~+Nr+*qX3J~E`<`Rw?lOXPWbYU+_@GEs#JN5c2ic^Ru`PzOM8-2Y^~9`D zD9OzB0%0FTTUJraE8fVW)zvyYO~`}9WG1HYNi}HTn>6r_yN@_w2DbX!z`h26fj<<6 z|1t3U9xQm(xn-Dv&Hrm)UxO>*{JEU5-Do=s`X~g8gusN7V-?M9O44$T6=iYU_)f>v?ToY*FK}Qy zt~d|^L

1cLqdc=ns%m>9s>l-@+?O_F!r9*~|lUWt(l1Q2#l?*hw#z7KhacLnr|W zB3798Xb{ww&tffX%S^5%FC)>4i1uzp>T`7Kq<8)dUUhyQ8XDnRozY?6C zjbD@HJupgO;zIqP6P@)6Q!zgAdB>q;|JzO%79Ahs*L{!6J(&voPP}g5O!A?r^YiWX z8E3u1CM(1h{gRgaG8oN))2WfwTfHxWu{0&zv~G`0F)L`0KJ#|>p;eo%=VxQN)%S#< z?N~+Bfh~H3#6!@Zkqp0H8$32cnqDEyM!_ON@Qz`r>(13TugLp&oD@}2Q6VuV7l|5o z`Tncp=)CK1XGEWgPHaA6I*b?Cx_3o8 z=f6Kt5$SecG$q4`k!-#ghkOvemEw7q;Ia#udpp|ri|M2XPmBqH5a=NWZt&{2$2g$t zA6>iY;RzHW{6hiPr+>E&+~os$5dyxJKmq~|*sl8-y?nsm57Pr+~W#>CpY8r*(f`ep#s@6h!}SKw_gdo%8yPG{1i%i_v=)f zP4$$?X8+J$pN!wV;H`QYyzsXQ7g2X>zh?3mbqsBI+mp%+;sqxP^Kf*PJ*o6jc(X@u z`RgV9s|AO`eMIRl?Pa9q__Sw%cGe4XBSASvA->BLhuB{4Tp5m;8tiVx<-S=IIk-5D z|2DhNAR9MJuq0TInJFJ!jHW;3KVkm@OEB9yIq(UZYlyqIyrgyTzNb`dRy>hb4k$^F z0ur3N!Y8Dlu!q;q7C>PG9;%UD>@;qIcZKs>*2yX_A*Xwj6EaR`8H9>{QN9p^-# z(jOB3t8~CaKPaojwRL>?N7B4 z6TNs%$+bs9d&BaLRaR*g z;hszLp0exQkJ%jR2ikKsL#a_}O5SH=bg%DG8_7x%`8}P(pc!j}2EO9TSzK^-jI8;h zE3x75&c^dcX4+FLq5GfwE2#HgJj2K}C1?21`}U281iiUxrU>z4wuu_ky>@N(g?hVe z^j8#q(1IW5$_<p>%c%#YVV}&z0RGT(vz;UC13r?;FY^-F{^oX&l_}Xzneh z>;iEUCi`*-YAbzn6Z|T1N$9H3Yp}JT_X-nVj=38)71sV ztll!1L2L2tox34RQ9Whsj5+(r@vF5`A=vL94d_c0r&d2zU*X~s#F~*zk)6^+4Iju_ zvEU;2XO4WJp)2bX%=viUp_eu;WedUrsc`5S78%`F*Dn<6#oMu~KuUp?7(0z6W5ZhXj# zg?u(TpI+axd#=qv?5vS3bxx#kswL|fm@AmCrD)q1s>=1G<8pO9f(WXO4Jph|w)Y`+ zmo0~+=68F=f?R}?Jw8DhxM%o{#DgjJ=8tn7?wy#h8WZb$37t~KQ z{Aj9Q`pb9Uoe3LnG-0*15>Mq)G4wDyeMw)&Y)5R{+1n(`A(&l{+&uCIfu-p+AGV*t zndb<8hx!vUtC->S!)(KQT;oFfYn}aQvEnE6GC|za9TFZZAs#|VONuQHNz)>+cSWPr zoIGq3v=o8~*FWMMIO`BzNc?|%y=7dMTd+5L(=8<}-AZ>i(jC&BqBPRo-3?OGNT+m5 zcT0D7*L&aW*WYut`y4;a*SV(Fe`c*UYc7jqvbtu1AMjmC4y((t5mmCmU9h5(EOp%( z@>&@J5+9lqP!>C@UQb7?#}bQ{zGujJcF)5O78QXsy)U>DobyjCktHiI!AG{givvS3 z)cvYdv=Gu0cKwslzXJ?m+` z{`0zKT4K_KKSsuf`eP>t7iV8_q_S8@~bYVOArmIhCk-+jpffFX8Vkkm!rTd43zK~Ufsl>T; zp~>6XPdqESk?JGu=-9>`|{wSFe zGN`MIAbu+`P)au^bxA=8rE5tpkPC@Q%o{Cls2E zjwP|6?RE$~HaYUDbq z#pe)nm(bCDdvmNY9j0_aE+)b-!(xX4V!mu=ry`zj$`8TjZ|`hY8ce;&)u5+_b8J^M zP}0|08B%o~y+JV+2aK`*dxv;!@{;Un|JXoePtTJH*g`8A)>eav^j+ z|G#pv^cP(!=j05DF#2!G-bmzlsCB&+Y4XM(|ew6(Y zXXrqczVvQvoXIvoZ%MZQB!pnSL2HoA0^3_OTeX76^Ts3Kvqu}A7&Kf9xb2MHU2i=+ zG&YDmLisCJqZrGvN>9;{eaXAj>Rb6&Vl6+lR+l@2Eluh?yD z_{i-JrszAh|48z|x-M%Zx*=YH@8|s;wg%#u>!8oVO<;zp*SY++I)c@k+2m-}3*&U! z>TrA6h1Z!Y+O3bR(Xf%Zqi#+2S;tOkb+6y+QU!b%hrigYMexl360F4?8~TQAynRnx zfaB0`TpJ-9+yUnrp1kCIjWLm0-{nU5Yk_sCdSt{*VA#>?)R=G;fmQcC4zN4PvJ#Xb zJU_n{Mbsj>JrRt;J0~`WuUmtLz2Phv=lHSd$YD5sX)6`1Bb%(=FUN~kXJr}d1K z$1CvE$w+2xr8hFMLs>sO6zQsOD!)0=Bwx-C!m5PzsN$f(uITUc*tS13k46smJg%rOP67Z}GRGFPLnLS4`l~;I>Wz!=iT7g2ax@y0U(A~x8^vLn9O z0CzOKQOP07IZNG~3VVw0={=~?dS|s5)X5lu%aBEh+|I|X*Gw_No(y{%!7!A$Ck_u# z)T5sJm6byV?CA1F(5-~T`0z1Gx1uN=qy_8sXlM1rsV6fBTvLGJnC%Kq@07AV9uM-e z5q}T_IPO6MW*`AC^d|&B_C4af>>>Vmy%UV{8v?k1x2|n|S(tYd9*@LG1#9Dl2vG@X zDzZ*Il0oU%UWFw3KXVmCT=rq+z4RtyYfx*{B|m22u-Y`TY*URiTq-3n_aBA(JT=pL zZ~|z|EgB6BMp(>*u%*JO$$J5n)yK?Sdv&5ZwEE-cpA+PbwyoFu#EPaLG#vOMXL%~G zMNHE~u$O7P7Gj4czwiKhXWm*&n36Uh)n5*W>e~Tb7mvFH&L~PtqitO2xdQ0 zI3LqhRld91)SaL+>m^>a`syd5F-ECxViV@CcZAZ@iu8(CCf^nd>y-;jlV`@|g&a^Y z^rb{g5#tYDFIM0`m!A!U8cfmn`wryVP-TvvS{TtMD&mJAdWCD_yrYcTRB$g3h+XT* zI&9ln)q-=^G#6N~NfE>LB72|cK`@3<{BcfZsz$A?*nY;+QLqttCI!49c`)BVX3v(? z-Y~t=$u1{&S*_kwqZo#}H4Qm;g_{fce!pmQLPs^VJmkm*+^j0Om9nr5cMj{TLs+3g z1-uJWhR~z7E9Zx!u~ogAlJ#nDjP+|P@45@XiqsW7_4dbVt3JdTKjT%R^cMX|-tQNo ztgR?x#d-WEl$c0jDpwZCl}shN5!VpdBaCl)-yT!y|hfs)&M-oZ23 zclpLqOi5X&=R&m9%=4KRW2UO`l}RB~h=#96i)c;ykR;8QP97`l*XBw*k=~zgqD(Gs zJ|^fCwP<+$aFa7+AMAHt%_%fPzvR5Q-L+aCwYsWK1ihVN`OtB zrmRYm-S=|9Tn4;a=5<^oDKwM)L!JlOEA+;mqLJ*?PM+piL$oYQ!hntL>Ypt%AI%+_ z6}hH&nwf3IEVTWeEE1%@9j%6aT8qg(7Pi>qd8m>^hD7e!MhQ$Bx0#hFzKO+&Czxm+ z-ojE7q3yES5x6#0y0sd(^-B_^hQi?w=k_1{QZnGo%}Ta-St=S+Ft@&ke+hAyE`8yG zqphic($zfEVNbVUP>QwCULi`_Bm*`1DsgWH8rL-LEqW$nOh%c=S+6*XLafjcSncjU zgfx7-YZ@yr*o@FMg#SQ=)Vkkz$mm<{K*5QAnM{1%qlo}{srE!7K?{yxtPzWXh#gz) z$-4!bz55USH$IOjoP_$|>8M26=Js%0`neJ}?Jg*0>;#mX7CCcH1f0x*mN8XdsLkbG zd40rYnDWk;>2f{t(gnnZ9hk51IXizQR>I}AjP{_{bJ~y&YV+BG>ty4&O1?gAITBa) zjJmr)>nKEGPe4Qr_w=d=D>Gz4#Y7#a*uM1xd`#>h zX+9+P6*Ui5xwYu5`5`$hv~bfKD2S8v8DRv*0|D<>G0IXg`2(Zjd2v`jhqf&`D{Z=% zyoGNlPlR2I6vTI>Gcw<7BS2f?vE@wNycV%5+60%=CaaL_8r|;Bo~_Db#)ehnu2J=J zq-4w&pBrd|$iG~&$&Q4*Fvuwheu`_F7A4^ycqz)N_W#yga@_|i%Fg@$D5u(S&%%Fz zin4^-e~L2d6X?GkxZu8kW%$4ArSKoW4_vf>{Y7p5f+gq znf?S`ayWLa(SPFQ>#Bo$o_qrV9GbckiqK8wsbsVgg1K#QG?3r8g|(N07+|E;fZ3zy zY*Ay?ygsZ}d8(E7<~Y%xF{^g){p~%>8xE+=@9?_23wVNhAq%=IGuue1aB)r%t@e5y z`fcG6m=E_n9IV5_#Y*pc2M@@}AzDRp-iyZWBp6Zl30?`+zsrDN(Z=+tC>4CG#i&oE z2$2cTi_2K;&FlppjzbqjeiRi>$$UVA5q&q{-MR_?UN0#BP{O%HGS6pM;0H{F_Xy8y z7^EHP#p-KINAhYD;LUnPY2UCayMQ+BLxo!Hx*mqxmwN9pI`Osdj=X`Rdtgep{f_=^ zx?oFkVFNnhxQRp=U}QXdLqg0OkR(jaa+<`-a62+ z80N}GM}TQRF-)<11RONe9Z|Jj8C*(PwazHmfRPF%nC7QE@%@LKGE#I3POzxx?uK9l z-qWwy=DWpw=dpeWMguF4v@H`Taef0z&*tfKltQ;TGZ~6PKYJ?U3}dGk2))qQ5*HUu z8`^NKun?&e|pMwBl1ReP@Kes4wQ2lb*O4` z^i83{4J_tl8=QyveExw@Z?)&KruFAcO$Bws00Hn^70-5Xd)BEjUQ5%QXT{dPF`hjo zd~_%+m1mIf2bl+95jp)~gssQNV)m@8{-hqt4fz@vOF=8mmZJS} z{>W5UqT=GBD2~&!zNitw-7p``4OquWYCBv{8ZNu+2_N3kHxamso$H+l{R_l(N=J#N zS|?d&b?Q3?Z(A*=Z%e!$FK)nOJjtgqC!L63ph<*RKUiEr(peo2H z@|jxN3T~=-5iv1|JT8!Jc72{aq~AC#h?Vt|8~JEWoW)ycFE$UseNVrl&^rs5uyhwdps-n;B@$?4r7gT&*!~V)`g+6k%ZxwhnFgqx0J-uk(+kc%})U`k%}2A`$jR!rMA3)}y*s`b9hcFf&i%75ZY^8GM8IzJe%VQ2yN53b`2oJ4 zdUE95&r9|gyAMiQ50z6New_`I)G9(*k*Eb*_9{bZ1W?aM1lJ6u7wr(Vyid!2J;qgqRhi|3=K1yx--e77PFY->In;I*{%aP+(z;s#o=`gUXez zu_A;U!QYPDMUBb4o;a)bHJSoEI~o+0?94oM%U7-WLrq;Wi{RXZ3-Kda_7R4F9d^c# zm8&7wKjjfZOwz`NZ$P5$07d)DKE3}XCHyhu&=HstVt}uepHHHJcd#HX-e=GNVE5$< z{YOE;NBkEBwg0VP;`VO^)qrn5OBaiz(!Y_$r$z@=7Q`QzX~eTqC;9n$)`QrW0@(`z z*@Ngmox3d811~~0{WovL+yj4vd~zB6`$x!$+qh>*F9J_K{56NYdC6g+{}wm~_`Ld` zO9L9KdH5-*9F5rPGNc1{;}DGucm|fssFIi1F1i38f%01&?W>f-&Zy zTToxQUYc7jf78xdBde0fke30|^IJ62*YDg{;{v|`(^pJNXo0?z z)$+pPx^9%wQRZ0bbl>Quu_Ln-mUEw%bz@13o7y{S)3vkUYdE=SfB4?FHK@x#T5y6W zwxobbqxvW|UwAjKTg?{cCGec0UX^FDmroS#j)T1Bg*H}juc+?)JW=nrF@@0=sh5L2 zHZ~{qQ-xchH{)5ojp&P+nQNy^l{`6)s-RywO@APPym&;)lwvLnuFvtiA8S60>=y6h zWpkd1z2tE==}ZOSHE@|*KGQ@-#e@)iTw|LxYn44Q>>Ep>RlC;`G41wg&_v$I+scQJNoS`Ne7@lVAMbWHq5Ir4!F9^NB$*<`s+SYRjqp0)B1g^c#W|#SH zqxG1eCtToL#)cbo$$LXGXO=TNdBNAXTIxuqnlPEX0GJe{MZtAu+TaY`(X2TAN+nCy z@@FY3$clT^Qt3INnh>SWSnqIZTR&@^f@@&(;T1`%rp(}aY`ZjckM0QIMZd~fYTctm zIV4Hlto^nDCyIWNqq3vOLvVrMe`>vRQ9Ou(G><5hKsmM%qk9?Khlu{f+cC!xwepI# zwWjHecn^D@!!5WDi|~`}3I$W0H>*Q{{}pS zzj$)kGw$st*Gw}bk9F=5%G6a#6m6O)-nIF5#{%OM<*t2cHA2E(0wN5 zpkB*Pgl%rQF}wFPg%j&ojURZMAbUf4^>Dl-p}Lf4%(QojN->&l*}3R(ZZ*w4lncul z>1c(F$m9&K7Jh#yS8ljvVu>+?{b`JFbeUhu%ykeTz*vf?l>)MS$N6l!S#f0`;Gy5H zScH)eTVlOdm$6yy`^Ag_-vkL|2zO{42!nH;{+cKTUoiNO2T7nrkpuuGiZI~gW)K(6 zXK2725X3?BpF}b6NC?8+@;{#Urvb-M{&UtEkO%-AH8+LEKMs@HnS@Eq^-H)*$*Y>| z8D_;QAFmT6_6yZbrZ2z7%+2P-sDK|u*m9zBKV%$HpRux~P+LVaX5-hPoKX;^Vnpg0 zTwc^Wcx1E1(3M*w<6hB@-_@qwbA}5bbbb}Z){eHs*pRqnWIB>COzP^GEb{tDevYYP zayCJE`fe3m6wKwxRKIkMYG;BR?Ja-1;#Vbl59k!QPcFtKdLhOjMT3Bf{v~A%y_oBd z!C3#83zV`pZ+@q&v;*9;^hgj#;IjSOwu|5;Wd;8`WhDWAr!3ewP|Au#yszp)mCTWk zfSnfjY)*9JhcXyiE=N&arT(U-5so$csEB1{2Fptvyu9I{SWoROS+_iL#%yT=AF8o8 zJDLB?B@9|l_soyXctQfmz4@`QMFLt0tEZ|mYWYIRP}K26NnG@JJC9&!Dx&7jpewbL z%SvjryD6jqUowbf2%9@SbHtpaPsS-Gr`=A}8Xiy>E+ra$5owHOTN>Y8(u>VeLzB|C zZxs*OEhFGYCYMpAb>|2mf5foZu2mD&RQD%hZ3Qx;)H9@ZR|^=tQpabLv6XX#?moX? z3X4+>VyGrlZ(VIHQ;oXO|Jr#N-1v%wC!X8GVY)MXTS4VAhAaMN zmcMR;LpMzCPdMCJzN^bFApME6lJtZAQgi3PLrWT~)rxG#^yZ4p8)~IE8kU^SUk$I^ zfr-`Q{#@T-x0hs3@~aTG@Mz2v`jBz@F}CDEr7wn&@M4)ol6wVc-u3JM;M z$zY6R`0V^d??jQ12qfNyh#?GbULsuUza*et%^wjo86ch5OH|NLpW zLoHdO2)pf&Fc5wHo3J07MgtFYKQEI07JS{smlRhyKt#! zBc){Ax(R&homwCnBOyt}NJECJ6k$yI;#_d~7mOixtAa)M$#;%mL)J;8=%Wa&Ub5S3 zW3N4PdzG1{*Wr-g<5e^v-t^#DYNQg-v* zg#@jhH|!Mnn6u3z50 z2I8Z>U2INBa3nv}^4OD^-~`PXJy+R7w61=qFD%0Ce7Jb(`GH_CP6LMS#&xr1Q-w`O zac0js@eg?;wb$-?3kF*op^W|C?;_APQeEQe%cE9X&&#~E?7sI?h7H<-+ojGj1y9p+ zaW3hlH-a~+zb5=vSi(Pen)L<^Q%N-Fhi19Vu`Dw7T9N4^r1adjGP|xPADki`x|%q4 zH`C7zOt!1H!nj4Jg!;+dvra=Y=jYpTc-ZCq3dL|frONA*j`%nB(f!9%mkPEJ63RlV z)Zg<55cr)yh#3n+%)jhyhF>ta`@h?UbkL-|lYrHKQdYsp-~JGD1M$Me^MB<6yn~#L z1+^_*Y;kYJA6ahI%UQ8Y+zNw)>sM6owuhZ8rJGV`{}lhpNdF&%dJN%Ep--xBKYDCtFA^#AcH1|R{SRNJt`U4g$&77 z_wJyMzZmDOQe~lY_tUU*e>{^J-I++<2)ol?PsN_>e-kuR6cOyX!}Bgre^X8COlA9n zR0i!<&DvX&eM(*SYlB5YT`Ehr)>%e2(W}5I{&6~d0Ut-Z3*#`sy>6Q}th5$ircB5) z3AP+l%%%tPO@km0`V^NMs^LS_r)$#aM!kV{illz0OY!%9(tDSFbe(mJVwrV#!$q>4 zFsXjEbF@_pU>a9Fu*6neiA>0jFr#u#uH&O7SAni8ZHpL^wijK;Ee{>SOS%Qe%mD5=98UYHo z|6RBv3g-C72zE^+<}r==vrV2TJL1L6KFbDzvPZJY}u_BhoeXTwlw?hD3@zxW8Upx8~inNqP zYVGX9KyEVQ1z$VT;fT*hrk#KYL+SZo;+-M4?HfzD6=Hr3gVs*t8;B5-CyKVBmXm!1 z1j?hXsLDRSL+Re!Vrxu=tq67f1+(SijGfkuIDv|fm&{(8-v@qfHHX=Rlp*q{mLU4? zRiZ11e$XdZQ$$`#66t_Y2p{oqOe)vhMQtgXD@;1Qr*EycJiHg+j!H>$iW>50=k_d2 z*L-wNLiKm7OfOIXt3AUfjY>n`ExRS%#n)xTSfuS?kNKF`GC_SoupmO?*h^(-DWS0x zU9M{IDU`$R^t-6l6uno#R8tR3?1*K^+|eMuqEJL}8!;75C#`!VAT?Pdj7y4*lgqWK7)n=EX#EVGg+z7n7zTi0*kCNw?;*eMRh8k^KN?+346Iz%+aqT z3?{<`*0XqHZ4Brq~ z%@v#0!*z+}p(PPhKijoG62v$GbRk&-iK_&SNT8$Lhs6;5=afsX??!AO2;S=O&ZFPk z`AI0fJK;o>>F^DPK>8yC-&4 zez?`Pd7n5`%=h0phbYi1&P`zk0sn&~6YCX|+O)cO0=Aue9|qW@C>jzqxXxl(Zcm z^#(_*un|4UZWS0U1tDh6HVi-?c=7_GKlct`N{_@Zdk3+9BWAYi3u0nF5G#cN0PE#K zG_`D;hb9mZ+;huX^*h;h9$=^R zsbnOEFS&h@)au6HBty}~aG0CcdYj8nfBG(td~g1T*pHJ@Z+l-!H{K#i@bRY$#U2~M zt|ToQw@(Ec!FI=8brPFDwX=TPj(?GaV{-tFLBnFsB- z2hXD?wemH>jEOeMEper5ieQoubk*83%r%r9$0*8GZRXoB`sHKC+CqjwKbVAQC})uWuZY##jmZn-qUVNEqV$pd8Y1)#zI@BDcK|C0HF z|HypfxMw+ep#168@K65i*oOf~0Pog8^rzb(xgSYDZu_qDZ@1-u4nNz0?KQ;kz_YAHRE?=M!!r+6kNTCK6xTU@Q%ff8qvE?hZsQf9zV+*pO?7LA~G*L0fkXMnXhdriH;YGT7{NEgZtnbYh zCRNZ%J~WEo4y>A~53n01ZpyLz+DA>5RciOn+k@UC2=Ud5dDWiWol~Z55*s#VqzeW; zWw1=;WDj<@+Yg0A)LUMU_gs2UbM1S(C6wIN%a(j~TiiD?xKj6S z7IXC@{aVEK<+0k&O-EEQERL96Lmsbr9nIaDL0G0=Mu*R zXFY|Rt3%X3>c4yTJ1M>5hdYsod}}X-Gh?d)-o0*rYbg7=feH3$(Zcvd%6y+w_047A z!%XIpO7vSY{XuW!oXR`ORkKy5dL0$}nq&P|ivY5ehw=~5*CbSD5nrd1m7+|zz>aE} zvZ>t^7A4Fp9X5n`#UBfUoJx!1sbtd|&Q=+Gv{ZC{MCJ!KxZFlH4fazWdyRTdU$#Jt z?I+r~i;!i>5qrTy7+puqCpzmnY_YIaq6@Cs@^Wt%s_2u;V^n{3TJ!jDnF%+ZhPx|E zsi?BO^2Rq%>(jZ$N?>Zep32e(J5q%1A)WQNYSm4J1>I@(J>}|M?z2?6Po|Nkld_y!fKNW~x7Ijse@|W<+$PgP%t75BF2FLW~^PXq5p688( zCqne>e_W6k+GH#A)O@udG?m^n7P^b9*x@P0ZMoF80)>TRaUI<|%ElVkAvN|~t93-S z@Ug6>vW_%IHuBZ$bYEiuua=_M>R;<;b;u9^GPKA-0}i-e?0)%Y?Z(GZ-5e&^+Vkzt z8-0}uJ>|_CQ*0lp<5g`NQ@@e5vP?(Zla(#6B2t^_OR)7I(`&C0$3^SdD?l&${Ru1pwns7$uvF;(8(2zme*??w69Aw`NUfadG|jq5 zwMc_B0>sk=$n*a`wz+v3^Lg&{U&zaE@IraouUGwjY*Uhpdsb2f;>d6f0{|X=z0iN8 zG_=mn{=p$(_)yZOTRzgBf)JOv-l%P(=D>% zeQ@gXh|H3aW@vXJfr&5kB`Fz4jzcJJvxy1#$cGZHmo~xkbpWeF`DB(-f8r|kS|fRw zGJEFgC=N|BwgBJjI1=&(iNEM%H+&oVoS6oGSq6zTxT=QVde$>|yKb}Il%I`)3})B% zEZi*El&hjorGYnB3q7Hzt5c0TDx(Kt&xb!B_^k5AJ~yUJ-x<_!_mRt3(+6d3=dZpo zgDNlv6m4@wVA`CL$YD#;g{A591hJTQG0w@GQInT&#+Jvif1Ta%(PPWosI`47Y9ujP zEHg#-<15Rt9PmzuqxC1|`$7lt4N7`()^e=2XH;3=RGfa8nx|;e6yqDrv!Rh`Jvb{E zog0KGT=IQqj&6k^nN+li9dle4rXClpJk>BviEsS!!8OuJzTZ1V?rhOD*EkEnl%eg{ z!f{XZ_7no?k8=#_CftU=$$q%Q)mMp~V^(XG!=?DgOUIlJ3g?Gkt+>rR(GB~oJy(=v zlw*REw2prIj^-cZ zpCau8Rcp-I)wTrCZ{3TsZZLuiW_0r9p~ATGh<9^^lJmUvVe7#=*9XH`T6vr#h(F)T zxiOM=mc|GgIhVjF4%;U=fs$zGbVKIr?UPes3tN<_NIx&d9NMjuZ27?5K5 zYJRcw1N%NYqeo4833=7Io~e@|{^|&gkMj7@X8TxG*-C5@MbZWDqNuVkRNyRR!+`rQ zm_Dtj2zaKPT!OCk_Nf_n_h}TE)sOF{Q;8l%o$_^!BbT34Ah5b@G-^#7F&dl`Rh@m;IIW%^TbdeF>Go_~IY)fQXzglcZa_J(I~vx2~!JuxBBGRoP| z5D;}uow?0C_xhQ&FqEpwlv=l4tvGA@{7o=)Ju-vLMDy7M^3xLb&aSXLcy;sm*A3RB zpvk9u)k2Pmo8r@a<`W08zOIu1mW)MNH~L~G@G(&oo6Zf|{*j`IBtCLt`NzBWi?HHW z)U!0C!lZ+6iPzj1k%LvE^0iXxy0WqDC~(Ota$=uqvSM!@IuWbinuZVhWmg-%lrSmX|ni!AFF-JDh~VPAywYz5TR-RiT4 z%!oxY=LG|=vX{!{_(r|d*bIua!xDh)_6U+2{+Q?N(*qw#jt2*~@(w*Q1zDWTToDL= zKIFW96Sg!nd=lCaPK5Vflv>nKy*M<7bh=oB4G*Oou|z#rZcg2bu+$8q1p22ZANtnS zDr$GP_Z`OiCiyL*^GY$Z8m7#26vn%dU@VWrRAriSfuS`&-DJhhwVSi{Y~^V8U0Ah4 z@{e23@2BFaiAbb4${;lv^&i8o@+(ht51wIIoP1bc&NV6DN&d|rn7W0z5`=d<*(jhMDX*ZS^?oZ~94`Ck}sb|WD( zuPY7%TPTBb^a93uClB8R;|7S4N+xGfvF=XuRy%!dLY&&CmS*gmRz%fCEtcb59WQI*!!ov~`RuDe6LMOOTR8YC z(|HfPJK?Q!>Dvp+1mdBY@;PX<@XEwW>psR)Bd&QJc%~Ubu*mlF_6D@*W3iey)5w|( zMO6^}>{~fewft;=!ybz6c!GBN6jbAbnS&1-4sG@yza*$H>&y!EYY<6QxY@EF0sG!h z;MG3n`KmD%3EyIysADb9{-R}W!Jp;jq~@GJA~J5wq+Fd)srQLdS9FraiEiO}JC`j* zQGMqfEx+gKT=ag9T!D|Q6B?|)MJ-Yj4U_1C$6JnC%-%FqF@mZrp>EdNhUy)PMtC=+9^Z&wr$PLFzw#l~)0rzU+i2kA2@O3aq)?d^`mg znyX4WY~r{biW|JdbS^ndS10Q61rc>S;BB`cG$f5JzgSc)W;#*4!M;^#9R+h2W1(H@ z#_3e3TvGWN9W?w7?_rGT_L}g@z0j*sMj|}lL*kIVZA0uk<~PBvS@w7M^{uqReaN@V z+1CcHdzxK|T{2|wp-YuGIqtE8KWZ|nLZdE*`-+vM*)GN2bJK7se6U;uOf*D$QX4!k z^RpoDmlJV;XQ2*zo=0RrJ7eZ_%!%hai7X}BZ(`CA@uT%d4X;V;M2GT-q24!-jSdBT z!Q7@}e!X_lg<*E*_O5YrYVBd(t5O0=uzt3UFlXm%(YRXuVsQWN6YVirR`!WltoLH& zd#I^-ud>Al)lX>4vMH?&75Ar==TOqPvu~ zet3Vfq7c>!;>DYuqWQ*`>Z)WkV>~6L6_9?N^_F3EvKp!O`ilDQ3Pa*)uVX`(XW{PW zm#E-NAK9!%DgFAihLA@mq|c;qt#362-qMwz_EK`8B)r||Lv9m+_tU(PfmODy6s+2z zSY6G7gB*yzax>tyR3YUdv9@J?ea|yNRB8f7pme;o+vs=b&S7X(+>f%kn~M`@ZYOtf z85#?Se$wtz_Y0nGA|1Wa`*H0>;o#H^5ZMzll@V$fKi17+tv9&f+YvqL1-FW{lFhIf z@i!#Zis9K#>zS2)U4o!*TcZ4HYoAmWk88eA)JB_P@XZPT4dN#&)9>&4c7Aw$CC9^L zm@U+W{^kGbxS*c#ZI&-6h%39`0a?K11foBK7!dnN{Q`-9{HeRD=64X!ECYks;+pQQ zz6Alqnzz9LzlPO~Ezxq+UEGHtQKRWVlQsAQlJl>#p6D9B-eeHq#0b=e&m`IJEvLR@ z@-T5n)T$<=W2Dp}G8Gb_V)3U+_ny-_^18myGOm-Cj_LDlLxkn4C)R6tHQA>1W3mq< z@KyKUeqwSX{Q?4F{{{gkJlwNtB2blOb@dOFR5~L741pSh=s!@RawGG?1?PX|0=(|K z1NLf8(#{hJ{2GLWqoX@2{#x~HIOhKb`oz3>rbaM9~wgtm7Jsv#(iCbFGk%rK30TEnP| z(qFTn@JH@$!#`fqOB-huPD5@iSyHvv(F*SKgVWpfi%sgM*?I6l?2g$j2m~O z3Nr55*dN9f2fC;WI0m+t<|hDf^QkmYWx>JcCjl#?4<;)S=~M-e=XjfO-&9oMq^#Q# zbB?%Jie9cLmsV8U^+->M%qE9fXo2D2%8}N+sn;B92vqigjCV^&v=>2#8WmP?WN=}# zCqYiAZ}jOz=d*!EaL4sygJ?@pb0YQvNa9I^|1k~Z&oQ4=kMBUfsWAI@VNl%#^i8!q z$~~Q(>pwyD;P?t)1S~B;^rwfxR3B+xJp4y_@&9wdzNdSG1u8G-_Mq0wyEv_cp*ROL zI~?1n5LH&_Hs33s_2>kd{jxIndW&^ElSPNpUSqQ!_N`_oZTMa^vFf8n?c9E2mt%`1 zg2*O3v6}=`%WWpHaGyVrYG8srdqpR_G)W#3?zEWOxOpg_Woc~ z|7_uu&TmGvB)@PtM75zXg@U!Kulw4|e8nya^+Mea#I`$vQEt;9Io%#e* z49m|1Ehx>wx}enZ-C4=$jAu1(Mu>0x?BX}1!u&j6!+~)xk1(3^%Geico0Cr-*((2p z{o@Ih;$6d#2!BC>o-Io1PmP@dF^b+?z3H;Mc#!9~yI%o-O;0cMpI8gHX?}^dKaTR8 zs&UV1&VaGzI*$hcoaj`p#k`U-Q~1=q{`rwl0E?||bxb-?6cL%}gWDqOT~i-Lj`7@Q z1M$oUs!*M;*k6U?;thugGHBXF$Jr>Zrg>$SaH_*qjAq;lEqnT`@wnoPpf@cWiU~Ch zI_6@>fHG<&g8ij#xqX4G)2Dy02WPb3GTyR+>K6Ky?In$sdKq`ECDkmyX$zTU8ACb` zd==)C)`+ingu%QEEH{q${7WRG?Bn`6Iiy}Y8OHrwX+D-AW-gM{4{Y&>;+AdVv>_;;|^oF!5?gMPgB{AmgR0CB0y00#sB4G*F}gB{@X$n?Sm^?&7p z{rv0$2Ml)k!sVEZwf#CNsgX)SYc3WcPh_PjMPUVqWetg$Mnpwv?Ag9@G3YUuYmJ!p zOU-qY&#pwLdV`O(X`-*xjzSZ2|?>!wU!q-4j<|JGM`rt?Pk=(X?B~-V0IeH;7dKtw1h7%Hi1g!W# z^ry~X&yOr1oy&s$t#hpq&~kP_?%Vi?pani7naSF}Uf^BKmxioI6;-+E;df4 z{{3L^IyZR)>9Kn*a5P30((ryJ5JJlBL~FGSi8IfnSQ#~RpY+aUsCSM=5rH0`a&?fL zqq_!NLNI?a6rZ7Oh_)z@v0=ov$6LwaWPe<*TQA6Kb}R0eH1FY? zQo$dw0`DKKY%|U$@OAfW|7O{pp<+`~=H>e5tlzX7-`vp}Vj^u}klrqG?f!GoqJg!}rLexCl4c z`gR=5hHR+;2y9e6?4Dw&q2mwU5rqeyrCNe2?Qfy(F_ZOAvWM`ye*kQsjzl#^1dU=a zjTi&mz8{rV$AUBAV6~a3_71Y;1FGm+$E&ap0CyE|Q8#>axW%Vd>1CLTitAxyrz9T; z1KiW9{6wvnzeq@O@FO%=_NTKSknkIP7mB9Zil{Qk*$4(cSurYX?4c+Pt8Dt}Smdpb z8*nQ!hpLs?tf7V-gXJ0_d?;}>JTZ=Imvc)JYsmwVJZy_e8q{v-74aL?*- zfP0)%i*Nrds~|M!|A%LPF3x~Q)|X`k_;-la1K%kB6JoQRz!1|>Ba>Nhrw)BMWa{9a z*wQ65dH`Sd)Lb}%e=P{9eZZ@B2OW&}AYPE-=1e9GO;t!(U)wL%;~#p?J}F-OeO_(Q zOGShCmn1{OeAoktQw)8UOzuxnstLY9OJ%Dtkf!@7Fn<{u{>b(sBmEyTI{ub1su(CE zonpE*%OkeCR|6|?p6okJFr@C(daXJ3u9gH)`N4db^RKtZcpe`>WUK6=# zWMhBpTVwBv)GCm0-x!eo9~S;O(cI|)ct-_fOZbOye!tna0Rd}A{s#%0%k`3*s@5B2 zCp*=Oj_k6~qjJwVn_q}j+7DR+22hM*Q_KVN@*H{E?rt9zDVHhi;Yd(EQxsEZRy1Wq zh1RodyS{G_`2y`}Jg}z@lIkn|UyB;{7dL7B;U?hCne!wN=L0^H{9`iM-dBKBAl!lI zKZqLs5Dn7pDE$xJCV%Tj3EaA07_ZMKTqgN0iJ}EQ#s@OphfCub#!kqw)g$}+MWgz( z*yFsxxR`SJv~as`bc=ioWtMTP+IAm8vtLLHT-k-8YzlW&;cuqNIPYrd&1~d)0>ZVx zu8C};8hA_G1hZvP*h5#S#(@fZaDn}$?tBD)5taPkqPlSY7IhTphMP}{tF$zx$scz= zBQxsc-aurRh#Q-4`D|@adLF%Qox7j#wEfjic6PuwP*b&8HWD%uP%&-Hw`FY8cy(jf zjZC>*`*gwr$9z3X-3N|%X5)g#5;U>l`aG1F?NvaZTxA(K?28EObZS~`4gpEbh4t4| z%=h9VmH)cP1r*gaAQyqSNSuKGO>b9_-iIZB=ncHO{3ohel|Xu5`KEK3!}|~HPMflP z#fYW4mM}-Vp-{Pj){#mG{T6bVGsnAo(`aUo`$_#Nl%uW0+$AtHq-tMzla)vn9s7Gj zyOeJ0+bG-8CPMnVJq^_G3Sv9*38L7L-fK(T?pmCb^HQ(d`i4o>JYY5=7Mlo!{7X2Q+Gqja9;-i_3S9aarF@W58}`(XtFJiM z9`lR?qrdo7Vp`S+61CO%f57^mfK2ts|KcL$|GLQai#*EX#%~t|^FaT_MNhzLNdWrb z@(+c-{8o4fxb$v50bcWunXg>x`1Uk=U~u;@SMg!rmoO@QJgk5>v%!`#g{4TaW`j5*69DpXapBGMO!Y7rt( zAbuZ=2_G#u#4|h+9NGucTk#C$FFWL?ycdbv{*ZX>x5N`0pdB)|{p7=j<~82|&4fM4 zhdiCvUwx-gf6`MnR#Dku8?)idqIAf^5Gz&stSEv$=`->_P6_{OYJnG1EB)8hZpc7W zOMy&10pxNq2=Ny%2D1hUzB~DE!QGI53l6yn0^`s|{B%wBRFd$)44J6$xtpQkd=WyT zPP}KvAvqLO0fSAU^>h3BkAef#?5rQE33=+|`LAw*FS;rIFS_Xh8-eeC>lRiG{g<23oKW7?r-6y!!_S38z;8e&ECf(I1I}1)?3qWq0U*<%0d|o9sC-*CI?nATmw6 ze{@ot28un+Bs^%ml+fQUN3Ik*>3QmYw&6XWACv^v9+W1l!VH0 zIkpQq*@TFq;eA1B1*a?c`Ce^f$>Ab>I6SW?w0Z1LyN*vBx|XB;SBqFg1|U%v)&Ix; zD}O2sY{mnd9Wwum!oYvmAKm^{I1}k_AshWJ>KYv2d@U0LJcva z<4<}Noji7(ewJw)-#zsP&6=tySFN!9sGqas4FDJKM1#yu)b&9MJM}>QCHO*lfkgdL z{9jQ$fnNdv=QD8P+ym$Htp)QN*m{fEdF|YmwYf}=z7~!gW&4u=|F>nfHC<7zIQC%O z0e${!qI?ESBSlCt-X&NJ*B6pq)-0IOiqm*MuA96>5fqSZPWyRMRfY-2taIKYrhC-Y zM`w$oHYD2Z@@cV0NBQwb1~sH1fFw4^{%f=let}5(Pl!CLfc>bVe<4D0h54J_moIv= z{ib)-zj`CLg7h{D-$EFde=_u;PJQ}0N@3G;os5HMJ>fF+agQ|KP~Ae*XE#GwVNCwp zYo&)Q+Ybw(VP2!)f@YvND>}R==-pYypx6*@l8n)J-x8+_uu@e%@p_YQTz_0Bkr?Dppt(2u|=)ftXBd$ zLu(#wKi8@kNogAM&~9<;wQ0qvk6aUN*T5vRLNJAxI|^}{(K_=3YVweo<3ea zNF#K5H$Q7A6hap_O&jjhA!17o=g6yvh^*a{b*w8W0eO^Y)YIm{?#iHytX;V5co_Z~ z9wd)fVOz&h^9N3HtSv4XC3$sm)y+5019mVu*ha?S12PEGrWwlLq>r(ZIxTIlxoy?^ z!TStN28y48_>kjJp$2JZ4q)6dy7E88i;UT59DH)lkdd_Cg0v2R)T%-R{yN=}G@)1Z z?HxK}L(UUA9)A>XVUcH%Ftu0^N|fz`MV-4Dja&hy>!Ck?ciIDMaJ0vO$%j<_44fm+ zGfS{(zZq(p6BMvXno{58k9ZN8(LM_)t3e;bFn!*9-G;xh^PF6L<_8R^3s9y7hHO$3wJ!3Bbf2~X7Kn%UfS81gOd3X z{-U?*TYF>^%GFZ*Ow68Rh%k(vp07KjNlu){pEAK1`5AqFobeugvV&yn#_K1S(hH|N z(n@L`*Fd?RZISJyeZ8};Ecf#$VLj?XTsj>7D7>?@a(s;DGb{(U>9!QV#bv#1dyS2f zk3_Hh0*wzx-f_o#r4s@_dh zVR8s5iDK2){pzVNHq9?9xkE4`$4}>wNTo-#D?q849%ZCij(V!Sm}G%zTnQh76D8}O zAFg(~;FnFAlE;V0gRlISL&d2Z8Opyj?e6`cLS!>10GgIH0DeR>bt>GRgc}{M6~K_K8ryVHHMQq^udN zAoEU>It5{T{0&?4x)q9LBxd-Qud&9O=UYD}GxUq(w0+$oQ&w7*3z86H?3&YZ>9i7K zkZ|h6h<{7lA74U+#DBf0g#+kCZJ;iu9{oRQ+h6t-zy+usi2hK~3?><*;#SjdDz^Qr z;te4Hur4sDsmSC6tx{6`!7g6z^2^myWP#73bB0KLhUtT+_|;j_RGa5eWl(GY^{;hu z?8=zo$*Xr0bqIuf^?ZT#euM}!Ky74t003K{2q5}H4t>~HFLFryCdc!?a?}n20Lvd# zE>;(|4Ji}w-0r?NDP(azbhF3ET zPSA=$`0)dFY~v6Ud2?H_6RCUKv{%b9NeKQ@LE{M#BG&JCpZM)_}g5C5xoDbRy&E%InRG{Wbqesw@0q3Aq|D={OO zsHSBL`G>X9^b|Hi$k;O-6hvy&-)F+4b@V?9dz)Z$n$zK`$JQ)v+)C{i!97Z<@UAV| z+>+Qpge^&7*UQ};a_ZI+4R^^^Jv|bky11@(@Aax=`HdO_Ak*3?#GM7zqL_( zIR2G*a~u@CQUw_y9X6$vIT}j#Zp-_gRAk8`Y0a8bUkPe;$M)!^T28!WBE*wD_PwW_ zQ*Zhqgv5A&b*PmVK|%1UkUzK~0wl*f?EkS~{BuPC+)W1t50T$I2WWpEH=z9m{ZqK${!(A|_6hQ5jHUzaPR@KZ zH)ql7sfHah+C+%BgaJ8cX|oUeU!KlE!Yf(A0f3+1zR>T(f+0Ks9TXU|e!F1Ma`qxT za9GJ`;IKYh-jn$d=GjN&u7db(x{7e6`c}FOEf{{cO4iP;`xv_XhRkTz5lYvAi`idd z52H4z7hfyh|9X(|H=jXIb_@n)K|(5_GV}52qA7VUwc`-VlKMy3hF#6w;Jc1o)$hGy~hAemy96020HONh<-ra`P zS4|@78w7S^&TLng8z(m~)IJ~K$jukOr%&sHHUI3QrOrR(a62cyo+(|0Da5@aS*P`b z+YkB7i&z#8BnS5KUmNnWFB4qwzZ1N53OLjDf&Hy1ofrUM7RY5f@Sl?vIUqOr9fmlB z7pGqn{7swvf3-QJ1BIz%d4t7S7~ZJ5^qI@qn4SP8Q|Fr7)GuS0fl|~_xtyPPCb+jw zjS!XZKkeCR^fO&FFrk%@l$c%!JHY)qx~d6qy)&Posxlt=Rx~d!6oNRbD`p}%kdtr7VSG#b5W>zU^YNr0Q3_Rxu>$3}4W$nbjZaqgZ3ju&vEb(*ki_#ue{GS- zy&xsj~kXgN`-Rea)X@WGjUNf`Z9}-=AKde!cKJQx%=$FevZFXU4NEfJi%RWte4A!}r zw}e@DeT(N_QW3I>Vx(sT@f=7eQ+wSajzq^-h`c!Z9qDB@Sqc4*{l-6JL;@~G`Jh)$ z|0|;((Z4cIvw~zyJ=hWVyip5au1k_qVx=4PyzzEDcuINpjH5NoC6jc8d&G9oR_;&g zX|Nndt(RpbK3m=u)Ulj)_KEGFD_0ev;--^s)psDgQWcKtu@?#NTkmXL6MI7b&?(OV zLL)Z*uiX&&7c}_)M8l5=h(`LqXef!o0HlB}1ESyKJb?E};e`wJ|H8!+c(9)LQ3e?2 zu>>WW&am%CJCx;F92W3DR+Ez-se^+=%vpp107KIY{o$0FNYyW9{H=EI^8}jl00_1J zyHEgNo)v~UmBJmr@JaLzv50vTR+^+d61mtD0Et{i82|cd-y$cLTCy&2om)hjrLfb$ z16&yqG&0o&EC6_H;f4NCi}OkGMJ@W@)H+C{@T&rH{%;T;2g*oM1SzzdQt-Mt@Qtm@8iFGt6M;6v4wSc&)K!2(R7&FJjsj@rQ$7p z!n(>P5|kX2^bp~a1$_r5Y?gDr(d()Mu5r7QGEMibOa#m(_y>#G-UZ;{BSWc~$9U@v%Q}AcTR7j*x`#FKakVY=_ne{Xx=oB2)DFn+6<#cH`hl z2{`t8PLN+oRC#K943M7B0=ajr=PkaPJEx=5+S8yF)%8yIl|Du)TYp_#l8#FIKNi(vc0sHCVsrMLAZqS`%@pBLgl3N?hX#Hp8!*FHBFrNbs}wKo8mnoSE@g4$@IX}<1q3j3D^Td zovp+~#t&Ub{nIwY1?u5gebRbap^?0$TfN#?g(let>4@uGWD6kHn|s}v6iii%uy8H~ ziRN-OvHXlUQXIBnv@~4ioP)FPP(Fhb1_JJR#QTK^PEl0Qf-+-znlI&`Olwk9hSYev zq++P>UA<5nzWz+H>GpyuPnvtLK=yNw@3ERdLoGWU%PeCq-p-QpLb6VReLD-AX9lw} zT?EZp{sXK@T`er>BSttw%B1vq2C;)hFaw91SDh$*6alAdmhktt9JEk#gd@|c4G%LJ zPs|XlnHAWY$N(&5H~3~ezJ5sd<`o*9eEjQySnj)EQNz%y6!{ANvoKp8;+E*upCK+h z1tnVsyalGU?clDFBXp}GeANViD@p&b0g5`DsqyvPAhEI%x{TyF8q{<4B}{qS667J_ zrJK5X6;r0VIEQ)iH>|z-*bY6oILXB^<}0UWjPWkHr=im(sp_o)yq#6jdu=&l?YCcC zm2D)_d5#^w_!>GTVR07_s6k+a7krWcCtT-!#i}CFWy-^kVWT`)v6T;7(-Jhab0j_ z_69Y>EeXMbh(kZ+n=Hy{=;OvNh z2|Um?CWa9z1-$>e@YG3~5`|Q!h4q1tw?ZCv4qj=;ExMKcK0-~A%l2n&Yh_rx-5E|j zP4xs9Y1P#}e|VW5<)47)O!W3BBycL4e!RK8QDnkU|4`{pTP3iH~;Y-32L6 ztAqcNg01a=H>equR8D@Ol%EJNqnGS`+!)xV(st(%y z=ow45;?(||wU~*Pmg;;3E?0NR#qvSoT2iWLh#Yp$sZyLFq8jSCr!Bv|!sEBp$8w)3 zLURM|m|7QtzZ2$GKw=Zzz&m9znA0nYoWBbcm0OD~ee(cEA-sQq9a|q+syWF2DW1(a zN{~91^%nxQhd99->wQVq({xu|$Vm0hW&gBubaCUDV*3MkpE%497hGSKOwr$4V(L(k z4IM)QjN7(LV-W#YWR~=p=N$+wxu+UK!h^#+zt8|{KXX;YxC@F`_p@U`5fkwz0>BrT z5JB`uRtLC(MuL*e;|xj(!w@miigt2m{s1 zUxe5#qUHH(gIqQQ1@9YwjxQ@tFUE!9dz`i}6-x_pbQSDB50dpNdO@V0arn|i5NLn| z5(WzNf47%j4ZY---%d37!{T0ap8&JenevB!veeJpza|3JmqhT}mSFeEzloq7510r- z)JaK7qOQeAu?@3o^;M;Ul&UR5Nxi&Io6V)hW~CU|B~0m9-zn+_JMRW*(w#9E2KwL&ea4^4l#W1FvbqU$O?I&M+X)b@QazWG<&7kqo)bMLjIzy|38qTD3z!jABG!5QDS$SR5oU+^Q`SuQj&1kKs8J<2(5f@v-+`K?41fYE=Ga7ooHI^@o4okf_v0ceHAhS zcgOxSJ+{gsGpR7unhz@Fa@=4U`TO*`+K83_;v0dc(v=A9QWnmqFaG4JF)PH3CjR4N zKe}B|iY!uC@=&yXWsc&l5e!e^1h8%D3{z*#swvw%nDs0_1io6{%6#QvkjVt*cqtG3 z+}TbE5l1B$CrRL&UY@r+J**@Z&x!ysz#lyjYPOvFqa#Yy8?Sp)qkYP}4#tqYU*GzX z>s@Vc{W+R3S$EWRTim8<^$n|+Z>SV##=X*2`GkvZsmeH^38FAV4&dWYFoUS>5mn=C z7Ay}7wvykRUMKf7TO!CX{lGTu*3S}L%tg+bnRm#D%$!oN9_Z|^&>C+Hv=gF$JfjCQ z)+Hcv)h9t*GqzGw{H`?XS~y>SRi~YlSw{O4awd2q>pAAwzGlCMsc5*hKbm{_*@}fG zQ)CIThF49IFOb?ovH!gfKovuqa;5wj4AMdT2fdThgm@#)4D+{~Sd}%*^cg0eWJ2-g z(no>#l2&PcwiUzXU(j!&erd42-fnSYVQ03neE@rB|Dd^ve>$Ak0arM;l^y>1jy|q- zu!#TO@~En*-iCprggU1%0~B{JejxzzAOSD*dm?~Lcv5?bn8W`~1pSNuCITN-0N~lr z^nT?F1XOl#d%P3WK5yeqauz#FItux?-tDT^PXD;ocTQ~*pT{pBHK3Vc3O>e){X{X& z-hPOSw0y;nEQ&5F(aa>NlDRr?xyt-WmOadZ0QWNm?aHPa&5#jMXHUSx(k5?(eowg+ zawo1FhimO?tD1^Y!L4Rk0nMq6+mR$XIP;tLPREJ94qWx;e0JfHZPqBQz(;UrbadKA zD6tyG{F1yF=2I9mr~J+A_VJpgyR++w> zGK-U!SL-};oOP%282FK^NY}f9)b%uPpk0_ruFba7mzG%+sf{#&s}eLV1pb*^VV$?(h62sOhjry|~> zIn+#SE95n6NTasmEuupbb>vgz}qMc7#GR z7Ud=KGBdAB1RSk{#EO$Wo`Sa5yql-DHks;fE`vW&sG@~O5Q2r*GDh)lQtd8d;H?Sz zHS-yo%3TFDolsvtrQw{Xq946ADcrf;znt$MYd<#mJk(gNCZHA4dzA~T^fUNQd426U zy!qMmS$T)%7_B#FLFPh4z|aC=g@z7JywUSR;WJqtEbp#FOx(K&WXJ_jHdgP#Q3W#_MdZkH7u(zNt78=2Syb zTnBG|tS@&RBO0AZ8B3cAcawnWN{1|A;riKJH1Lb=V+j%jT}w3!KY zCH5bm=qeGE`4rsHg{4_xNsMusJ!%kK>%QsYfpfn5@X=@U&Q?JK&pP`<(wDhjsjZMK zi|=}+L>)GCRTabxdVv}UR%ZA<%hXX4tRDR=0T$(HP=QQnMhfq0$eFG=5ez4ly_;c? zW(rZ^Y`#*S5%vkdduT?$(xr)L)ew6nV=Zjq*x^R(4f{YvKpD*(Y-)N!0rHXxnGSBNQKP_i);RUh@M_)U&1#7A1PFYH_i z8gL)HB{y6{B{R-^1KQ`l+RJ>A$!2)KwEcWm=*r)y|B8|V>{4w6_B)fQMl6kCcPH_q zZT3Qgo|4fb5#)KAln{NM9!w`(8~0lhrO2LFwF9~s=)t8;8=vQxpeB=xaiwoXtW z9KK`^UarRx(T3z1P*0l50l|gXi6!*tgJ*YQ%(YK#CH_& zgHFXaY7Ha4?N1=FX#rx{69zhPj<&4z)MI_yTtZVcoT{kwr@mt*w?2TKY%8>{sn&a{ zBWsc4u)*Q>Q#*^dL$-@aZTor6;QN`I?Fr6)g`grP0RKfj%oh8q&!;e}~pL zks&eLexk<8WOlO}iAG0L5^lVUf`{2eTWw7REX?vS(SwK`^g_&#{ew)o5{_r!)&Uk%S|h#W_Aa zm3SL<>B@}SF)^LTQ`=Oh6fcoCHWKoQLnx}Cva<_rp5toLKk~mvB8#w&(6Z3NimY>q_bO>I1`N8xzN@I1gy%$M%K5u`;Vu43y z&*k$>ID*$wI7XZM5anP+#x)`sjbbXY{Pf115kuw3Bh}vEC{yW`M8`8ktmh zX5E0fb}VizyexXu#4`=9hXltxKl!WNR~}gJNGyCceqhIj zHdMs3;xoT4dfzboaDpi(6t`FH{L6?6MKi15MvsMZs$cGnrUKG`z^=AV|ghcjGs94Fr626G{#nFG_e^f6IQ*qQ4DxvN)`v( zOog7wwWd;y^I>P`YmbTaP|m09FKpj^ihxA@5JXNK*w&X7rKbb>&Zs2Y}%k+ zJSOLoDbQ{BRDcuvnQ2gPJA4G?6a+Qbs`F~MMP(1FfJ zU%G35B48zoQ15GUKVs|BzR9Emli`Re6*CS97gZoG0zgPW^v9+R>#LEM-bu@!`EY_2 z^m_vb=D&~TO#tt%Po{u4E&(};1DS#7cccMaPr#F=z=IHEzah=~@2bEX9eABqd<(UW z-Wd6s+gRdEZvze;?8yei84mt`9C!PDpN{I)=!>l1{*-kx1(>@NME3v5-GK;*0Bztb z1kvyEfyq5-f#j=1`mcPGDgVlcb_be?)QvV7_&N7m?y63uC&ywPwlW)3J~2`Nt^PYc0hai#kJ&bpam`5EUUUXqESk zBQnlqwvQ{XJGU4YVc}gyyH4L$Y@1oXDkxy4_@+?$QA!Hy7W4H3`Z@Vr6|R|0v#BK# zDVA@;*|<;^*^b(4sZR|w=e=(hO;@4xE>>v zIGe3B=LI2h=jNW{Tm(lUY8=PaJLaYmV5wBYQLUUV8SQkX#FSpJfuv#zhUf3hg*ffN zIayswXn)AjsF%9v_HRk^Y#gybD)B@Qa(`ATnvjJkGl#a1+iOLuuAAtyh=noV*pR`|4B>69j(}(|}*j5Tc5$%UGxB~R;>wP7eLb! zkX9MFwK6=YVq3E_XPhpawJu426{?;xCOpgO{FpaoW(3RK`x4{F0W(3E^%zZElbVpk zTqmtn7^nIW*lgLl@U;-Eb>BN_w1kxF^vDZZtq6H?M6+&)pNOcaoMyNZ`?jyq9VNlH zPuU83&Q?8J$o&aWytM67c}(W!MihWvSb=nBzLMX8X@<+j!aU|LB%%i_P|fY^Lz+If zJ=wdd&tbSHgd1*lOKYlR1Yv7NHSvl8xN)raW7_`KpeNu5CNZnD1qn?q-38y{crv@)$=b>jCCNS zs5IPJUNtLNGzM5oxo7qE&6rK}QPYkzpO+A2z*sZgbDbt(8J+JCwilvOamNd{`>Av8 z`P{!tiN|MH*N%(q@t%PVJD!?2+Y&}@$|BalN=psZm!}Q3FZxvrd>wGWE@207pSeV z)pDl;i$%$aL2+&IP6N)#Pt=c)F^b&`F!92$zf_OdhOBpNw&@=@K5=qYy$(Pva|U}_ z)t`YF-e>0wOS;Ov7BAws)!si51U!loBhSquwezRmPY`>6to`QVuQ77_1;5Ha@tb)9 z9z$yrlKv+~g25vJEfKVdQwM1;%?0+ZoL)M0k`4eH9oASS#nBtqzS?s z(y4M|$anbE`!3dN3@%7a4MvxJ^R#oW<6(ci^R0X8hEG&vSHaUrjT4_s%ll@*zzh0L z?Z;+RTrdBu6iF-Kb2>(Ba@0{?UW=B-tHU8t;mZ`ot`XHoZ8P(ndP2ftOs*oOM9p$Pb$8zDe)B()wP;E=OFNr%m~KlY?mB!ns;QaZ?hfC*WH=X zs2tyu)aYvM{LCq++hXM`@S`xe+2O?~exAY#CG7*#yJ_8B$W69!{N*Eoz)e2pKF zb;T6Iit}J0cr90+{st8E(u!a)iCo{k5f;ZLIK`x2pRQ6=_&ZNwDtQSUB}PZ2K!y2+ zw)G0Ar4;xqb%(enLb;Y7HF|pdwChj9;hH-|^R1sjZReqNfsm3AQ0=Pk&_Ac6Q=h~V zBfhJyXO6!9dLVbo_7UOx+D?KF4X;f`b}iwA?!FD3`M$OcPF5-0PEKum@|C6n{LOqU zjmeJl!I`7Frlu43W8-F;=2&8Q!TX)h%EyljtNVR6LOjBIn!N`aV?yg>+QVws0Ga^E z!`ERLaU~2hq#BS5zU(;ndXQxDy5Dph;f`Q*UjH;Gp`Lq z6!`YRL+-{R5YgbTo(%~rv|wflU~Z@~qYjRlJO-#yy-OkA`YdP{G*QY-%OR~loCPc$ zC#bZK4cMat|LVf=Z!sa*x+jEgOML1t5%)UjIdWWWXpS)ZIyy(9O$k}s$=@k;R!HOx z;eO+BFsVDhoImwJHM=|-JiEXY+B2-zkOhvbv4`Dx+|8uV*0m5OBl9RYXvV<(WE#+u zh6uow7=_I9O{k9!xRvLnJWoN#zCv81j<%1}-fRoyiB?Zc4PQoR z!W}Qkhq{nuM0S5ZJ#Qwe<3A;9_^VQi@{3$be?S0>I)za$iJclD8eP;16HL9xeZ`#6 za))HhO#=A^>SJ?H2Yyoeq}e{e$GQAf1#DlhEU6hx)}RTJF7x1Ad#u!C5??e7lCsb? z?7+d-OY`_*9L~)Vm!(w(88(r|pd)0b;p%iq1HleWE89#e3HvcjSwfFcNCk{Nt0M1A z_eh!#JruLrlKXREUm)?R+x%>mtXC#(S`prP8BrHfo(B_t(}Hc}-s)e-Eg&Ihg8w+P zwtNt3Pa9RoMld>`QTukm4Zp7Vdx6OV;sUD*pdQ+-yFtIo7ZLcxEy&Lqs2!A-t(6k3(`-)jO|FE zHRykT!shQDWrOS6zI!GG$8TIwG`F;~jo;Tf3F&_x!K35}&6{7tjMCj#50*Qu7b;_XjCqbHNVCA1z%whIlxUtU zu3=2rIAdV4!<=?{jJ=2%m zJ!*2)B;=^yPr?V|e%+JN#)QX&C+=((mj9Epvi#L_O^b8*LW(-oj zv`UYkH+x>@gKVDr*O`;tvQc$^y+%P3zt!k|_vP!?J`tUo_0?iQdiiRl8PwdJGIYZN6*G`ffQ#Lxh#lBH{f3R*>?1IpOk|# z;cYtwBDLqND;!f`r0{fm{97wVMS%2DZ9!r3|78KFVQ)joDV z-}DjpHb0@YOmLU`6=$}y)4Ps1<(s(IW@{;Z`|$B>+_=$@`pT^dUMFWvTFV;`={v++ zv)2RU5+2H*-vPh9U%UCrmUwf*+aFosVnrCtuzR-g#FuA(e&>37;3~<097l{=8}juE zwq(Db^gFO?hQBi3Y|>3^7gt`5<(yXi+!l|kW*$o}SsU=0#m+z2IZLKh=2mrbVd<@{ z4I_lav`7FB@>@CS@CVh4&q6g1IMV67bIfrWKcZVgJf22eZV;`n4XJdsy)B9_{x zB&lUW9}u#?2SyLMy-Yg--&i8Yy3QB zDL`=@xFN{QzDDVA-JPpF=~mg}^WgWcUaMR&zYnSrxi6<%x{oc1Q)N~L0G30&SKn!o z1J^D7RvTVmN(;CZn9bYKhDh!M^NstHH{a)o^7TRHZ4=asiWv_)3c7TIXo!%G1zPnu z)f7sI&(j8mB70o5NXGO_4_8$uyQM|E@H7D7Ib7DsyYPtX`+}2#5^o&K^JugTOL&f1 z5!uBw65*rG@;V;DdS&fnuWLVi>@q_43hMr0Wh|uX0n5zVrYFlaopUkDjbzoRpKcaJ z&ZCsB9}^pmJm`Kjh$HN#Y|H&9&H0XGZ%SB6sFH55#3ToDe_^2e|KDX7GWtu|<@;yZ zwE+n#yEuSLO%NBvmA{r<1}|mTZx;$SApb49(tAN=7Z=?&*!*F?%>Lavz|&1`tS!V% z5&jyi?n2I(cm7@pb9pVni1KUG_SYeft=IzN#e+$+rtz7R8tCn-9PTbHHkKHD{zXF; zt3mLx8|qRR^j5?k@_sg^%YLkl`?I4kz&o7lGXk;R(4w06VP{`n2kW-yc)J2}Ur8MN z(1_uB+l8}%vp}r5|Ef4#^;)##%xGxJAOA8(Yi9lGW6pEbn2c^`cg7ve!|nV@XB$ic zlH14j;X*HVmcW!?NBWU@S`9O_Y2b6Y)n=ov;hA|zME=`iMWmg9d35Od3CgR%%)&Ub z+w&a~NY3G6yfgZSUv{v%LKJ1Yj|%cUC9sYN@DKDOzJ@RAD25gFDhRq`NiOQK-&0z6 zft9sJ^@I;^u5qIO`tk%uSj0yvMjK3Uyr29XyzVwmT!5iugk#)2?WksNxeH#LM@sU< z3l2>6uCpF`Y&Y=9sU;g8W0i_AA9K=^SK?PG*|`WFnz#>*c)5D0Znm07DjMC_Gm-l< z)_v^}7*`$0;yHp3W4j~k98ppvG;QsnQ12WmhGA{r8qQryNqTal5IybvfJBhIEZmXQ z+&HOZtklFw&OsrwDc+>CPm?AYF;sHLEF`rXe1yz^`x?M7< zH%r(`WE$$DHrwpUT`7{sO}uZUj?tL@Pm0P6UnOFN zM0+s|_8R)m3TzHuIYn}GrwCXN>x|L3fgP$9r!pQiC%?B$9l8COB{kz!8Z1U5hxs1n zm$I>W7j*GrYQv9jqIZM{7oy$b1_omTx&mMpNZmTq0dKaA5s0lH)G2-;Q__UG;P4*(XX{XmDD2bh$Uknw_7pJBBsjqP5|lLlaKdg`8kWYi{AqKUwQ?xQJoM z8+@H2ZQtza**9=eRg=CHU|UCsCV;t?>Dn17birHzypICD$KTM=;8gvit*(Rg=e~jS zl#?4zZ3}uZ{rhwa`ZShrQY%|Ng9QLm-1#Jky1hs6H1T>Oi_v!p3lnk@(VtV{c`Uas z+e!_1=5opoezA(*tX&`C?U+E%e`J*tFH{@!E$kzqMm&PAl}d~gWL>AadP|B8?%WKc z*R%D6hNax7eTvkZYGaWchs&(=a5^qc)raa3_EkB+u-;r;yh#uIE8q4jVmLsdF6n7~ z&^b+nlW761$3?YR*1CI=Ea#sdiQTcz~E zyJG)>sSoTeL2*+Se%47GoRT(dZX9O*_u^yllo_nT=(fVwNG?_bOD0A1M+Nus;O7#U zE2O}^em8Lq2Ic&P6@kgXX>GXABHz3Z7uJY%p?nq<3l~;~1fGRDpN$?ZajvLHpTcDf ziEoNbh0{gVt~VzeYy(3LcGd%iJ4CK;Bca1&K6Aq-g!g)Xu zvwj2kQX8=B0?{93m&=pkOWC#h-?Hlm@UkCpp$aU!Jn@0AmWVC~&}X0SqLG*_W`-Fn z2DG1Nq)1a&%Y8WIYOed*1^*%0>gfT=-9TEoTtXUt@5^vOhf#Z2$pmh%b!#Kmh;!mH zjt}5nXb*WDR-{MRH>@vT$x#}6HLn~up-I-rwk*Y-(ka1(@Ey4B2a?WdkF>B*#-BdV zjU3Nbs^t4|sC4RL4tD#Tdc2G91*OV?rRq5r7ri~ED_^L^T~9)nWwE^2e2-Pz?t#P!}< z>pO}yH~mA_rEFTP9eF5JQUOH_Xg#bS^sl$i72J2sMv0CEL&}>sH%l&WGt35K+`(*< zg{B2_?YtstKAE1zH_VJ+*pv<53qP#w#&7@Vht;nQ%$OFmkef^kgsLrR6rSvWP5IKI9zw{im($cdiztVYGGVMsOPO%;L1yRRWRp z6xyoa#h|?(1-c*r&-ZEwbl)eqIwS{YV}xTy(=tU43rn zf5}G9^;14PWSu{#jTfE8@;C5dzKR$6VSmh}jwd_flM(+`FbkHk}^Pb z$b_61V@mJp63c)j-1@~-?D~GoKOPow%G>}k{BEwNBYx*ZmNug=XCu^j8|Q~JFf6$x@^7OygaOx{O_P`5Sl?C()WE)?5}8eml-wI67fQf+kg zZ_QH#j~~!@#tj1Di48__BBYFSV8ifI(o<^uM@AT-DC(K>&pj&k^TG4>>5^UN6YD5u zNzL3hS75M=(U)(SJI(3vei{rEU6?o(i(?A?f@HTt)3}GyxZ>JdjJLY$GC5TSBc7k; zD5E|2To(~QS8hq)t1NJw<3-5?sgDpZ3d5@XIh(SO+qCs~lj&A^UPm73-5^C4?mdwK zf37^N@F8T`67Op-L#EJA7E&fU-xn0wdU{!Xf} zn|5@r;3^L+yF_972>bwcvQ(p{SM5?C#hi5G?Fu|&*uUjoOMGUOhE)b^y&lU3A` zfkQyM?%|XZTh$__v~XBkX_oAcMI_iH*$n&ct`|8JPA@dgvaWkSK~@>lNv`lQ zUc7+)gb+D5Yie9{GX~1jNnl`-nkf#&*oJ%!U#q}jUOyDl^@bfvw;*;NSsSZUJ2e9cU2W(%Pk?dw?GUuU%Y`8=83(kf}f znn~=QyxY}MiCjeb{LoZc2WSl-S@_@KMcq7)RFL(0+QiCrzJc_WlRK6z#mlOT!@BP~ zhr>Dz`W_ZvBOF3Fm}2iKoe>0qaz&mtned)@pYoDRv@J-;TxAt2Znau?kP<^cE*M7` zAGgJ-@3XBImvZQY$R?I*Ns+q37;yKIEMMgR>+7xKs(haJ;X`+KN=SDrDM&~+0@B^x zNOy-)(w&lmbT`s1ty0pR2cC2A=j-{1@2`K%zq51AUNbxQ-n+9=tUuMmi1c#&vPbje z`S+RZixrX3d8v20N;wE$5Me9mUn^F{mlFMQC9g#Cv96qz>Ec*9;Tls6oFatt1=#-0QhF z{V>6k8p_j|o;%r~HF)RqSo3DDZ~LD}NPW>wmiK5cQB|QtwkzmvM@(L)shefH4JcyG zdA*C<-ZOaGr$B|#y7<9UlvfmfIQ0#C6Hj>dQ>0d;m* zgtUGhV>N)fhGzb&FWh8C!80+<7HWbY4X;(P;4MfDaf8ZS=HpHB8DD>O6K%JJw|FOX z(!00&g86;hxl}8IRopKAXL59uW%95dj@$!LCQdo@ZGC8mY7)$sWQ|xw;ffNo_2WW% zP83~m+$}>T!oFI>B!z}fb$dE)%?UGuEe@;ZECjbX!>Uq0Y*YEZ`<&V6vA=DYWIyRz z4;GbU7!BiVcesSzIt{T35M-Te+Rz zB}mY#0wV9{oE?S7r0?6(kG0W$tdfV~ZO3mMFjx%#T+->H0Jc?r+fa{8u&)WJVqGdIV zAwayj1fJF`(6s(zImLNcP9MEx9{~D~eE@EabV&E+*K(S6i~>jimC0avOb)0JpxHyI z{OE-HLCbG)jBSCI(*Rv)+^nD;z6v8xqk^kV1~tZbcJ9U`BjjW^Y|cilqx8fd+pCO= z?9XXMNk8k~;L(|hrJHN|$ZTJe`!(nc0O4gAwx}RYjnhKTHlK1Wh0E%bJp}vU$dgv}m(*>d*#2)rEF3xy-Y#YZWZ;ck9Ko+ZCz~HF z^$s2GP3$asKKFT+YQ9X#G-r>0s3fqx#dm1euo~%mr0%dC^dbF|PvHrIb*rObdjq61 zQuscLyrS_5y;{^x7Ay`kil#%o8=2?W9-y9GXpXC$#)2mWbC96vmDYW-O>Z#O`=t=~ z5}UrQ#ng`x%$hEp$)pas=lw9va9lV#`81X$;F%? zJw(Xd-=IW{7D=7g1@SxF9QvZ-&oL(V6@&{ZM(E8sSnEqEbP3yP>iKl^qv@U6 zn)p|tb>3U}7Fcz)8f))cj2l_?uOe)^*5uI%>?_{RfBNqEk!wh~=%rwim*`fm8=`JQ z^;XS|unUzOiEknLCA89e!(>ciC^YEVDadKcxco>{(>YtvLzL zCi3&7BlX1)Rpssx22Q1@*jrLKHiUU}P1f_QQ3K7whQ_UM2^3Wc;|>RipYN%M4S=ajoR-|1-g#u_ri+$%dug61`|#_WS5NMcz1nfIQDUMZA_ zY!FnumCvfDcgR`VIrdWIh0lr|ozbTOW;JLpUe-6EGtEYbdQ}Jb$V=$qQqxq1D$jyK zU0e+CuTX#c5bF4USI#HIpisxB2HoHWYf10>S7%kUOt6-PAOEQZ{~lj|5PVL_vtT8&O(m>1OED`S9(CqZ@^IxjZUU`>9kl^=}z^ES*s-pfG0d$8F1 z(G=bMw)h=6_`Qs!h27W$ZfLhwG=g`7H#l-QeC8f3U%}c(%Kq87!{WiKk6La#c=ZOv z&U-4PUtV?9h5JVv6%2i_jq}rg+8BuS+s61?aElTfouLeUcQ4Xnv8^4BBtBya*o=2(C57FZO)(%if;B8|Vi zV0k$ZK8=rOecPdQ$Kb`CkUA^}M*2eVpRKDcACS`i&7VMf5Yn*UNH-1P0ZpJ<3@neA zT!@cAE3lS|od47UY8ayjnvHh9p!JHbrcTZoTGZNU4MAOYG>_nvuA=Qd(cBBq%MCRB zL*|6a7hVI>P3z0tHHOq(mg#I*rOnVP*_WRbs=}S$;s^wZDfXjh$cKihDloZ5t6^yu zT3-k+#s?YD8?fYXEVd=1Viw!jQ?kj5JEQp8T>;Fy8qF}LWv}k2;(#10)BPd-@OtTI z>H$m6Udn@`6}BF1f@y3de%Tt}-EdoWXDCDc7E+jS5zCT-B{o{3%Hl48(Du$KrD zFPKdIuqgB>I-h^W`Q+uo7et}K7z=A1@k2&w%*KyJ?Xi<+;%IT-*6mTW6TfbJPMA9P zRF(K*B#)*7CtBhg7Q!HIu=To9V1LI`eS{814z=LURvm{wchQ_W<3NK`6CGYtHbA&( zVm0h?ti@nAyqVIlmZ z%@s!318E1j73 zn|ta`VSj##_&QO=tA-X#@Q@3Hi2whPkGrs`0K0NJqsXj_cF2FPT(6;f*ji_%)&hmjMcvkkkVNNP~mwg0=) z#C-pN67VQAGe9Uuz?CLN&#wT#AVK-ZFaMOg+fKNTzf!iTf+ zOe~0FVSNzzVBoUei8FU6p1{J7%H*kX|Xn@d1D_DJ+h(ti_lGnS4{}}4n`cwUKn0?OSKku6!2^n z4Vh>W*D;_b$mtmRSoY16$;UYKM*@pYLdOde-%>xcj=J?DN0BrqA0V!LmA>TZ~C za6UFMj^rOMN;b_)hXH{bqNao z;Hz9=q$GA}CVQJiS0zIMtHbOB&l#*hiD=}{>UAEd(^IO$SEX*VSkd%2VG&-fU;kGmY=w=qG80f zCX{&+H@VjHOM#Z}+t88Pv0KPOEye8Fu%HM8h@mB_)odcQlrot$Bzh$5O#hn361iFI znFz&K4li#jV|l8^2fqVb<2I$t-O7-TRq;|HY^m+Ssm*dJ{1uQxu+Z&bjEI3-~+Ls#0(oNPrmfEUK3kuuMwDiRUFFGXATkSO zTRGe=4}7tFe8|<$Tbr5lQLb;uJEs!T%|F0@f;&&mmEq$SSybbl*-u>&f2~~A^x9Ug z&NI$LW`ERY)B+3%oAIu$*-AX)bSWlR742EnL<)BGj@6&oF;#|*Q+*Pxs$QM)Qp6(p zPb8rh@z;6HSoaHg5cj?L@#-j>y=3iOnE~tfk3=rd?}hNwRzR5q4wdYUB%OzIUg ztz^;bUbTdya0f`+9H$XT(8WO>yHEL?OfwiI>Ud46>nCqcZQv^IjI`^?cQ$Z_u*Llu;NA)WEv~>6+=4b=A!_Deb(eoDe+U%hLI^hCgHwjw-#u{^GG!EqH~`j#f~8P!?HY7_yixXq*u^23yd{)r?*v6&iV_)-_R4~OYO4~_l=WZoFyiy7p}bA zSi32{3Tqd(Wims#k(BWyHX|k&by+>6fT|N>pN^w(U-+>&bZmdx=Wv=g-t8M=p0A`; znFJ99e;V$4b+nNpGWQc3K9zpU4Re41pAIYHWv;M9%Is+3(s3F3oSR`$&nsLT6gLW| z7L0h>b>XG4Y%YJCBKAN?y+L$zdS!m(48+kuk!mNHP5ipD@^v#v1tkhDG757|Hw9Gg zmG29{@>B9~(eS(+S9@dcNk3aT8?iTiKjkBxd?y&}P5PnUe30I|4EVO6B>bu> zc+pk7?T~IxtZEz3H;5cSF081o%P(3~ErCspztzoPfFuS}$I~oS-HHEmq`_HM23n|8 zhc4;!{mke`Q^bhcz7}dYnyU4T?Ts_n5dz!%Gsc6u0%GU}0fyCfatH&h^CbvM1?k(9 zJQlWQE5?>2lE@s*6CxVR)tOkn^>eiiX>i6oYX4`(ba=>^Tz_WFo3q~;GlUNSu+=?; z6LK;DvJ_qv6v{;)e;u1-t)=v#FS&sysH1ma8Ncl@w!_RLiHS#Ged4AITP1b?&9dQy zD6!UT6TSc;ny^t*NC74BU8%=uVC__rnM0C;3~K}T@CIVrhIYi50S9yNJ#jl;5?|=q zhbsmxSwqE=cQ4ZR+%&lPR7MdPr9uF8RjgO{gVN_lsS7VSnlxZ8^T= zQIQpXdPzdOb6MTgc`TYSjb9_XA*)`0q_wom&SA2kEJ>k@$cWd9s>Cnn9SnpH4z3KnDwa>llI#I)c_B#T~x^4WSkL=J6sfxi^CBNwGF{{Zn>{N-(F9HvwL?;a(~t2z zY+zaBOqV`8VD6YJ>80J2c4g>YBPVrH67@QH{zZU$9Bi`>#sH9#W+FrsP4XGkwCj9p zidVVh_4`gr)kEFS*{YL^wjxy&Ep6zkQ8({a940RdEgID>AYwQ;Fuo_bcX9=}>2uSw z2h9uXY!ioRba=>fop_P+3KHK52YCFXIP5?r`QdNzrD26W)NBoF#eHmI-iUd?!5%8x z=v8TqIvs;5ifzueQ5x0|{4yazID-LOl+ZW9J$;yZlP482+e-7ha~erPy^;VT0-sp% z;Shw4&EVQd?c|i&r5{uImgP390XkZ{@LbPMbEZxTTYwDRQ6*(j`95cSp}Fb|r=;H! zNEeZREc9g?ek*Q?c$*?rpE@))u0tT%lH~!Jp=WH5u>^Cb$XjyE5@8+4s4rVvrWQlN zC(ufVdFxdB>^6nNB{G`{<{O2EObyBknb-I3e%0X3#GCOGZ4}y6z*I~gjVh8xP7ST@ z3omP5&zRs+UL<@vc0L}o36(pDQp$?%MNikBj4X1?iEup!fa4b|atn%hbsZvxPiFeAdWFBsc4QdeLohiXekL>m)+j!a9GG+9$MwGXzE zHOqXyoU&0EXW3!P7`%?9ivO6RWTjJZbiuq&yNN)w!MGGMxI7E7E*&~ZvNDy~9b=_J z1S5x7gbz7+6-kT2JcJ2|aT%~0O?mh613i+7D8Bjl3vY<0+Zj(hO_Ilu9TgQVSS_5z zG;w%xA$zoEP^oxI@RrumifvQ_^Tp2+zE#$i?V)h11S%Q&{D6v_tHJGhqoOH6f%^i#hwtlKkPDkm-N~panGoZ?t)5CMApq=K&~3o8`=-oy)mKsJANeX zg!IRgE20;rxC7`Miqa)|Ch0Wr4Fa5=XfflCUFU@P8pj(Bue7jGUJRzHf1a?SfaU8P z&_c=H?q|UWJL5s8bi|4WXH43Te^#oM;IE0h*i!O@yKG+}EB&r7Q%k!(N?@z=c)nS<7uMaqDdnZ!^W z$f4L4)J^IX1-2k$+k3T zZ?4(h-dIa~7r&4e>eeXnMuu-xk|X1nWSk!P&KVNcN?7)4B3PrYYPckcvf%aQ3xnlq zi{v+lAKH~|GfSVjQ+poBTf^c&;w-cp3+jJxGg0z9Ro{+o*%ZgVv?-o`TV}l2n-z;L z=2gP-9>~`EeAhJ=63+kjLk>Q{*lW>{CD!3|k|&}L)Jo<|LnH1zPoxL7!m+rY@<4~S zM1vaboGUJ=>RdtEbhnvwo}q^pBY%t6Kl*^$vFUw2a6tK8JNKZt!inw!SA=AKLc{+1 z=7r!1EGgvPixRYECf*RL=SjnPMJcyR*@G`nkAoblxVRUGR2uKfN^m#{MvxLP_aNc= z*LGSzS?HU&bz@%&e$;##?ptF_JxF+J(Ar$jSVUxRI33()OKuWd;qz$|a`a%*A~rNr zeg}2VXktvNgx@(t`lZ%t9Diy2MRgkqGigVdAI1bJ-5~9Y7{BWBk#a7nv@IigAMH^w z*9^f_w@*T)t)Qbw>^xI>g)%!q(?Y0eF(+;2BSZ~B-{lN#lC`EgBFHfCl9mIxriNdJE^XWCJwS%I%__LLYJYk+JuE%5b`>fbCF-AEM+xC-l|m$jn_N9Kb);XyH8^43UnLG$G%lU$8<) z%H6KmhGwbwkaf{#0d)u&-)S>WUdA|>@ZJ_R+M(r(`lK!ug+#Aru2`X???x!n}fcNR@ez zbIxf)B4Mb-4Ixjq@gbgmvb>3>d)VmyZG0B{))6P$J2c%vVO|ZNBCoU*QcJUX%Xn-& zT7VQ}8X$orLdGrJ%FsgR&oXhHILhX67P=mgHY<&A&lU6|pAybId2Q3ut#-R8Jk zGRh8RljN1p3ZvJqi4+5qsKardXY0!a1=yXEYe0Qzd}!t#DkbDe!oq-nGM+)r>~KXfSCNQY5@^Tk=Zqah zWgt~cMToZ>R_Jg;?BIFi_oo-02T)x0C33}!XYt{Hj1=prj`N4%ijC#jPeZD?Xwe)`7LvMt=H{9>>ECf?~S zo~Z+Q&1VwYpXVHfuVSy_vr>i7%_!bgZZ`C{J@2mX3Q1f>rBlBaJu@(J`LwrMAmV7U z9@=;#pXTWvGi2Grc;T+AAM}MT54Y+K%7j}85v3Hp_#4t%6{Y~5)6KQAd|qGus3iUI zswT>>{EGJ1oY=GnkWg*g2EP0q6qtgrbV$Tz*eZ)s*sIE?v%dWSJ*|>?P&dUzo!MePXa{XqCfhZ(_z20S~6@M>uDzX)%R3;mDHPi&e8Esq}C_SDva8U57tnwQc^pU3>_BI59DV%rjTMk#zvIXYbmFHy5K%gqa%27j znR?lKh}=%voY*A^axaBOMJ~r`m@D{q+@Z95>ggM5m|WV&3f5QB)^B=<6AU2S@(k5F z=B!RQ3tr~eY)nIu7(0Xy9yHBN3p2dmo80>ZfNVldKcpw&0)!e(L6Ny|&u3*!)&>7q zV~`ru!{~o;(203`%3UCmy0^%mcB!(9r?rr`bN`dp_VnpkhbY5|P0>>khMDw=K;?jd zYV4&a5)s{4=QS*;7;#=+6CV{{I|zyFYG!a7h1g`gxDH?SR1##VzT1&lAld1`mvmQB zLAw8jVMoJd+WegVNFt!j!Eci==*r-!gOMHcoh^5^eN^v}nbMxH8KsMiJ72!8TbC!scc_`B(GnDzu80t}tC0soT7H4H_ZH zO*yaBQYJ{`HE;AK@|04-3EFL$v$!4zeT`-xj<4-JB8-)(Rx_rk8=IHo+i30We0Sj@ z`hHK(Z6PV{JvN2P=nI=*p~PM(d-KF#V<)Fo04J8PKfsM&$+{i_E~A_^%A(NKW4RK{ z+z4&dx6p?VtI{lPOcN6m7pgNxrWkV0J~G`>Y_1;S?~Iv_Qrd5p&g` zx*U*gkeg95Hj5c5j$uLWG}@6II8 zZZru6PaI-xco;WVK)5viutQC$zsg?oixva(=;VW$0x|IzG>P)K>X~SMwZ1|unt7Wm zI{1#pDc)6~E^ca^sd{}h9=%S8Vi}RLcU!2h8HE7;+(g)~iLICf?^k-K!{-QPqN3Cy ztl=;hqOqfL+?|oX#x-K_LHivGRzHyLacczf7+|F9v@`P`y5JZE`55PiFi%#PK7J)qimA`$%K8 z$LUMvyF}de)-;t9vE;epdA#Ris*Y4R>345vdZEdX6kymbVESyd@P#{8&K4aCaOg-= zb?fI%^tioe*#x%7+^Sq-W;54RGL&KcJ(U-tA_P7O@V5VK@>p-Lj|Mff+qZFXbeo*f zi|z8ttpN;W*PC8$lGp?xs*3Z3D>nEZ7y*1ysYQtw2j<~ABE5>kf*1`E!O4#CJ2apG zv?hb)aXx`e2D&_~%OC$-my!8@=M!SkQ#S4Xb~B($#fCKp?~el8;hB zt(l3b3fHT*d81%+Z`x{I52)gjzVR`tckI3^s2=Y}X?cMXrQe#>w`#m*l7c8cuN1i$ zIh~!#mv>FVG2A^MMdxvSn4&Oos9TYiW)s(g3oIK0fvl~C;2tdD}cXmpt zn-Sp?j}!T*sd7)f_HKVzdht3W6)s_FE;l}1!MMu6qF>Eci{JcQ&FWfs`b;DD(0!P~!p9=QFzU2|narXenPl`QJkbauVt1 zu}SkE?|L;pap$hwXG1zD>8#G*j#BCgB55~&f7(k**MmizqbV4h5BKS66e$Xp6w=`+ z@5K$=_{T|k#``TA<;p_`RgMs`5n@*Iw&O27(FBV4Z)R2f0x#kVt*b^;edZO}%TN{zT2y$y96|~ukDQE# z%Iu25<|Glm8i{JI&Bb+0ibAkE9dlJQ!V-r7BN?(ksBMtpl<4|E@`~)gKmD{zo_eR8p%{y%di>;vu@yg$aDt1Vhh-gffe^atLlvex?9pcir2 zCbjm(*!ptp@P-U6(aUJIdriLD7@~(>LfXB@3Ti ze^0y`jrUUz>#||nBdPYJ%%a+6M^4f|#Jt9(ouxTmx5(wmIuGl%V^v&;<`%X@A%<{F zG=I#|;L^vaV_s8y$~ zo(m(rK+*%oc@T{`;kFcy2GZqxLbbiyl0tT9n}a6k5Blz0`3jp(!?$T=hDGLM!x_fHtZDsh%{e~G=vz;osg2mVhv!aA1Esae*4$My8(%b!4}57P zU6W6KmA^HS|K_@a&|7nJtGS~pBPKyt;Zn@2R*xYy6q#n~Jpwj_m~-UXEq-t%@4mQ4 zhWJ~)X17DhozpA^{FZPcU5^Tfj3cZ}n#OM@FLFYA1-}bNy)7hdki-H7@Te&BDbCpE`_`PlBP$YtscY-v3zjrRC0~Z@BIG(jjR)ai%%6>t<@bg|g|uf3n%G9Iu>P>V zgxEnMoGsPo2Qu>>QCRnvY_&0`&WqHX_6(VL!oF@Bvm1jJggvXHWZ5c;Q1}|Md~}1j zsaD6hsz(rg!a<)ra3}h_@3lzDr``8BXv_)F_B227pp*|jHGW09y-umL+SKzttayH9dxmAtP>@Nddra=B!cMfb@7#2ZT=ne4r`}hBIob>N>I{Xm4 zeSZdTkO1KcRv&oThGP1aPUH090I47VVEL=-XB76vgBHpE)B-wfMg;lgu@^ zN5~Y|Anu8_lFefR9J}Tj@MrAr7He2^R>{ql?+Q+8#_JSJwqb3k|3De$9ucUPcH}Oi z=9fbCI6s9o$#hX-Q|@+rLDehDY6w%Fr5ICo*ZUOqx*WoW?K#WQPZu!YerE&#s6>8{ zzf5(*0iBC^0XseU$kdz%z#vm^U;%(Fg$g^H>>!(EbcQt(SfL;>D|&SS^Hl#E9u&d? zdx6F^wXM2l)3KU+RT#g})(U}wofap%pBKXs;XkU5*Yt^YQ52T1i#|*=`VOTpm;0Mr3Q0tOA_yB;{vy*$pw|PL zZvM<`|4$44oq7)lTRRauMKg~K9Ym`@w7-hS3E8(fyx}xNm-Mp~u$f*+NdI!);)Cs8=HC zsdzHag)$nHoV|kAczO_&kq)BJ(7tAn{{MXo#`ihXzwc%;m-O zN2Z@_XHf3@M$Co`cNxKBPs525^Tl)3HRfL#WHi#WzrQOA2)OW{%$4!Id6xEFLID{! zFwc-lK6G8B9BkFs_GV*Ozl<|$ zLHyVAmi`ZpzyD7y_&|Ci&^~B8=A?+SJrbb4$1wyQaJ=B14Yuy2?_VdE2}!}A$6X$Q z9s{|~1mwDY2uc9pD@aT5^}k$4^spk0*8Qgz{QG`g(6y0n43c1T$XESYcl9Jo(ec${ zV6_uKXn+-nXkht^Z92e!2hd+1fi?lTZVkjX0{;&n(Ad)*4JscWTFmN&(%dt!^eWho^FH%EJFbv0W57W0 z*r0Pv-|#uXuuueG$Usg>`#m<*SJ2o2MPE9LHpyR!Pxgs*cLV^@lAhB=#{z;62qz&9 z+@uV)b#f(K3&V20Tx>S=U8ME*5@lTUpI~P=>CJy29pcwKzIhS46S86$hWU|zf-9)E z0Ef}qEiF}3W9cq9rzCcq&J{o6eUpOqv=B1&I_+A9#>aDOxDwUm)loYS$fDubh$jFd zVI&dESF>s6o}y<(sLBW9FQtupFrzQx;subt#^>G^>q(Qb-Jl%V#PSkP8t)Y=;}r}~ z^$w0V$YBOo1PHFgFfEPP36_=&nz1{&6&;q~Cc89f3|pqw%jzC9Q+ey6=`g)fqtm85 zhd#i~*Sn!Pk{3{={ni~4tlg+}=U#dlx=r7{SyvxXcDv}}5s7J=;NVwY0PQ^QgHWYp zMHxvg_3AWSu+T29eCz8wJvg4>!*HLc2-6L(x0yPNSd95+9j_L)r_lYCzpmE_7NB-7 zu{M!NF~qA{TNz+`=dOpBv-kUnCl+zE)=5t2&ON(_QD`ZeMhcgVHZ>`Pn5O#X zWxzt=O%mmN*ugaJHh@yLJ=D2)x0<`m*A1hhn@WMmk#bCOABl+(cYOr&j2uT3Xrg}R zV4Ytkm`j#xPUR7zZxP|vm=0VP8U0V^-ilstxT%8@?omQX1HcDX$b7sGpF2?q*t47H~|-em%vA11w|&uXcg zbK|X5mGTyW^jU@JIsS8b>)Z6tbe-FM!_fVTowTA0PqLk5zjbpK6PhjfF~5Qz(CbvZ zDG}RxyQyM37WHw#p%0kE5x_sh*%%LHPxmG;G z+C?o^W*CZbmb(&}V`YOcc3+eT3c{GjebrHA!O@P|{69(huacS=lwM!Jj$%GaU~7aY z_@ba%ZtRZXR~md7j`Xh&gP@0e_2?TFU-Wm*C>96h3|=U9__|OXHt5m?s@+NrmRoU- z&*Byzya3m;*pjMbBrSLMr2?V0Bo0!Y`{B%5L$i19FJPV*&jgE~ z`Fj|pbON=h7wo$`^xCO z&-PiN#aQwkSa!f?#a4N!eOxeXDD8TZg@`{JpizI&O|~C3a5e31${@rcuvnW+RIaQ* zA4{=+GIP)`QVlxj9v8=}l2?-r?w=>1SI1jm7UPJaS-{Y{r!x{iZr^?K)L8`2C-o|D zMc{bk5XO1)J;s#$PR{jaZ`m?c&wr=C3b{Rudm8kHTxg90>y?Fq4GYkA>mtI3sbm zQoB;&mn?iJj3Rq5ygMt>GN7odU6?l*(v6)Ui{*gz$k*rM08Wo#BVnA@3c0z)%+)NzV`?ctNsJh;&MKU@gDdNz5T@ymsY z`7r-_up|7z_OnMW1ls1q%=YSqqlGOlAW{ zKA3#>r^!U1YdF75K0kUe83AN6xi8qLAkF`|6Z>~oe)nJ$#D5rt2{Nkl+VYoCPKE!o z+wixR4`3}{uK!aD21tv=FD?DP|MQsp-yb7UU}#makDy_KpnU^ literal 0 HcmV?d00001 From a63d7307c856dc6af15381e5cb7bf378e2450e97 Mon Sep 17 00:00:00 2001 From: Josh Liburdi Date: Sun, 15 Feb 2015 23:13:40 -0800 Subject: [PATCH 151/299] FreeRDP test trace showing SSL encryption -- RDP analyzer does not currently handle this and SSL analyzer does not identify it either --- .../btest/Traces/rdp/nla_win7_win2k8r2.pcap | Bin 0 -> 134982 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 testing/btest/Traces/rdp/nla_win7_win2k8r2.pcap diff --git a/testing/btest/Traces/rdp/nla_win7_win2k8r2.pcap b/testing/btest/Traces/rdp/nla_win7_win2k8r2.pcap new file mode 100644 index 0000000000000000000000000000000000000000..3a6a2999eba4fa36f1655a95464494db44c8bede GIT binary patch literal 134982 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 e_S;kcZG8CMef3`TrFsiUL3u2o@ Date: Mon, 16 Feb 2015 12:11:59 -0800 Subject: [PATCH 152/299] Creating the installation directory for plugins at install time. --- src/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 13c6e45006..f13a4a6ebf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -408,6 +408,9 @@ add_dependencies(bro bif_loader_plugins) # Install *.bif.bro. install(DIRECTORY ${CMAKE_BINARY_DIR}/scripts/base/bif DESTINATION ${BRO_SCRIPT_INSTALL_PATH}/base) +# Create plugin directory at install time. +install(DIRECTORY DESTINATION ${BRO_PLUGIN_INSTALL_PATH}) + # Make clean removes the bif directory. set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_BINARY_DIR}/scripts/base/bif) From ff16f6215ad6f3cf8208b4e35756b75410a38403 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Mon, 16 Feb 2015 12:49:54 -0800 Subject: [PATCH 153/299] Removing setting installation plugin path from bro-path-dev.sh . Also, adding to existing BRO_PLUGIN_PATH rather than replacing. Addresses #1312 --- CMakeLists.txt | 4 ++-- aux/bro-aux | 2 +- cmake | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c0ff6c09d4..04ac197f74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,12 +31,12 @@ configure_file(bro-path-dev.in ${CMAKE_CURRENT_BINARY_DIR}/bro-path-dev) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/bro-path-dev.sh "export BROPATH=`${CMAKE_CURRENT_BINARY_DIR}/bro-path-dev`\n" - "export BRO_PLUGIN_PATH=\"${CMAKE_CURRENT_BINARY_DIR}/src:${BRO_PLUGIN_INSTALL_PATH}\"\n" + "export BRO_PLUGIN_PATH=\"${CMAKE_CURRENT_BINARY_DIR}/src\":${BRO_PLUGIN_PATH}\n" "export PATH=\"${CMAKE_CURRENT_BINARY_DIR}/src\":$PATH\n") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/bro-path-dev.csh "setenv BROPATH `${CMAKE_CURRENT_BINARY_DIR}/bro-path-dev`\n" - "setenv BRO_PLUGIN_PATH \"${CMAKE_CURRENT_BINARY_DIR}/src:${BRO_PLUGIN_INSTALL_PATH}\"\n" + "setenv BRO_PLUGIN_PATH \"${CMAKE_CURRENT_BINARY_DIR}/src\":${BRO_PLUGIN_PATH}\n" "setenv PATH \"${CMAKE_CURRENT_BINARY_DIR}/src\":$PATH\n") file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" VERSION LIMIT_COUNT 1) diff --git a/aux/bro-aux b/aux/bro-aux index 3714d3594c..c011f3a724 160000 --- a/aux/bro-aux +++ b/aux/bro-aux @@ -1 +1 @@ -Subproject commit 3714d3594ce0d2b8a757c04e6e7d901d6b559915 +Subproject commit c011f3a7243a8a1dc8be7eff2f45799be7ee85f4 diff --git a/cmake b/cmake index 1316c07f70..0147a2e05b 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 1316c07f7059647b6c4a496ea36e4b83bb5d8f0f +Subproject commit 0147a2e05b613a044ac30374874acdb8bc216feb From d36422fde156caf7d4f2cddf3883d1812f3fb7e7 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Mon, 16 Feb 2015 13:37:59 -0800 Subject: [PATCH 154/299] Explicitly removing some old scripts on install. Some scripts have moved into plugins, but may cause confusion if they stick around from old installations. Explicitl removing them on install. We had this problem before in other cases, and it should be ok to help people upgrading a bit here, even though hardcoding these isn't great. --- src/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f13a4a6ebf..6d24172b97 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -414,3 +414,12 @@ install(DIRECTORY DESTINATION ${BRO_PLUGIN_INSTALL_PATH}) # Make clean removes the bif directory. set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_BINARY_DIR}/scripts/base/bif) +# Remove some stale files and scripts that previous Bro versions put in +# place, yet make confuse us now. This makes upgrading easier. +install(CODE " + file(REMOVE_RECURSE + ${BRO_SCRIPT_INSTALL_PATH}/base/frameworks/logging/writers/dataseries.bro + ${BRO_SCRIPT_INSTALL_PATH}/base/frameworks/logging/writers/elasticsearch.bro + ${BRO_SCRIPT_INSTALL_PATH}/policy/tuning/logs-to-elasticsearch.bro + ) +") From ab3cdf494a216cd20d528f982af3834ff4623695 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Mon, 16 Feb 2015 13:40:21 -0800 Subject: [PATCH 155/299] Updating submodules. --- aux/bro-aux | 2 +- cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aux/bro-aux b/aux/bro-aux index c011f3a724..63675de3cc 160000 --- a/aux/bro-aux +++ b/aux/bro-aux @@ -1 +1 @@ -Subproject commit c011f3a7243a8a1dc8be7eff2f45799be7ee85f4 +Subproject commit 63675de3cc7bc3eb2a3645860224c372d3f7f36a diff --git a/cmake b/cmake index 0147a2e05b..9623367210 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 0147a2e05b613a044ac30374874acdb8bc216feb +Subproject commit 962336721040fdf55a6b264f8bbc84153b54d9a5 From 0f96d0625273f748793988f67756b4317c1e074e Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Mon, 16 Feb 2015 14:24:32 -0800 Subject: [PATCH 156/299] Making plugin names case-insensitive for some internal comparisions. Makes the plugin system a bit more tolerant against spelling inconsistencies that would be hard to catch otherwise. --- src/plugin/Manager.cc | 22 ++++++++++++---------- src/util.cc | 7 +++++++ src/util.h | 3 +++ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/plugin/Manager.cc b/src/plugin/Manager.cc index ab0b85676b..b891a0faab 100644 --- a/src/plugin/Manager.cc +++ b/src/plugin/Manager.cc @@ -79,18 +79,19 @@ void Manager::SearchDynamicPlugins(const std::string& dir) std::string name; std::getline(in, name); strstrip(name); + string lower_name = strtolower(name); if ( name.empty() ) reporter->FatalError("empty plugin magic file %s", magic.c_str()); - if ( dynamic_plugins.find(name) != dynamic_plugins.end() ) + if ( dynamic_plugins.find(lower_name) != dynamic_plugins.end() ) { DBG_LOG(DBG_PLUGINS, "Found already known plugin %s in %s, ignoring", name.c_str(), dir.c_str()); return; } // Record it, so that we can later activate it. - dynamic_plugins.insert(std::make_pair(name, dir)); + dynamic_plugins.insert(std::make_pair(lower_name, dir)); DBG_LOG(DBG_PLUGINS, "Found plugin %s in %s", name.c_str(), dir.c_str()); return; @@ -135,7 +136,7 @@ void Manager::SearchDynamicPlugins(const std::string& dir) bool Manager::ActivateDynamicPluginInternal(const std::string& name, bool ok_if_not_found) { - dynamic_plugin_map::iterator m = dynamic_plugins.find(name); + dynamic_plugin_map::iterator m = dynamic_plugins.find(strtolower(name)); if ( m == dynamic_plugins.end() ) { @@ -230,7 +231,7 @@ bool Manager::ActivateDynamicPluginInternal(const std::string& name, bool ok_if_ // Make sure the name the plugin reports is consistent with // what we expect from its magic file. - if ( string(current_plugin->Name()) != name ) + if ( strtolower(current_plugin->Name()) != strtolower(name) ) reporter->FatalError("inconsistent plugin name: %s vs %s", current_plugin->Name().c_str(), name.c_str()); @@ -297,7 +298,7 @@ void Manager::UpdateInputFiles() static bool plugin_cmp(const Plugin* a, const Plugin* b) { - return a->Name() < b->Name(); + return strtolower(a->Name()) < strtolower(b->Name()); } void Manager::RegisterPlugin(Plugin *plugin) @@ -318,10 +319,11 @@ void Manager::RegisterBifFile(const char* plugin, bif_init_func c) { bif_init_func_map* bifs = BifFilesInternal(); - bif_init_func_map::iterator i = bifs->find(plugin); + std::string lower_plugin = strtolower(plugin); + bif_init_func_map::iterator i = bifs->find(lower_plugin); if ( i == bifs->end() ) - i = bifs->insert(std::make_pair(std::string(plugin), new bif_init_func_list())).first; + i = bifs->insert(std::make_pair(lower_plugin, new bif_init_func_list())).first; i->second->push_back(c); } @@ -348,7 +350,7 @@ void Manager::InitBifs() for ( plugin_list::iterator i = Manager::ActivePluginsInternal()->begin(); i != Manager::ActivePluginsInternal()->end(); i++ ) { - bif_init_func_map::const_iterator b = bifs->find((*i)->Name()); + bif_init_func_map::const_iterator b = bifs->find(strtolower((*i)->Name())); if ( b != bifs->end() ) { @@ -397,7 +399,7 @@ Manager::inactive_plugin_list Manager::InactivePlugins() const for ( plugin_list::const_iterator j = all->begin(); j != all->end(); j++ ) { - if ( (*i).first == (*j)->Name() ) + if ( (*i).first == strtolower((*j)->Name()) ) { found = true; break; @@ -434,7 +436,7 @@ Manager::bif_init_func_map* Manager::BifFilesInternal() static bool hook_cmp(std::pair a, std::pair b) { if ( a.first == b.first ) - return a.second->Name() < a.second->Name(); + return strtolower(a.second->Name()) < strtolower(a.second->Name()); // Reverse sort. return a.first > b.first; diff --git a/src/util.cc b/src/util.cc index 60a92af45f..ac2a942ed3 100644 --- a/src/util.cc +++ b/src/util.cc @@ -541,6 +541,13 @@ bool is_printable(const char* s, int len) return true; } +std::string strtolower(const std::string& s) + { + std::string t = s; + std::transform(t.begin(), t.end(), t.begin(), ::tolower); + return t; + } + const char* fmt_bytes(const char* data, int len) { static char buf[1024]; diff --git a/src/util.h b/src/util.h index 50c33d5608..f65e0fb7d0 100644 --- a/src/util.h +++ b/src/util.h @@ -159,6 +159,9 @@ int strstr_n(const int big_len, const unsigned char* big, extern int fputs(int len, const char* s, FILE* fp); extern bool is_printable(const char* s, int len); +// Return a lower-cased version of the string. +extern std::string strtolower(const std::string& s); + extern const char* fmt_bytes(const char* data, int len); // Note: returns a pointer into a shared buffer. From b6bbf90643e272814174b6eb065fdbab60e1d938 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Mon, 16 Feb 2015 14:32:08 -0800 Subject: [PATCH 157/299] Updating plugin tests. The init-plugin scripts now expects a destination directory. Normally that would be a new subdirectory, but for the tests to keep working we can also put it right into the current directory. --- testing/btest/Baseline/plugins.api-version-mismatch/output | 2 +- testing/btest/plugins/api-version-mismatch.sh | 2 +- testing/btest/plugins/bifs-and-scripts-install.sh | 4 ++-- testing/btest/plugins/bifs-and-scripts.sh | 2 +- testing/btest/plugins/file.bro | 2 +- testing/btest/plugins/hooks.bro | 2 +- testing/btest/plugins/init-plugin.bro | 2 +- testing/btest/plugins/pktdumper.bro | 2 +- testing/btest/plugins/pktsrc.bro | 2 +- testing/btest/plugins/protocol.bro | 2 +- testing/btest/plugins/reader.bro | 2 +- testing/btest/plugins/writer.bro | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/testing/btest/Baseline/plugins.api-version-mismatch/output b/testing/btest/Baseline/plugins.api-version-mismatch/output index 1e4dae5e65..04f3cdd3a2 100644 --- a/testing/btest/Baseline/plugins.api-version-mismatch/output +++ b/testing/btest/Baseline/plugins.api-version-mismatch/output @@ -1 +1 @@ -fatal error in /home/robin/bro/master/scripts/base/init-bare.bro, line 1: plugin's API version does not match Bro (expected 2, got 42 in /home/robin/bro/master/testing/btest/.tmp/plugins.api-version-mismatch//lib/XXX) +fatal error in /home/robin/bro/plugins/scripts/base/init-bare.bro, line 1: plugin's API version does not match Bro (expected 2, got 42 in /home/robin/bro/plugins/testing/btest/.tmp/plugins.api-version-mismatch/build//lib/XXX) diff --git a/testing/btest/plugins/api-version-mismatch.sh b/testing/btest/plugins/api-version-mismatch.sh index cfb4269946..2483582359 100644 --- a/testing/btest/plugins/api-version-mismatch.sh +++ b/testing/btest/plugins/api-version-mismatch.sh @@ -1,4 +1,4 @@ -# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin Demo Foo +# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin -u . Demo Foo # @TEST-EXEC: bash %INPUT # @TEST-EXEC: ./configure --bro-dist=${DIST} && make # @TEST-EXEC-FAIL: BRO_PLUGIN_PATH=`pwd` bro -NN Demo::Foo >tmp 2>&1 diff --git a/testing/btest/plugins/bifs-and-scripts-install.sh b/testing/btest/plugins/bifs-and-scripts-install.sh index 627eb0f2c5..60c754f8ff 100644 --- a/testing/btest/plugins/bifs-and-scripts-install.sh +++ b/testing/btest/plugins/bifs-and-scripts-install.sh @@ -1,10 +1,10 @@ -# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin Demo Foo +# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin -u . Demo Foo # @TEST-EXEC: bash %INPUT # @TEST-EXEC: ./configure --bro-dist=${DIST} --install-root=`pwd`/test-install # @TEST-EXEC: make # @TEST-EXEC: make install # @TEST-EXEC: BRO_PLUGIN_PATH=`pwd`/test-install bro -NN Demo::Foo >>output -# @TEST-EXEC: BRO_PLUGIN_PATH=`pwd` bro demo/foo -r $TRACES/empty.trace >>output +# @TEST-EXEC: BRO_PLUGIN_PATH=`pwd`/test-install bro demo/foo -r $TRACES/empty.trace >>output # @TEST-EXEC: TEST_DIFF_CANONIFIER= btest-diff output mkdir -p scripts/demo/foo/base/ diff --git a/testing/btest/plugins/bifs-and-scripts.sh b/testing/btest/plugins/bifs-and-scripts.sh index cf49642766..25f2dbeb5e 100644 --- a/testing/btest/plugins/bifs-and-scripts.sh +++ b/testing/btest/plugins/bifs-and-scripts.sh @@ -1,4 +1,4 @@ -# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin Demo Foo +# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin -u . Demo Foo # @TEST-EXEC: bash %INPUT # @TEST-EXEC: ./configure --bro-dist=${DIST} && make # @TEST-EXEC: BRO_PLUGIN_PATH=`pwd` bro -NN Demo::Foo >>output diff --git a/testing/btest/plugins/file.bro b/testing/btest/plugins/file.bro index 7d25cab538..29724aa8a4 100644 --- a/testing/btest/plugins/file.bro +++ b/testing/btest/plugins/file.bro @@ -1,4 +1,4 @@ -# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin Demo Foo +# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin -u . Demo Foo # @TEST-EXEC: cp -r %DIR/file-plugin/* . # @TEST-EXEC: ./configure --bro-dist=${DIST} && make # @TEST-EXEC: BRO_PLUGIN_PATH=`pwd` bro -NN Demo::Foo >>output diff --git a/testing/btest/plugins/hooks.bro b/testing/btest/plugins/hooks.bro index 786e6ccc88..c1dec2f4c3 100644 --- a/testing/btest/plugins/hooks.bro +++ b/testing/btest/plugins/hooks.bro @@ -1,4 +1,4 @@ -# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin Demo Hooks +# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin -u . Demo Hooks # @TEST-EXEC: cp -r %DIR/hooks-plugin/* . # @TEST-EXEC: ./configure --bro-dist=${DIST} && make # @TEST-EXEC: BRO_PLUGIN_PATH=`pwd` bro -r $TRACES/http/get.trace %INPUT 2>&1 | $SCRIPTS/diff-remove-abspath | sort | uniq >output diff --git a/testing/btest/plugins/init-plugin.bro b/testing/btest/plugins/init-plugin.bro index 2fffa88f2c..a4ebf7b00c 100644 --- a/testing/btest/plugins/init-plugin.bro +++ b/testing/btest/plugins/init-plugin.bro @@ -1,4 +1,4 @@ -# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin Demo Foo +# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin -u . Demo Foo # @TEST-EXEC: ./configure --bro-dist=${DIST} && make # @TEST-EXEC: BRO_PLUGIN_PATH=`pwd` bro -NN Demo::Foo >>output # @TEST-EXEC: echo === >>output diff --git a/testing/btest/plugins/pktdumper.bro b/testing/btest/plugins/pktdumper.bro index 29b69acadd..d9bd91a5a6 100644 --- a/testing/btest/plugins/pktdumper.bro +++ b/testing/btest/plugins/pktdumper.bro @@ -1,4 +1,4 @@ -# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin Demo Foo +# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin -u . Demo Foo # @TEST-EXEC: cp -r %DIR/pktdumper-plugin/* . # @TEST-EXEC: ./configure --bro-dist=${DIST} && make # @TEST-EXEC: BRO_PLUGIN_PATH=`pwd` bro -NN Demo::Foo >>output diff --git a/testing/btest/plugins/pktsrc.bro b/testing/btest/plugins/pktsrc.bro index 349e361664..a13596e245 100644 --- a/testing/btest/plugins/pktsrc.bro +++ b/testing/btest/plugins/pktsrc.bro @@ -1,4 +1,4 @@ -# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin Demo Foo +# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin -u . Demo Foo # @TEST-EXEC: cp -r %DIR/pktsrc-plugin/* . # @TEST-EXEC: ./configure --bro-dist=${DIST} && make # @TEST-EXEC: BRO_PLUGIN_PATH=`pwd` bro -NN Demo::Foo >>output diff --git a/testing/btest/plugins/protocol.bro b/testing/btest/plugins/protocol.bro index 671edb6cf1..8a6c2a6399 100644 --- a/testing/btest/plugins/protocol.bro +++ b/testing/btest/plugins/protocol.bro @@ -1,4 +1,4 @@ -# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin Demo Foo +# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin -u . Demo Foo # @TEST-EXEC: cp -r %DIR/protocol-plugin/* . # @TEST-EXEC: ./configure --bro-dist=${DIST} && make # @TEST-EXEC: BRO_PLUGIN_PATH=`pwd` bro -NN Demo::Foo >>output diff --git a/testing/btest/plugins/reader.bro b/testing/btest/plugins/reader.bro index 5065678c2e..ec9b6cf046 100644 --- a/testing/btest/plugins/reader.bro +++ b/testing/btest/plugins/reader.bro @@ -1,4 +1,4 @@ -# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin Demo Foo +# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin -u . Demo Foo # @TEST-EXEC: cp -r %DIR/reader-plugin/* . # @TEST-EXEC: ./configure --bro-dist=${DIST} && make # @TEST-EXEC: BRO_PLUGIN_PATH=`pwd` bro -NN Demo::Foo >>output diff --git a/testing/btest/plugins/writer.bro b/testing/btest/plugins/writer.bro index f2e74ad667..8cecff6843 100644 --- a/testing/btest/plugins/writer.bro +++ b/testing/btest/plugins/writer.bro @@ -1,4 +1,4 @@ -# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin Demo Foo +# @TEST-EXEC: ${DIST}/aux/bro-aux/plugin-support/init-plugin -u . Demo Foo # @TEST-EXEC: cp -r %DIR/writer-plugin/* . # @TEST-EXEC: ./configure --bro-dist=${DIST} && make # @TEST-EXEC: BRO_PLUGIN_PATH=`pwd` bro -NN Demo::Foo >>output From bdb2707a08a5772d9de7371dc1050f0d5db4bb56 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Mon, 16 Feb 2015 20:05:30 -0800 Subject: [PATCH 158/299] Updating submodules. --- aux/bro-aux | 2 +- aux/plugins | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aux/bro-aux b/aux/bro-aux index 63675de3cc..8c37b26823 160000 --- a/aux/bro-aux +++ b/aux/bro-aux @@ -1 +1 @@ -Subproject commit 63675de3cc7bc3eb2a3645860224c372d3f7f36a +Subproject commit 8c37b26823ada9c77614b2f8f781c11c8fe3d078 diff --git a/aux/plugins b/aux/plugins index ad600b5bdc..9072e28935 160000 --- a/aux/plugins +++ b/aux/plugins @@ -1 +1 @@ -Subproject commit ad600b5bdcd56a2723e323c0f2c8e1708956ca4f +Subproject commit 9072e28935ba85b6938fe946d7aa23cb58ee1566 From b06d82ccedeb7e079a76c2b6f207df681f05a96a Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 17 Feb 2015 10:50:57 -0600 Subject: [PATCH 159/299] broker integration: add API documentation (broxygen/doxygen) Also changed asynchronous data store query code a bit; trying to make memory management and handling of corner cases a bit clearer (former maybe could still be better, but I need to lookup queries by memory address to associate response cookies to them, and so wrapping pointers kind of just gets in the way). --- scripts/base/frameworks/comm/main.bro | 39 ++- src/Trigger.h | 2 + src/comm/Data.h | 67 ++++++ src/comm/Manager.cc | 28 ++- src/comm/Manager.h | 212 ++++++++++++++++- src/comm/Store.cc | 5 - src/comm/Store.h | 27 +++ src/comm/comm.bif | 106 +++++++++ src/comm/data.bif | 331 +++++++++++++++++++++++++- src/comm/messaging.bif | 102 +++++++- src/comm/store.bif | 222 +++++++++++++++-- src/logging/Manager.h | 18 ++ 12 files changed, 1114 insertions(+), 45 deletions(-) diff --git a/scripts/base/frameworks/comm/main.bro b/scripts/base/frameworks/comm/main.bro index 66dc1715f4..da910f20bf 100644 --- a/scripts/base/frameworks/comm/main.bro +++ b/scripts/base/frameworks/comm/main.bro @@ -1,32 +1,53 @@ +##! Various data structure definitions for use with Bro's communication system. module Comm; export { + ## A name used to identify this endpoint to peers. + ## .. bro:see:: Comm::connect Comm::listen const endpoint_name = "" &redef; + ## Change communication behavior. type EndpointFlags: record { + ## Whether to restrict message topics that can be published to peers. auto_publish: bool &default = T; + ## Whether to restrict what message topics or data store identifiers + ## the local endpoint advertises to peers (e.g. subscribing to + ## events or making a master data store available). auto_advertise: bool &default = T; }; + ## Fine-grained tuning of communication behavior for a particular message. type SendFlags: record { + ## Send the message to the local endpoint. self: bool &default = F; + ## Send the message to peer endpoints that advertise interest in + ## the topic associated with the message. peers: bool &default = T; + ## Send the message to peer endpoints even if they don't advertise + ## interest in the topic associated with the message. unsolicited: bool &default = F; }; + ## Opaque communication data. type Data: record { d: opaque of Comm::Data &optional; }; + ## Opaque communication data. type DataVector: vector of Comm::Data; + ## Opaque event communication data. type EventArgs: record { - name: string &optional; # nil for invalid event/args. + ## The name of the event. Not set if invalid event or arguments. + name: string &optional; + ## The arguments to the event. args: DataVector; }; + ## Opaque communication data used as a convenient way to wrap key-value + ## pairs that comprise table entries. type Comm::TableItem : record { key: Comm::Data; val: Comm::Data; @@ -37,30 +58,44 @@ module Store; export { + ## Whether a data store query could be completed or not. type QueryStatus: enum { SUCCESS, FAILURE, }; + ## A expiry time for a key-value pair inserted in to a data store. type ExpiryTime: record { + ## Absolute point in time at which to expire the entry. absolute: time &optional; + ## A point in time relative to the last modification time at which + ## to expire the entry. New modifications will delay the expiration. since_last_modification: interval &optional; }; + ## The result of a data store query. type QueryResult: record { + ## Whether the query completed or not. status: Store::QueryStatus; + ## The result of the query. Certain queries may use a particular + ## data type (e.g. querying store size always returns a count, but + ## a lookup may return various data types). result: Comm::Data; }; + ## Options to tune the SQLite storage backend. type SQLiteOptions: record { + ## File system path of the database. path: string &default = "store.sqlite"; }; + ## Options to tune the RocksDB storage backend. type RocksDBOptions: record { + ## File system path of the database. path: string &default = "store.rocksdb"; - use_merge_operator: bool &default = F; }; + ## Options to tune the particular storage backends. type BackendOptions: record { sqlite: SQLiteOptions &default = SQLiteOptions(); rocksdb: RocksDBOptions &default = RocksDBOptions(); diff --git a/src/Trigger.h b/src/Trigger.h index 7662901dc5..3af9ddf1b0 100644 --- a/src/Trigger.h +++ b/src/Trigger.h @@ -55,6 +55,8 @@ public: // may not immediately delete it as other references may still exist. void Disable(); + bool Disabled() const { return disabled; } + virtual void Describe(ODesc* d) const { d->Add(""); } // Overidden from Notifier. We queue the trigger and evaluate it diff --git a/src/comm/Data.h b/src/comm/Data.h index ed3c16f677..ef7b15110d 100644 --- a/src/comm/Data.h +++ b/src/comm/Data.h @@ -15,18 +15,53 @@ extern OpaqueType* opaque_of_table_iterator; extern OpaqueType* opaque_of_vector_iterator; extern OpaqueType* opaque_of_record_iterator; +/** + * Convert a broker port protocol to a bro port protocol. + */ TransportProto to_bro_port_proto(broker::port::protocol tp); +/** + * Create a Comm::Data value from a Bro value. + * @param v the Bro value to convert to a Broker data value. + * @return a Comm::Data value, where the optional field is set if the conversion + * was possible, else it is unset. + */ RecordVal* make_data_val(Val* v); +/** + * Create a Comm::Data value from a Broker data value. + * @param d the Broker value to wrap in an opaque type. + * @return a Comm::Data value that wraps the Broker value. + */ RecordVal* make_data_val(broker::data d); +/** + * Get the type of Broker data that Comm::Data wraps. + * @param v a Comm::Data value. + * @param frame used to get location info upon error. + * @return a Comm::DataType value. + */ EnumVal* get_data_type(RecordVal* v, Frame* frame); +/** + * Convert a Bro value to a Broker data value. + * @param v a Bro value. + * @return a Broker data value if the Bro value could be converted to one. + */ broker::util::optional val_to_data(Val* v); +/** + * Convert a Broker data value to a Bro value. + * @param d a Broker data value. + * @param type the expected type of the value to return. + * @return a pointer to a new Bro value or a nullptr if the conversion was not + * possible. + */ Val* data_to_val(broker::data d, BroType* type); +/** + * A Bro value which wraps a Broker data value. + */ class DataVal : public OpaqueVal { public: @@ -51,6 +86,9 @@ protected: {} }; +/** + * Visitor for retrieving type names a Broker data value. + */ struct type_name_getter { using result_type = const char*; @@ -100,8 +138,25 @@ struct type_name_getter { { return "record"; } }; +/** + * Retrieve Broker data value associated with a Comm::Data Bro value. + * @param v a Comm::Data value. + * @param f used to get location information on error. + * @return a reference to the wrapped Broker data value. A runtime interpreter + * exception is thrown if the the optional opaque value of \a v is not set. + */ broker::data& opaque_field_to_data(RecordVal* v, Frame* f); +/** + * Retrieve variant data from a Broker data value. + * @tparam T a type that the variant may contain. + * @param d a Broker data value to get variant data out of. + * @param tag a Bro tag which corresponds to T (just used for error reporting). + * @param f used to get location information on error. + * @return a refrence to the requested type in the variant Broker data. + * A runtime interpret exception is thrown if trying to access a type which + * is not currently stored in the Broker data. + */ template T& require_data_type(broker::data& d, TypeTag tag, Frame* f) { @@ -116,12 +171,24 @@ T& require_data_type(broker::data& d, TypeTag tag, Frame* f) return *ptr; } +/** + * @see require_data_type() and opaque_field_to_data(). + */ template inline T& require_data_type(RecordVal* v, TypeTag tag, Frame* f) { return require_data_type(opaque_field_to_data(v, f), tag, f); } +/** + * Convert a Comm::Data Bro value to a Bro value of a given type. + * @tparam a type that a Broker data variant may contain. + * @param v a Comm::Data value. + * @param tag a Bro type to convert to. + * @param f used to get location information on error. + * A runtime interpret exception is thrown if trying to access a type which + * is not currently stored in the Broker data. + */ template inline Val* refine(RecordVal* v, TypeTag tag, Frame* f) { diff --git a/src/comm/Manager.cc b/src/comm/Manager.cc index 92b2c167dd..65a7bddbf6 100644 --- a/src/comm/Manager.cc +++ b/src/comm/Manager.cc @@ -41,7 +41,7 @@ static int require_field(RecordType* rt, const char* name) return rval; } -static int GetEndpointFlags(Val* broker_endpoint_flags) +static int endpoint_flags_to_int(Val* broker_endpoint_flags) { int rval = 0; auto r = broker_endpoint_flags->AsRecordVal(); @@ -111,7 +111,7 @@ bool comm::Manager::Enable(Val* broker_endpoint_flags) name = fmt("bro@.%ld", static_cast(getpid())); } - int flags = GetEndpointFlags(broker_endpoint_flags); + int flags = endpoint_flags_to_int(broker_endpoint_flags); endpoint = unique_ptr(new broker::endpoint(name, flags)); iosource_mgr->Register(this, true); return true; @@ -122,7 +122,7 @@ bool comm::Manager::SetEndpointFlags(Val* broker_endpoint_flags) if ( ! Enabled() ) return false; - int flags = GetEndpointFlags(broker_endpoint_flags); + int flags = endpoint_flags_to_int(broker_endpoint_flags); endpoint->set_flags(flags); return true; } @@ -179,7 +179,8 @@ bool comm::Manager::Print(string topic, string msg, Val* flags) if ( ! Enabled() ) return false; - endpoint->send(move(topic), broker::message{move(msg)}, GetFlags(flags)); + endpoint->send(move(topic), broker::message{move(msg)}, + send_flags_to_int(flags)); return true; } @@ -243,7 +244,7 @@ bool comm::Manager::Event(std::string topic, RecordVal* args, Val* flags) msg.emplace_back(data_val->data); } - endpoint->send(move(topic), move(msg), GetFlags(flags)); + endpoint->send(move(topic), move(msg), send_flags_to_int(flags)); return true; } @@ -275,7 +276,7 @@ bool comm::Manager::AutoEvent(string topic, Val* event, Val* flags) return false; } - handler->AutoRemote(move(topic), GetFlags(flags)); + handler->AutoRemote(move(topic), send_flags_to_int(flags)); return true; } @@ -484,7 +485,7 @@ bool comm::Manager::UnadvertiseTopic(broker::topic t) return true; } -int comm::Manager::GetFlags(Val* flags) +int comm::Manager::send_flags_to_int(Val* flags) { auto r = flags->AsRecordVal(); int rval = 0; @@ -869,6 +870,14 @@ void comm::Manager::Process() auto query = *it; + if ( query->Disabled() ) + { + // Trigger timer must have timed the query out already. + delete query; + pending_queries.erase(it); + continue; + } + switch ( response.reply.stat ) { case broker::store::result::status::timeout: // Fine, trigger's timeout takes care of things. @@ -885,6 +894,7 @@ void comm::Manager::Process() break; } + delete query; pending_queries.erase(it); } } @@ -1006,8 +1016,6 @@ bool comm::Manager::CloseStore(const broker::store::identifier& id, bool comm::Manager::TrackStoreQuery(StoreQueryCallback* cb) { - if ( ! Enabled() ) - return false; - + assert(Enabled()); return pending_queries.insert(cb).second; } diff --git a/src/comm/Manager.h b/src/comm/Manager.h index e8a8d5e5b1..bd1236bf34 100644 --- a/src/comm/Manager.h +++ b/src/comm/Manager.h @@ -14,73 +14,275 @@ namespace comm { -// TODO: documentation - -// Manages various forms of communication between peer Bro processes -// or possibly between different parts of a single Bro process. +/** + * Manages various forms of communication between peer Bro processes + * or other external applications via use of the Broker messaging library. + */ class Manager : public iosource::IOSource { friend class StoreHandleVal; public: + /** + * Destructor. Any still-pending data store queries are aborted. + */ ~Manager(); + /** + * Enable use of communication. + * @param flags used to tune the local Broker endpoint's behavior. + * See the Comm::EndpointFlags record type. + * @return true if communication is successfully initialized. + */ bool Enable(Val* flags); + /** + * Changes endpoint flags originally supplied to comm::Manager::Enable(). + * @param flags the new behavior flags to use. + * @return true if flags were changed. + */ bool SetEndpointFlags(Val* flags); + /** + * @return true if comm::Manager::Enable() has previously been called and + * it succeeded. + */ bool Enabled() { return endpoint != nullptr; } + /** + * Listen for remote connections. + * @param port the TCP port to listen on. + * @param addr an address string on which to accept connections, e.g. + * "127.0.0.1". A nullptr refers to @p INADDR_ANY. + * @param reuse_addr equivalent to behavior of SO_REUSEADDR. + * @return true if the local endpoint is now listening for connections. + */ bool Listen(uint16_t port, const char* addr = nullptr, bool reuse_addr = true); + /** + * Initiate a remote connection. + * @param addr an address to connect to, e.g. "localhost" or "127.0.0.1". + * @param port the TCP port on which the remote side is listening. + * @param retry_interval an interval at which to retry establishing the + * connection with the remote peer. + * @return true if it's possible to try connecting with the peer and + * it's a new peer. The actual connection may not be established until a + * later point in time. + */ bool Connect(std::string addr, uint16_t port, std::chrono::duration retry_interval); + /** + * Remove a remote connection. + * @param addr the address used in comm::Manager::Connect(). + * @param port the port used in comm::Manager::Connect(). + * @return true if the arguments match a previously successful call to + * comm::Manager::Connect(). + */ bool Disconnect(const std::string& addr, uint16_t port); + /** + * Print a simple message to any interested peers. + * @param topic a topic string associated with the print message. + * Peers advertise interest by registering a subscription to some prefix + * of this topic name. + * @param msg the string to send to peers. + * @param flags tune the behavior of how the message is send. + * See the Comm::SendFlags record type. + * @return true if the message is sent successfully. + */ bool Print(std::string topic, std::string msg, Val* flags); + /** + * Send an event to any interested peers. + * @param topic a topic string associated with the print message. + * Peers advertise interest by registering a subscription to some prefix + * of this topic name. + * @param msg the event to send to peers, which is the name of the event + * as a string followed by all of its arguments. + * @param flags tune the behavior of how the message is send. + * See the Comm::SendFlags record type. + * @return true if the message is sent successfully. + */ bool Event(std::string topic, broker::message msg, int flags); + + /** + * Send an event to any interested peers. + * @param topic a topic string associated with the print message. + * Peers advertise interest by registering a subscription to some prefix + * of this topic name. + * @param args the event and its arguments to send to peers. See the + * Comm::EventArgs record type. + * @param flags tune the behavior of how the message is send. + * See the Comm::SendFlags record type. + * @return true if the message is sent successfully. + */ bool Event(std::string topic, RecordVal* args, Val* flags); + /** + * Send a log entry to any interested peers. The topic name used is + * implicitly "bro/log/". + * @param stream_id the stream to which the log entry belongs. + * @param columns the data which comprises the log entry. + * @param flags tune the behavior of how the message is send. + * See the Comm::SendFlags record type. + * @return true if the message is sent successfully. + */ bool Log(EnumVal* stream_id, RecordVal* columns, int flags); + /** + * Automatically send an event to any interested peers whenever it is + * locally dispatched (e.g. using "event my_event(...);" in a script). + * @param topic a topic string associated with the event message. + * Peers advertise interest by registering a subscription to some prefix + * of this topic name. + * @param event a Bro event value. + * @param flags tune the behavior of how the message is send. + * See the Comm::SendFlags record type. + * @return true if automatic event sending is now enabled. + */ bool AutoEvent(std::string topic, Val* event, Val* flags); + /** + * Stop automatically sending an event to peers upon local dispatch. + * @param topic a topic originally given to comm::Manager::AutoEvent(). + * @param event an event originally given to comm::Manager::AutoEvent(). + * @return true if automatic events will no occur for the topic/event pair. + */ bool AutoEventStop(const std::string& topic, Val* event); + /** + * Create an EventArgs record value from an event and its arguments. + * @param args the event and its arguments. The event is always the first + * elements in the list. + * @return an EventArgs record value. If an invalid event or arguments + * were supplied the optional "name" field will not be set. + */ RecordVal* MakeEventArgs(val_list* args); + /** + * Register interest in peer print messages that use a certain topic prefix. + * @param topic_prefix a prefix to match against remote message topics. + * e.g. an empty prefix will match everything and "a" will match "alice" + * and "amy" but not "bob". + * @return true if it's a new print subscriptions and it is now registered. + */ bool SubscribeToPrints(std::string topic_prefix); + /** + * Unregister interest in peer print messages. + * @param topic_prefix a prefix previously supplied to a successful call + * to comm::Manager::SubscribeToPrints(). + * @return true if interest in topic prefix is no longer advertised. + */ bool UnsubscribeToPrints(const std::string& topic_prefix); + /** + * Register interest in peer event messages that use a certain topic prefix. + * @param topic_prefix a prefix to match against remote message topics. + * e.g. an empty prefix will match everything and "a" will match "alice" + * and "amy" but not "bob". + * @return true if it's a new event subscription and it is now registered. + */ bool SubscribeToEvents(std::string topic_prefix); + /** + * Unregister interest in peer event messages. + * @param topic_prefix a prefix previously supplied to a successful call + * to comm::Manager::SubscribeToEvents(). + * @return true if interest in topic prefix is no longer advertised. + */ bool UnsubscribeToEvents(const std::string& topic_prefix); + /** + * Register interest in peer log messages that use a certain topic prefix. + * @param topic_prefix a prefix to match against remote message topics. + * e.g. an empty prefix will match everything and "a" will match "alice" + * and "amy" but not "bob". + * @return true if it's a new log subscription and it is now registered. + */ bool SubscribeToLogs(std::string topic_prefix); + /** + * Unregister interest in peer log messages. + * @param topic_prefix a prefix previously supplied to a successful call + * to comm::Manager::SubscribeToLogs(). + * @return true if interest in topic prefix is no longer advertised. + */ bool UnsubscribeToLogs(const std::string& topic_prefix); + /** + * Allow sending messages to peers if associated with the given topic. + * This has no effect if auto publication behavior is enabled via the flags + * supplied to comm::Manager::Enable() or comm::Manager::SetEndpointFlags(). + * @param t a topic to allow messages to be published under. + * @return true if successful. + */ bool PublishTopic(broker::topic t); + /** + * Disallow sending messages to peers if associated with the given topic. + * This has no effect if auto publication behavior is enabled via the flags + * supplied to comm::Manager::Enable() or comm::Manager::SetEndpointFlags(). + * @param t a topic to disallow messages to be published under. + * @return true if successful. + */ bool UnpublishTopic(broker::topic t); + /** + * Allow advertising interest in the given topic to peers. + * This has no effect if auto advertise behavior is enabled via the flags + * supplied to comm::Manager::Enable() or comm::Manager::SetEndpointFlags(). + * @param t a topic to allow advertising interest/subscription to peers. + * @return true if successful. + */ bool AdvertiseTopic(broker::topic t); + /** + * Disallow advertising interest in the given topic to peers. + * This has no effect if auto advertise behavior is enabled via the flags + * supplied to comm::Manager::Enable() or comm::Manager::SetEndpointFlags(). + * @param t a topic to disallow advertising interest/subscription to peers. + * @return true if successful. + */ bool UnadvertiseTopic(broker::topic t); + /** + * Register the availability of a data store. + * @param handle the data store. + * @return true if the store was valid and not already away of it. + */ bool AddStore(StoreHandleVal* handle); + /** + * Lookup a data store by it's identifier name and type. + * @param id the store's name. + * @param type the type of data store. + * @return a pointer to the store handle if it exists else nullptr. + */ StoreHandleVal* LookupStore(const broker::store::identifier& id, StoreType type); + /** + * Close and unregister a data store. Any existing references to the + * store handle will not be able to be used for any data store operations. + * @param id the stores' name. + * @param type the type of the data store. + * @return true if such a store existed and is now closed. + */ bool CloseStore(const broker::store::identifier& id, StoreType type); + /** + * Register a data store query callback. + * @param cb the callback info to use when the query completes or times out. + * @return true if now tracking a data store query. + */ bool TrackStoreQuery(StoreQueryCallback* cb); - static int GetFlags(Val* flags); + /** + * Convert Comm::SendFlags to int flags for use with broker::send(). + */ + static int send_flags_to_int(Val* flags); private: diff --git a/src/comm/Store.cc b/src/comm/Store.cc index 8c55c31785..5fcc7daa85 100644 --- a/src/comm/Store.cc +++ b/src/comm/Store.cc @@ -48,14 +48,9 @@ comm::StoreHandleVal::StoreHandleVal(broker::store::identifier id, #ifdef HAVE_ROCKSDB std::string path = backend_options->Lookup(1)->AsRecordVal() ->Lookup(0)->AsStringVal()->CheckString(); - bool use_merge_op = backend_options->Lookup(1)->AsRecordVal() - ->Lookup(1)->AsBool(); rocksdb::Options rock_op; rock_op.create_if_missing = true; - if ( use_merge_op ) - options.merge_operator.reset(new rocksdb_merge_operator); - auto rocksdb = new broker::store::rocksdb_backend; if ( rocksdb->open(path, options).ok() ) diff --git a/src/comm/Store.h b/src/comm/Store.h index b02c5b4f5b..289290eab4 100644 --- a/src/comm/Store.h +++ b/src/comm/Store.h @@ -14,12 +14,21 @@ namespace comm { extern OpaqueType* opaque_of_store_handle; +/** + * Enumerates the possible types of data stores. + */ enum StoreType { + // Just a view in to a remote store, contains no data itself. FRONTEND, MASTER, CLONE, }; +/** + * Create a Store::QueryStatus value. + * @param success whether the query status should be set to success or failure. + * @return a Store::QueryStatus value. + */ inline EnumVal* query_status(bool success) { static EnumType* store_query_status = nullptr; @@ -36,6 +45,10 @@ inline EnumVal* query_status(bool success) return new EnumVal(success ? success_val : failure_val, store_query_status); } +/** + * @return a Store::QueryResult value that has a Store::QueryStatus indicating + * a failure. + */ inline RecordVal* query_result() { auto rval = new RecordVal(BifType::Record::Store::QueryResult); @@ -44,6 +57,11 @@ inline RecordVal* query_result() return rval; } +/** + * @param data the result of the query. + * @return a Store::QueryResult value that has a Store::QueryStatus indicating + * a success. + */ inline RecordVal* query_result(RecordVal* data) { auto rval = new RecordVal(BifType::Record::Store::QueryResult); @@ -52,6 +70,9 @@ inline RecordVal* query_result(RecordVal* data) return rval; } +/** + * Used for asynchronous data store queries which use "when" statements. + */ class StoreQueryCallback { public: @@ -84,6 +105,9 @@ public: Unref(result); } + bool Disabled() const + { return trigger->Disabled(); } + const broker::store::identifier& StoreID() const { return store_id; } @@ -98,6 +122,9 @@ private: StoreType store_type; }; +/** + * An opaque handle which wraps a Broker data store. + */ class StoreHandleVal : public OpaqueVal { public: diff --git a/src/comm/comm.bif b/src/comm/comm.bif index 1d41b572f6..23e163c748 100644 --- a/src/comm/comm.bif +++ b/src/comm/comm.bif @@ -9,50 +9,133 @@ module Comm; type Comm::EndpointFlags: record; +## Enable use of communication. +## +## flags: used to tune the local Broker endpoint behavior. +## +## Returns: true if communication is successfully initialized. function Comm::enable%(flags: EndpointFlags &default = EndpointFlags()%): bool %{ return new Val(comm_mgr->Enable(flags), TYPE_BOOL); %} +## Changes endpoint flags originally supplied to :bro:see:`Comm::enable`. +## +## flags: the new endpoint behavior flags to use. +## +## Returns: true of flags were changed. function Comm::set_endpoint_flags%(flags: EndpointFlags &default = EndpointFlags()%): bool %{ return new Val(comm_mgr->SetEndpointFlags(flags), TYPE_BOOL); %} +## Allow sending messages to peers if associated with the given topic. +## This has no effect if auto publication behavior is enabled via the flags +## supplied to :bro:see:`Comm::enable` or :bro:see:`Comm::set_endpoint_flags`. +## +## topic: a topic to allow messages to be published under. +## +## Returns: true if successful. function Comm::publish_topic%(topic: string%): bool %{ return new Val(comm_mgr->PublishTopic(topic->CheckString()), TYPE_BOOL); %} +## Disallow sending messages to peers if associated with the given topic. +## This has no effect if auto publication behavior is enabled via the flags +## supplied to :bro:see:`Comm::enable` or :bro:see:`Comm::set_endpoint_flags`. +## +## topic: a topic to disallow messages to be published under. +## +## Returns: true if successful. function Comm::unpublish_topic%(topic: string%): bool %{ return new Val(comm_mgr->UnpublishTopic(topic->CheckString()), TYPE_BOOL); %} +## Allow advertising interest in the given topic to peers. +## This has no effect if auto advertise behavior is enabled via the flags +## supplied to :bro:see:`Comm::enable` or :bro:see:`Comm::set_endpoint_flags`. +## +## topic: a topic to allow advertising interest/subscription to peers. +## +## Returns: true if successful. function Comm::advertise_topic%(topic: string%): bool %{ return new Val(comm_mgr->AdvertiseTopic(topic->CheckString()), TYPE_BOOL); %} +## Disallow advertising interest in the given topic to peers. +## This has no effect if auto advertise behavior is enabled via the flags +## supplied to :bro:see:`Comm::enable` or :bro:see:`Comm::set_endpoint_flags`. +## +## topic: a topic to disallow advertising interest/subscription to peers. +## +## Returns: true if successful. function Comm::unadvertise_topic%(topic: string%): bool %{ return new Val(comm_mgr->UnadvertiseTopic(topic->CheckString()), TYPE_BOOL); %} +## Generated when a connection has been established due to a previous call +## to :bro:see:`Comm::connect`. +## +## peer_address: the address used to connect to the peer. +## +## peer_port: the port used to connect to the peer. +## +## peer_name: the name by which the peer identified itself. event Comm::outgoing_connection_established%(peer_address: string, peer_port: port, peer_name: string%); +## Generated when a previously established connection becomes broken. +## Reconnection will automatically be attempted at a frequency given +## by the original call to :bro:see:`Comm::connect`. +## +## peer_address: the address used to connect to the peer. +## +## peer_port: the port used to connect to the peer. +## +## .. bro:see:: Comm::outgoing_connection_established event Comm::outgoing_connection_broken%(peer_address: string, peer_port: port%); +## Generated when a connection via :bro:see:`Comm::connect` has failed +## because the remote side is incompatible. +## +## peer_address: the address used to connect to the peer. +## +## peer_port: the port used to connect to the peer. event Comm::outgoing_connection_incompatible%(peer_address: string, peer_port: port%); +## Generated when a peer has established a connection with this process +## as a result of previously performing a :bro:see:`Comm::listen`. +## +## peer_name: the name by which the peer identified itself. event Comm::incoming_connection_established%(peer_name: string%); +## Generated when a peer that previously established a connection with this +## process becomes disconnected. +## +## peer_name: the name by which the peer identified itself. +## +## .. bro:see:: Comm::incoming_connection_established event Comm::incoming_connection_broken%(peer_name: string%); +## Listen for remote connections. +## +## p: the TCP port to listen on. +## +## a: an address string on which to accept connections, e.g. +## "127.0.0.1". An empty string refers to @p INADDR_ANY. +## +## reuse: equivalent to behavior of SO_REUSEADDR. +## +## Returns: true if the local endpoint is now listening for connections. +## +## .. bro:see:: Comm::incoming_connection_established function Comm::listen%(p: port, a: string &default = "", reuse: bool &default = T%): bool %{ @@ -67,6 +150,21 @@ function Comm::listen%(p: port, a: string &default = "", return new Val(rval, TYPE_BOOL); %} +## Initiate a remote connection. +## +## a: an address to connect to, e.g. "localhost" or "127.0.0.1". +## +## p: the TCP port on which the remote side is listening. +## +## retry: an interval at which to retry establishing the +## connection with the remote peer if it cannot be made initially, or +## if it ever becomes disconnected. +## +## Returns: true if it's possible to try connecting with the peer and +## it's a new peer. The actual connection may not be established +## a later point in time. +## +## .. bro:see:: Comm::outgoing_connection_established function Comm::connect%(a: string, p: port, retry: interval%): bool %{ if ( ! p->IsTCP() ) @@ -80,6 +178,14 @@ function Comm::connect%(a: string, p: port, retry: interval%): bool return new Val(rval, TYPE_BOOL); %} +## Remove a remote connection. +## +## a: the address used in previous successful call to :bro:see:`Comm::connect`. +## +## p: the port used in previous successful call to :bro:see:`Comm::connect`. +## +## Returns: true if the arguments match a previously successful call to +## :bro:see:`Comm::connect`. function Comm::disconnect%(a: string, p: port%): bool %{ if ( ! p->IsTCP() ) diff --git a/src/comm/data.bif b/src/comm/data.bif index 2a78a9229a..7120046920 100644 --- a/src/comm/data.bif +++ b/src/comm/data.bif @@ -7,6 +7,8 @@ module Comm; +## Enumerates the possible types that :bro:see:`Comm::Data` may be in terms of +## Bro data types. enum DataType %{ BOOL, INT, @@ -29,36 +31,78 @@ type Comm::Data: record; type Comm::TableItem: record; +## Convert any Bro value in to communication data. +## +## d: any Bro value to attempt to convert (not all types are supported). +## +## Returns: the converted communication data which may not set its only +## opaque field of the the conversion was not possible (the Bro data +## type does not support being converted to communicaiton data). function Comm::data%(d: any%): Comm::Data %{ return comm::make_data_val(d); %} +## Retrieve the type of data associated with communication data. +## +## d: the communication data. +## +## Returns: the data type associated with the communication data. function Comm::data_type%(d: Comm::Data%): Comm::DataType %{ return comm::get_data_type(d->AsRecordVal(), frame); %} +## Convert communication data with a type of :bro:see:`Comm::BOOL` to +## an actual Bro value. +## +## d: the communication data to convert. +## +## Returns: the value retrieved from the communication data. function Comm::refine_to_bool%(d: Comm::Data%): bool %{ return comm::refine(d->AsRecordVal(), TYPE_BOOL, frame); %} +## Convert communication data with a type of :bro:see:`Comm::INT` to +## an actual Bro value. +## +## d: the communication data to convert. +## +## Returns: the value retrieved from the communication data. function Comm::refine_to_int%(d: Comm::Data%): int %{ return comm::refine(d->AsRecordVal(), TYPE_INT, frame); %} +## Convert communication data with a type of :bro:see:`Comm::COUNT` to +## an actual Bro value. +## +## d: the communication data to convert. +## +## Returns: the value retrieved from the communication data. function Comm::refine_to_count%(d: Comm::Data%): count %{ return comm::refine(d->AsRecordVal(), TYPE_COUNT, frame); %} +## Convert communication data with a type of :bro:see:`Comm::DOUBLE` to +## an actual Bro value. +## +## d: the communication data to convert. +## +## Returns: the value retrieved from the communication data. function Comm::refine_to_double%(d: Comm::Data%): double %{ return comm::refine(d->AsRecordVal(), TYPE_DOUBLE, frame); %} +## Convert communication data with a type of :bro:see:`Comm::STRING` to +## an actual Bro value. +## +## d: the communication data to convert. +## +## Returns: the value retrieved from the communication data. function Comm::refine_to_string%(d: Comm::Data%): string %{ return new StringVal(comm::require_data_type(d->AsRecordVal(), @@ -66,6 +110,12 @@ function Comm::refine_to_string%(d: Comm::Data%): string frame)); %} +## Convert communication data with a type of :bro:see:`Comm::ADDR` to +## an actual Bro value. +## +## d: the communication data to convert. +## +## Returns: the value retrieved from the communication data. function Comm::refine_to_addr%(d: Comm::Data%): addr %{ auto& a = comm::require_data_type(d->AsRecordVal(), @@ -74,6 +124,12 @@ function Comm::refine_to_addr%(d: Comm::Data%): addr return new AddrVal(IPAddr(*bits)); %} +## Convert communication data with a type of :bro:see:`Comm::SUBNET` to +## an actual Bro value. +## +## d: the communication data to convert. +## +## Returns: the value retrieved from the communication data. function Comm::refine_to_subnet%(d: Comm::Data%): subnet %{ auto& a = comm::require_data_type(d->AsRecordVal(), @@ -82,6 +138,12 @@ function Comm::refine_to_subnet%(d: Comm::Data%): subnet return new SubNetVal(IPPrefix(IPAddr(*bits), a.length())); %} +## Convert communication data with a type of :bro:see:`Comm::PORT` to +## an actual Bro value. +## +## d: the communication data to convert. +## +## Returns: the value retrieved from the communication data. function Comm::refine_to_port%(d: Comm::Data%): port %{ auto& a = comm::require_data_type(d->AsRecordVal(), @@ -89,6 +151,12 @@ function Comm::refine_to_port%(d: Comm::Data%): port return new PortVal(a.number(), comm::to_bro_port_proto(a.type())); %} +## Convert communication data with a type of :bro:see:`Comm::TIME` to +## an actual Bro value. +## +## d: the communication data to convert. +## +## Returns: the value retrieved from the communication data. function Comm::refine_to_time%(d: Comm::Data%): time %{ auto v = comm::require_data_type(d->AsRecordVal(), @@ -96,6 +164,12 @@ function Comm::refine_to_time%(d: Comm::Data%): time return new Val(v, TYPE_TIME); %} +## Convert communication data with a type of :bro:see:`Comm::INTERVAL` to +## an actual Bro value. +## +## d: the communication data to convert. +## +## Returns: the value retrieved from the communication data. function Comm::refine_to_interval%(d: Comm::Data%): interval %{ auto v = comm::require_data_type(d->AsRecordVal(), @@ -103,6 +177,13 @@ function Comm::refine_to_interval%(d: Comm::Data%): interval return new Val(v, TYPE_INTERVAL); %} +## Convert communication data with a type of :bro:see:`Comm::ENUM` to +## the name of the enum value. :bro:see:`lookup_ID` may be used to convert +## the name to the actual enum value. +## +## d: the communication data to convert. +## +## Returns: the enum name retrieved from the communication data. function Comm::refine_to_enum_name%(d: Comm::Data%): string %{ auto& v = comm::require_data_type(d->AsRecordVal(), @@ -110,11 +191,17 @@ function Comm::refine_to_enum_name%(d: Comm::Data%): string return new StringVal(v); %} +## Create communication data of type "set". function Comm::set_create%(%): Comm::Data %{ return comm::make_data_val(broker::set()); %} +## Remove all elements within a set. +## +## s: the set to clear. +## +## Returns: always true. function Comm::set_clear%(s: Comm::Data%): bool %{ auto& v = comm::require_data_type(s->AsRecordVal(), TYPE_TABLE, @@ -123,6 +210,11 @@ function Comm::set_clear%(s: Comm::Data%): bool return new Val(true, TYPE_BOOL); %} +## Get the number of elements within a set. +## +## s: the set to query. +## +## Returns: the number of elements in the set. function Comm::set_size%(s: Comm::Data%): count %{ auto& v = comm::require_data_type(s->AsRecordVal(), TYPE_TABLE, @@ -130,6 +222,13 @@ function Comm::set_size%(s: Comm::Data%): count return new Val(static_cast(v.size()), TYPE_COUNT); %} +## Check if a set contains a particular element. +## +## s: the set to query. +## +## key: the element to check for existence. +## +## Returns: true if the key exists in the set. function Comm::set_contains%(s: Comm::Data, key: Comm::Data%): bool %{ auto& v = comm::require_data_type(s->AsRecordVal(), TYPE_TABLE, @@ -138,6 +237,13 @@ function Comm::set_contains%(s: Comm::Data, key: Comm::Data%): bool return new Val(v.find(k) != v.end(), TYPE_BOOL); %} +### Insert an element into a set. +## +## s: the set to modify. +## +## key: the element to insert. +## +## Returns: true if the key was inserted, or false if it already existed. function Comm::set_insert%(s: Comm::Data, key: Comm::Data%): bool %{ auto& v = comm::require_data_type(s->AsRecordVal(), TYPE_TABLE, @@ -146,6 +252,13 @@ function Comm::set_insert%(s: Comm::Data, key: Comm::Data%): bool return new Val(v.insert(k).second, TYPE_BOOL); %} +## Remove an element from a set. +## +## s: the set to modify. +## +## key: the element to remove. +## +## Returns: true if the element existed in the set and is now removed. function Comm::set_remove%(s: Comm::Data, key: Comm::Data%): bool %{ auto& v = comm::require_data_type(s->AsRecordVal(), TYPE_TABLE, @@ -154,17 +267,36 @@ function Comm::set_remove%(s: Comm::Data, key: Comm::Data%): bool return new Val(v.erase(k) > 0, TYPE_BOOL); %} +## Create an iterator for a set. Note that this makes a copy of the set +## internally to ensure the iterator is always valid. +## +## s: the set to iterate over. +## +## Returns: an iterator. function Comm::set_iterator%(s: Comm::Data%): opaque of Comm::SetIterator %{ return new comm::SetIterator(s->AsRecordVal(), TYPE_TABLE, frame); %} +## Check if there are no more elements to iterate over. +## +## it: an iterator. +## +## Returns: true if there are no more elements to iterator over, i.e. +## the iterator is one-past-the-final-element. function Comm::set_iterator_last%(it: opaque of Comm::SetIterator%): bool %{ auto set_it = static_cast(it); return new Val(set_it->it == set_it->dat.end(), TYPE_BOOL); %} +## Advance an iterator. +## +## it: an iterator. +## +## Returns: true if the iterator, after advancing, still references an element +## in the collection. False if the iterator, after advancing, is +## one-past-the-final-element. function Comm::set_iterator_next%(it: opaque of Comm::SetIterator%): bool %{ auto set_it = static_cast(it); @@ -176,6 +308,11 @@ function Comm::set_iterator_next%(it: opaque of Comm::SetIterator%): bool return new Val(set_it->it != set_it->dat.end(), TYPE_BOOL); %} +## Retrieve the data at an iterator's current position. +## +## it: an iterator. +## +## Returns: element in the collection that the iterator currently references. function Comm::set_iterator_value%(it: opaque of Comm::SetIterator%): Comm::Data %{ auto set_it = static_cast(it); @@ -193,11 +330,17 @@ function Comm::set_iterator_value%(it: opaque of Comm::SetIterator%): Comm::Data return rval; %} +## Create communication data of type "table". function Comm::table_create%(%): Comm::Data %{ return comm::make_data_val(broker::table()); %} +## Remove all elements within a table. +## +## t: the table to clear. +## +## Returns: always true. function Comm::table_clear%(t: Comm::Data%): bool %{ auto& v = comm::require_data_type(t->AsRecordVal(), @@ -206,6 +349,11 @@ function Comm::table_clear%(t: Comm::Data%): bool return new Val(true, TYPE_BOOL); %} +## Get the number of elements within a table. +## +## t: the table to query. +## +## Returns: the number of elements in the table. function Comm::table_size%(t: Comm::Data%): count %{ auto& v = comm::require_data_type(t->AsRecordVal(), @@ -213,6 +361,13 @@ function Comm::table_size%(t: Comm::Data%): count return new Val(static_cast(v.size()), TYPE_COUNT); %} +## Check if a table contains a particular key. +## +## t: the table to query. +## +## key: the key to check for existence. +## +## Returns: true if the key exists in the set. function Comm::table_contains%(t: Comm::Data, key: Comm::Data%): bool %{ auto& v = comm::require_data_type(t->AsRecordVal(), @@ -221,6 +376,16 @@ function Comm::table_contains%(t: Comm::Data, key: Comm::Data%): bool return new Val(v.find(k) != v.end(), TYPE_BOOL); %} +## Insert a key-value pair into a table. +## +## t: the table to modify. +## +## key: the key at which to insert the value. +## +## val: the value to insert. +## +## Returns: true if the key-value pair was inserted, or false if the key +## already existed in the table. function Comm::table_insert%(t: Comm::Data, key: Comm::Data, val: Comm::Data%): Comm::Data %{ auto& table = comm::require_data_type(t->AsRecordVal(), @@ -242,6 +407,14 @@ function Comm::table_insert%(t: Comm::Data, key: Comm::Data, val: Comm::Data%): } %} +## Remove a key-value pair from a table. +## +## t: the table to modify. +## +## key: the key to remove from the table. +## +## Returns: the value associated with the key. If the key did not exist, then +## the optional field of the returned record is not set. function Comm::table_remove%(t: Comm::Data, key: Comm::Data%): Comm::Data %{ auto& table = comm::require_data_type(t->AsRecordVal(), @@ -259,6 +432,14 @@ function Comm::table_remove%(t: Comm::Data, key: Comm::Data%): Comm::Data } %} +## Retrieve a value from a table. +## +## t: the table to query. +## +## key: the key to lookup. +## +## Returns: the value associated with the key. If the key did not exist, then +## the optional field of the returned record is not set. function Comm::table_lookup%(t: Comm::Data, key: Comm::Data%): Comm::Data %{ auto& table = comm::require_data_type(t->AsRecordVal(), @@ -272,17 +453,36 @@ function Comm::table_lookup%(t: Comm::Data, key: Comm::Data%): Comm::Data return comm::make_data_val(it->second); %} +## Create an iterator for a table. Note that this makes a copy of the table +## internally to ensure the iterator is always valid. +## +## t: the table to iterate over. +## +## Returns: an iterator. function Comm::table_iterator%(t: Comm::Data%): opaque of Comm::TableIterator %{ return new comm::TableIterator(t->AsRecordVal(), TYPE_TABLE, frame); %} +## Check if there are no more elements to iterate over. +## +## it: an iterator. +## +## Returns: true if there are no more elements to iterator over, i.e. +## the iterator is one-past-the-final-element. function Comm::table_iterator_last%(it: opaque of Comm::TableIterator%): bool %{ auto ti = static_cast(it); return new Val(ti->it == ti->dat.end(), TYPE_BOOL); %} +## Advance an iterator. +## +## it: an iterator. +## +## Returns: true if the iterator, after advancing, still references an element +## in the collection. False if the iterator, after advancing, is +## one-past-the-final-element. function Comm::table_iterator_next%(it: opaque of Comm::TableIterator%): bool %{ auto ti = static_cast(it); @@ -294,6 +494,11 @@ function Comm::table_iterator_next%(it: opaque of Comm::TableIterator%): bool return new Val(ti->it != ti->dat.end(), TYPE_BOOL); %} +## Retrieve the data at an iterator's current position. +## +## it: an iterator. +## +## Returns: element in the collection that the iterator currently references. function Comm::table_iterator_value%(it: opaque of Comm::TableIterator%): Comm::TableItem %{ auto ti = static_cast(it); @@ -316,11 +521,17 @@ function Comm::table_iterator_value%(it: opaque of Comm::TableIterator%): Comm:: return rval; %} +## Create communication data of type "vector". function Comm::vector_create%(%): Comm::Data %{ return comm::make_data_val(broker::vector()); %} +## Remove all elements within a vector. +## +## v: the vector to clear. +## +## Returns: always true. function Comm::vector_clear%(v: Comm::Data%): bool %{ auto& vec = comm::require_data_type(v->AsRecordVal(), @@ -329,6 +540,11 @@ function Comm::vector_clear%(v: Comm::Data%): bool return new Val(true, TYPE_BOOL); %} +## Get the number of elements within a vector. +## +## v: the vector to query. +## +## Returns: the number of elements in the vector. function Comm::vector_size%(v: Comm::Data%): count %{ auto& vec = comm::require_data_type(v->AsRecordVal(), @@ -336,6 +552,17 @@ function Comm::vector_size%(v: Comm::Data%): count return new Val(static_cast(vec.size()), TYPE_COUNT); %} +## Insert an element into a vector at a particular position, possibly displacing +## existing elements (insertion always grows the size of the vector by one). +## +## v: the vector to modify. +## +## d: the element to insert. +## +## idx: the index at which to insert the data. If it is greater than the +## current size of the vector, the element is inserted at the end. +## +## Returns: always true. function Comm::vector_insert%(v: Comm::Data, d: Comm::Data, idx: count%): bool %{ auto& vec = comm::require_data_type(v->AsRecordVal(), @@ -346,6 +573,16 @@ function Comm::vector_insert%(v: Comm::Data, d: Comm::Data, idx: count%): bool return new Val(true, TYPE_BOOL); %} +## Replace an element in a vector at a particular position. +## +## v: the vector to modify. +## +## d: the element to insert. +## +## idx: the index to replace. +## +## Returns: the value that was just evicted. If the index was larger than any +## valid index, the optional field of the returned record is not set. function Comm::vector_replace%(v: Comm::Data, d: Comm::Data, idx: count%): Comm::Data %{ auto& vec = comm::require_data_type(v->AsRecordVal(), @@ -360,6 +597,14 @@ function Comm::vector_replace%(v: Comm::Data, d: Comm::Data, idx: count%): Comm: return rval; %} +## Remove an element from a vector at a particular position. +## +## v: the vector to modify. +## +## idx: the index to remove. +## +## Returns: the value that was just evicted. If the index was larger than any +## valid index, the optional field of the returned record is not set. function Comm::vector_remove%(v: Comm::Data, idx: count%): Comm::Data %{ auto& vec = comm::require_data_type(v->AsRecordVal(), @@ -373,6 +618,14 @@ function Comm::vector_remove%(v: Comm::Data, idx: count%): Comm::Data return rval; %} +## Lookup an element in a vector at a particular position. +## +## v: the vector to query. +## +## idx: the index to lookup. +## +## Returns: the value at the index. If the index was larger than any +## valid index, the optional field of the returned record is not set. function Comm::vector_lookup%(v: Comm::Data, idx: count%): Comm::Data %{ auto& vec = comm::require_data_type(v->AsRecordVal(), @@ -384,17 +637,36 @@ function Comm::vector_lookup%(v: Comm::Data, idx: count%): Comm::Data return comm::make_data_val(vec[idx]); %} +## Create an iterator for a vector. Note that this makes a copy of the vector +## internally to ensure the iterator is always valid. +## +## v: the vector to iterate over. +## +## Returns: an iterator. function Comm::vector_iterator%(v: Comm::Data%): opaque of Comm::VectorIterator %{ return new comm::VectorIterator(v->AsRecordVal(), TYPE_VECTOR, frame); %} +## Check if there are no more elements to iterate over. +## +## it: an iterator. +## +## Returns: true if there are no more elements to iterator over, i.e. +## the iterator is one-past-the-final-element. function Comm::vector_iterator_last%(it: opaque of Comm::VectorIterator%): bool %{ auto vi = static_cast(it); return new Val(vi->it == vi->dat.end(), TYPE_BOOL); %} +## Advance an iterator. +## +## it: an iterator. +## +## Returns: true if the iterator, after advancing, still references an element +## in the collection. False if the iterator, after advancing, is +## one-past-the-final-element. function Comm::vector_iterator_next%(it: opaque of Comm::VectorIterator%): bool %{ auto vi = static_cast(it); @@ -406,6 +678,11 @@ function Comm::vector_iterator_next%(it: opaque of Comm::VectorIterator%): bool return new Val(vi->it != vi->dat.end(), TYPE_BOOL); %} +## Retrieve the data at an iterator's current position. +## +## it: an iterator. +## +## Returns: element in the collection that the iterator currently references. function Comm::vector_iterator_value%(it: opaque of Comm::VectorIterator%): Comm::Data %{ auto vi = static_cast(it); @@ -414,7 +691,7 @@ function Comm::vector_iterator_value%(it: opaque of Comm::VectorIterator%): Comm if ( vi->it == vi->dat.end() ) { reporter->PushLocation(frame->GetCall()->GetLocationInfo()); - reporter->Warning("attempt to retrieve value of invalid table iterator"); + reporter->Warning("attempt to retrieve value of invalid vector iterator"); reporter->PopLocation(); return rval; } @@ -423,11 +700,21 @@ function Comm::vector_iterator_value%(it: opaque of Comm::VectorIterator%): Comm return rval; %} +## Create communication data of type "record". +## +## sz: the number of fields in the record. +## +## Returns: record data, with all fields uninitialized. function Comm::record_create%(sz: count%): Comm::Data %{ return comm::make_data_val(broker::record(std::vector(sz))); %} +## Get the number of fields within a record. +## +## r: the record to query. +## +## Returns: the number of fields in the record. function Comm::record_size%(r: Comm::Data%): count %{ auto& v = comm::require_data_type(r->AsRecordVal(), @@ -435,6 +722,15 @@ function Comm::record_size%(r: Comm::Data%): count return new Val(static_cast(v.fields.size()), TYPE_COUNT); %} +## Replace a field in a record at a particular position. +## +## t: the table to modify. +## +## d: the new field value to assign. +## +## idx: the index to replace. +## +## Returns: false if the index was larger than any valid index, else true. function Comm::record_assign%(r: Comm::Data, d: Comm::Data, idx: count%): bool %{ auto& v = comm::require_data_type(r->AsRecordVal(), @@ -448,6 +744,15 @@ function Comm::record_assign%(r: Comm::Data, d: Comm::Data, idx: count%): bool return new Val(true, TYPE_BOOL); %} +## Lookup a field in a record at a particular position. +## +## r: the record to query. +## +## idx: the index to lookup. +## +## Returns: the value at the index. The optional field of the returned record +## may not be set if the field of the record has no value or if the +## the index was not valid. function Comm::record_lookup%(r: Comm::Data, idx: count%): Comm::Data %{ auto& v = comm::require_data_type(r->AsRecordVal(), @@ -462,17 +767,36 @@ function Comm::record_lookup%(r: Comm::Data, idx: count%): Comm::Data return comm::make_data_val(*v.fields[idx]); %} +## Create an iterator for a record. Note that this makes a copy of the record +## internally to ensure the iterator is always valid. +## +## r: the record to iterate over. +## +## Returns: an iterator. function Comm::record_iterator%(r: Comm::Data%): opaque of Comm::RecordIterator %{ return new comm::RecordIterator(r->AsRecordVal(), TYPE_RECORD, frame); %} +## Check if there are no more elements to iterate over. +## +## it: an iterator. +## +## Returns: true if there are no more elements to iterator over, i.e. +## the iterator is one-past-the-final-element. function Comm::record_iterator_last%(it: opaque of Comm::RecordIterator%): bool %{ auto ri = static_cast(it); return new Val(ri->it == ri->dat.fields.end(), TYPE_BOOL); %} +## Advance an iterator. +## +## it: an iterator. +## +## Returns: true if the iterator, after advancing, still references an element +## in the collection. False if the iterator, after advancing, is +## one-past-the-final-element. function Comm::record_iterator_next%(it: opaque of Comm::RecordIterator%): bool %{ auto ri = static_cast(it); @@ -484,6 +808,11 @@ function Comm::record_iterator_next%(it: opaque of Comm::RecordIterator%): bool return new Val(ri->it != ri->dat.fields.end(), TYPE_BOOL); %} +## Retrieve the data at an iterator's current position. +## +## it: an iterator. +## +## Returns: element in the collection that the iterator currently references. function Comm::record_iterator_value%(it: opaque of Comm::RecordIterator%): Comm::Data %{ auto ri = static_cast(it); diff --git a/src/comm/messaging.bif b/src/comm/messaging.bif index 26f9497449..fb65c981b2 100644 --- a/src/comm/messaging.bif +++ b/src/comm/messaging.bif @@ -14,6 +14,15 @@ type Comm::EventArgs: record; event Comm::print_handler%(msg: string%); +## Print a simple message to any interested peers. +## +## topic: a topic associated with the printed message. +## +## msg: the print message to send to peers. +## +## flags: tune the behavior of how the message is sent. +## +## Returns: true if the message is sent. function Comm::print%(topic: string, msg: string, flags: SendFlags &default = SendFlags()%): bool %{ @@ -22,24 +31,53 @@ function Comm::print%(topic: string, msg: string, return new Val(rval, TYPE_BOOL); %} +## Register interest in all peer print messages that use a certain topic prefix. +## +## topic_prefix: a prefix to match against remote message topics. +## e.g. an empty prefix matches everything and "a" matches +## "alice" and "amy" but not "bob". +## +## Returns: true if it's a new print subscription and it is now registered. function Comm::subscribe_to_prints%(topic_prefix: string%): bool %{ auto rval = comm_mgr->SubscribeToPrints(topic_prefix->CheckString()); return new Val(rval, TYPE_BOOL); %} +## Unregister interest in all peer print messages that use a topic prefix. +## +## topic_prefix: a prefix previously supplied to a successful call to +## :bro:see:`Comm::subscribe_to_prints`. +## +## Returns: true if interest in the topic prefix is no longer advertised. function Comm::unsubscribe_to_prints%(topic_prefix: string%): bool %{ auto rval = comm_mgr->UnsubscribeToPrints(topic_prefix->CheckString()); return new Val(rval, TYPE_BOOL); %} +## Create a data structure that may be used to send a remote event via +## :bro:see:`Comm::event`. +## +## args: an event, followed by a list of argument values that may be used +## to call it. +## +## Returns: opaque communication data that may be used to send a remote event. function Comm::event_args%(...%): Comm::EventArgs %{ auto rval = comm_mgr->MakeEventArgs(@ARGS@); return rval; %} +## Send an event to any interested peers. +## +## topic: a topic associated with the event message. +## +## args: event arguments as made by :bro:see:`Comm::event_args`. +## +## flags: tune the behavior of how the message is sent. +## +## Returns: true if the message is sent. function Comm::event%(topic: string, args: Comm::EventArgs, flags: SendFlags &default = SendFlags()%): bool %{ @@ -48,6 +86,18 @@ function Comm::event%(topic: string, args: Comm::EventArgs, return new Val(rval, TYPE_BOOL); %} +## Automatically send an event to any interested peers whenever it is +## locally dispatched (e.g. using "event my_event(...);" in a script). +## +## topic: a topic string associated with the event message. +## Peers advertise interest by registering a subscription to some prefix +## of this topic name. +## +## ev: a Bro event value. +## +## flags: tune the behavior of how the message is send. +## +## Returns: true if automatic event sending is now enabled. function Comm::auto_event%(topic: string, ev: any, flags: SendFlags &default = SendFlags()%): bool %{ @@ -55,51 +105,101 @@ function Comm::auto_event%(topic: string, ev: any, return new Val(rval, TYPE_BOOL); %} +## Stop automatically sending an event to peers upon local dispatch. +## +## topic: a topic originally given to :bro:see:`Comm::auto_event`. +## +## ev: an event originally given to :bro:see:`Comm::auto_event`. +## +## Returns: true if automatic events will no occur for the topic/event pair. function Comm::auto_event_stop%(topic: string, ev: any%): bool %{ auto rval = comm_mgr->AutoEventStop(topic->CheckString(), ev); return new Val(rval, TYPE_BOOL); %} +## Register interest in all peer event messages that use a certain topic prefix. +## +## topic_prefix: a prefix to match against remote message topics. +## e.g. an empty prefix matches everything and "a" matches +## "alice" and "amy" but not "bob". +## +## Returns: true if it's a new event subscription and it is now registered. function Comm::subscribe_to_events%(topic_prefix: string%): bool %{ auto rval = comm_mgr->SubscribeToEvents(topic_prefix->CheckString()); return new Val(rval, TYPE_BOOL); %} +## Unregister interest in all peer event messages that use a topic prefix. +## +## topic_prefix: a prefix previously supplied to a successful call to +## :bro:see:`Comm::subscribe_to_events`. +## +## Returns: true if interest in the topic prefix is no longer advertised. function Comm::unsubscribe_to_events%(topic_prefix: string%): bool %{ auto rval = comm_mgr->UnsubscribeToEvents(topic_prefix->CheckString()); return new Val(rval, TYPE_BOOL); %} +## Enable remote logs for a given log stream. +## +## id: the log stream to enable remote logs for. +## +## flags: tune the behavior of how log entry messages are sent. +## +## Returns: true if remote logs are enabled for the stream. function Comm::enable_remote_logs%(id: Log::ID, flags: SendFlags &default = SendFlags()%): bool %{ auto rval = log_mgr->EnableRemoteLogs(id->AsEnumVal(), - comm::Manager::GetFlags(flags)); + comm::Manager::send_flags_to_int(flags)); return new Val(rval, TYPE_BOOL); %} +## Disable remote logs for a given log stream. +## +## id: the log stream to disable remote logs for. +## +## Returns: true if remote logs are disabled for the stream. function Comm::disable_remote_logs%(id: Log::ID%): bool %{ auto rval = log_mgr->DisableRemoteLogs(id->AsEnumVal()); return new Val(rval, TYPE_BOOL); %} +## Returns: true if remote logs are enabled for the given stream. function Comm::remote_logs_enabled%(id: Log::ID%): bool %{ auto rval = log_mgr->RemoteLogsAreEnabled(id->AsEnumVal()); return new Val(rval, TYPE_BOOL); %} +## Register interest in all peer log messages that use a certain topic prefix. +## Logs are implicitly sent with topic "bro/log/" and the +## receiving side processes them through the logging framework as usual. +## +## topic_prefix: a prefix to match against remote message topics. +## e.g. an empty prefix matches everything and "a" matches +## "alice" and "amy" but not "bob". +## +## Returns: true if it's a new log subscription and it is now registered. function Comm::subscribe_to_logs%(topic_prefix: string%): bool %{ auto rval = comm_mgr->SubscribeToLogs(topic_prefix->CheckString()); return new Val(rval, TYPE_BOOL); %} +## Unregister interest in all peer log messages that use a topic prefix. +## Logs are implicitly sent with topic "bro/log/" and the +## receiving side processes them through the logging framework as usual. +## +## topic_prefix: a prefix previously supplied to a successful call to +## :bro:see:`Comm::subscribe_to_logs`. +## +## Returns: true if interest in the topic prefix is no longer advertised. function Comm::unsubscribe_to_logs%(topic_prefix: string%): bool %{ auto rval = comm_mgr->UnsubscribeToLogs(topic_prefix->CheckString()); diff --git a/src/comm/store.bif b/src/comm/store.bif index 18e63282e8..6a27c05dcb 100644 --- a/src/comm/store.bif +++ b/src/comm/store.bif @@ -16,12 +16,22 @@ type Store::QueryResult: record; type Store::BackendOptions: record; +## Enumerates the possible storage backends. enum BackendType %{ MEMORY, SQLITE, ROCKSDB, %} +## Create a master data store which contains key-value pairs. +## +## id: a unique name for the data store. +## +## b: the storage backend to use. +## +## options: tunes how some storage backends operate. +## +## Returns: a handle to the data store. function Store::create_master%(id: string, b: BackendType &default = MEMORY, options: BackendOptions &default = BackendOptions()%): opaque of Store::Handle %{ @@ -42,6 +52,28 @@ function Store::create_master%(id: string, b: BackendType &default = MEMORY, return rval; %} +## Create a clone of a master data store which may live with a remote peer. +## A clone automatically synchronizes to the master by automatically receiving +## modifications and applying them locally. Direct modifications are not +## possible, they must be sent through the master store, which then +## automatically broadcasts the changes out to clones. But queries may be made +## directly against the local cloned copy, which may be resolved quicker than +## reaching out to a remote master store. +## +## id: the unique name which identifies the master data store. +## +## b: the storage backend to use. +## +## options: tunes how some storage backends operate. +## +## resync: the interval at which to re-attempt synchronizing with the master +## store should the connection be lost. If the clone has not yet +## synchronized for the first time, updates and queries queue up until +## the synchronization completes. After, if the connection to the +## master store is lost, queries continue to use the clone's version, +## but updates will be lost until the master is once again available. +## +## Returns: a handle to the data store. function Store::create_clone%(id: string, b: BackendType &default = MEMORY, options: BackendOptions &default = BackendOptions(), resync: interval &default = 1sec%): opaque of Store::Handle @@ -64,6 +96,12 @@ function Store::create_clone%(id: string, b: BackendType &default = MEMORY, return rval; %} +## Create a frontend interface to an existing master data store that allows +## querying and updating its contents. +## +## id: the unique name which identifies the master data store. +## +## Returns: a handle to the data store. function Store::create_frontend%(id: string%): opaque of Store::Handle %{ auto id_str = id->CheckString(); @@ -81,6 +119,12 @@ function Store::create_frontend%(id: string%): opaque of Store::Handle return rval; %} +## Close a data store. +## +## h: a data store handle. +## +## Returns: true if store was valid and is now closed. The handle can no +## longer be used for data store operations. function Store::close_by_handle%(h: opaque of Store::Handle%): bool %{ auto handle = static_cast(h); @@ -96,6 +140,17 @@ function Store::close_by_handle%(h: opaque of Store::Handle%): bool # non-blocking update API # ########################### +## Insert a key-value pair in to the store. +## +## h: the handle of the store to modify. +## +## k: the key to insert. +## +## v: the value to insert. +## +## e: the expiration time of the key-value pair. +## +## Returns: false if the store handle was not valid. function Store::insert%(h: opaque of Store::Handle, k: Comm::Data, v: Comm::Data, e: Store::ExpiryTime &default = Store::ExpiryTime()%): bool @@ -134,6 +189,13 @@ function Store::insert%(h: opaque of Store::Handle, return new Val(true, TYPE_BOOL); %} +## Remove a key-value pair from the store. +## +## h: the handle of the store to modify. +## +## k: the key to remove. +## +## Returns: false if the store handle was not valid. function Store::erase%(h: opaque of Store::Handle, k: Comm::Data%): bool %{ auto handle = static_cast(h); @@ -146,6 +208,11 @@ function Store::erase%(h: opaque of Store::Handle, k: Comm::Data%): bool return new Val(true, TYPE_BOOL); %} +## Remove all key-value pairs from the store. +## +## h: the handle of the store to modify. +## +## Returns: false if the store handle was not valid. function Store::clear%(h: opaque of Store::Handle%): bool %{ auto handle = static_cast(h); @@ -157,6 +224,16 @@ function Store::clear%(h: opaque of Store::Handle%): bool return new Val(true, TYPE_BOOL); %} +## Increment an integer value in a data store. +## +## h: the handle of the store to modify. +## +## k: the key whose associated value is to be modified. +## +## by: the amount to increment the value by. A non-existent key will first +## create it with an implicit value of zero before incrementing. +## +## Returns: false if the store handle was not valid. function Store::increment%(h: opaque of Store::Handle, k: Comm::Data, by: int &default = +1%): bool %{ @@ -170,6 +247,16 @@ function Store::increment%(h: opaque of Store::Handle, return new Val(true, TYPE_BOOL); %} +## Decrement an integer value in a data store. +## +## h: the handle of the store to modify. +## +## k: the key whose associated value is to be modified. +## +## by: the amount to decrement the value by. A non-existent key will first +## create it with an implicit value of zero before decrementing. +## +## Returns: false if the store handle was not valid. function Store::decrement%(h: opaque of Store::Handle, k: Comm::Data, by: int &default = +1%): bool %{ @@ -183,6 +270,16 @@ function Store::decrement%(h: opaque of Store::Handle, return new Val(true, TYPE_BOOL); %} +## Add an element to a set value in a data store. +## +## h: the handle of the store to modify. +## +## k: the key whose associated value is to be modified. +## +## element: the element to add to the set. A non-existent key will first +## create it with an implicit empty set value before modifying. +## +## Returns: false if the store handle was not valid. function Store::add_to_set%(h: opaque of Store::Handle, k: Comm::Data, element: Comm::Data%): bool %{ @@ -197,6 +294,16 @@ function Store::add_to_set%(h: opaque of Store::Handle, return new Val(true, TYPE_BOOL); %} +## Remove an element from a set value in a data store. +## +## h: the handle of the store to modify. +## +## k: the key whose associated value is to be modified. +## +## element: the element to remove from the set. A non-existent key will +## implicitly create an empty set value associated with the key. +## +## Returns: false if the store handle was not valid. function Store::remove_from_set%(h: opaque of Store::Handle, k: Comm::Data, element: Comm::Data%): bool %{ @@ -211,6 +318,16 @@ function Store::remove_from_set%(h: opaque of Store::Handle, return new Val(true, TYPE_BOOL); %} +## Add a new item to the head of a vector value in a data store. +## +## h: the handle of store to modify. +## +## k: the key whose associated value is to be modified. +## +## item: the element to insert in to the vector. A non-existent key will first +## create empty vector value before modifying. +## +## Returns: the handle of store to modify. function Store::push_left%(h: opaque of Store::Handle, k: Comm::Data, items: Comm::DataVector%): bool %{ @@ -234,6 +351,16 @@ function Store::push_left%(h: opaque of Store::Handle, k: Comm::Data, return new Val(true, TYPE_BOOL); %} +## Add a new item to the tail of a vector value in a data store. +## +## h: the handle of store to modify. +## +## k: the key whose associated value is to be modified. +## +## item: the element to insert in to the vector. A non-existent key will first +## create empty vector value before modifying. +## +## Returns: the handle of store to modify. function Store::push_right%(h: opaque of Store::Handle, k: Comm::Data, items: Comm::DataVector%): bool %{ @@ -297,20 +424,23 @@ static bool prepare_for_query(Val* opaque, Frame* frame, *cb = new comm::StoreQueryCallback(trigger, frame->GetCall(), (*handle)->store->id(), (*handle)->store_type); - comm_mgr->TrackStoreQuery(*cb); + comm_mgr->TrackStoreQuery(*cb); return true; } %%} +## Pop the head of a data store vector value. +## +## h: the handle of the store to query. +## +## k: the key associated with the vector to modify. +## +## Returns: the result of the query. function Store::pop_left%(h: opaque of Store::Handle, k: Comm::Data%): Store::QueryResult %{ - double timeout; - comm::StoreQueryCallback* cb; - comm::StoreHandleVal* handle; - - if ( ! prepare_for_query(h, frame, &handle, &timeout, &cb) ) + if ( ! comm_mgr->Enabled() ) return comm::query_result(); Val* key = k->AsRecordVal()->Lookup(0); @@ -318,19 +448,29 @@ function Store::pop_left%(h: opaque of Store::Handle, if ( ! key ) return comm::query_result(); + double timeout; + comm::StoreQueryCallback* cb; + comm::StoreHandleVal* handle; + + if ( ! prepare_for_query(h, frame, &handle, &timeout, &cb) ) + return comm::query_result(); + handle->store->pop_left(static_cast(key)->data, std::chrono::duration(timeout), cb); return 0; %} +## Pop the tail of a data store vector value. +## +## h: the handle of the store to query. +## +## k: the key associated with the vector to modify. +## +## Returns: the result of the query. function Store::pop_right%(h: opaque of Store::Handle, k: Comm::Data%): Store::QueryResult %{ - double timeout; - comm::StoreQueryCallback* cb; - comm::StoreHandleVal* handle; - - if ( ! prepare_for_query(h, frame, &handle, &timeout, &cb) ) + if ( ! comm_mgr->Enabled() ) return comm::query_result(); Val* key = k->AsRecordVal()->Lookup(0); @@ -338,19 +478,29 @@ function Store::pop_right%(h: opaque of Store::Handle, if ( ! key ) return comm::query_result(); + double timeout; + comm::StoreQueryCallback* cb; + comm::StoreHandleVal* handle; + + if ( ! prepare_for_query(h, frame, &handle, &timeout, &cb) ) + return comm::query_result(); + handle->store->pop_right(static_cast(key)->data, std::chrono::duration(timeout), cb); return 0; %} +## Lookup the value associated with a key in a data store. +## +## h: the handle of the store to query. +## +## k: the key to lookup. +## +## Returns: the result of the query. function Store::lookup%(h: opaque of Store::Handle, k: Comm::Data%): Store::QueryResult %{ - double timeout; - comm::StoreQueryCallback* cb; - comm::StoreHandleVal* handle; - - if ( ! prepare_for_query(h, frame, &handle, &timeout, &cb) ) + if ( ! comm_mgr->Enabled() ) return comm::query_result(); Val* key = k->AsRecordVal()->Lookup(0); @@ -358,19 +508,29 @@ function Store::lookup%(h: opaque of Store::Handle, if ( ! key ) return comm::query_result(); + double timeout; + comm::StoreQueryCallback* cb; + comm::StoreHandleVal* handle; + + if ( ! prepare_for_query(h, frame, &handle, &timeout, &cb) ) + return comm::query_result(); + handle->store->lookup(static_cast(key)->data, std::chrono::duration(timeout), cb); return 0; %} +## Check if a data store contains a given key. +## +## h: the handle of the store to query. +## +## k: the key to check for existence. +## +## Returns: the result of the query (uses :bro:see:`Comm::BOOL`). function Store::exists%(h: opaque of Store::Handle, k: Comm::Data%): Store::QueryResult %{ - double timeout; - comm::StoreQueryCallback* cb; - comm::StoreHandleVal* handle; - - if ( ! prepare_for_query(h, frame, &handle, &timeout, &cb) ) + if ( ! comm_mgr->Enabled() ) return comm::query_result(); Val* key = k->AsRecordVal()->Lookup(0); @@ -378,11 +538,23 @@ function Store::exists%(h: opaque of Store::Handle, if ( ! key ) return comm::query_result(); + double timeout; + comm::StoreQueryCallback* cb; + comm::StoreHandleVal* handle; + + if ( ! prepare_for_query(h, frame, &handle, &timeout, &cb) ) + return comm::query_result(); + handle->store->exists(static_cast(key)->data, std::chrono::duration(timeout), cb); return 0; %} +## Retrieve all keys in a data store. +## +## h: the handle of the store to query. +## +## Returns: the result of the query (uses :bro:see:`Comm::VECTOR`). function Store::keys%(h: opaque of Store::Handle%): Store::QueryResult %{ double timeout; @@ -396,8 +568,16 @@ function Store::keys%(h: opaque of Store::Handle%): Store::QueryResult return 0; %} +## Get the number of key-value pairs in a data store. +## +## h: the handle of the store to query. +## +## Returns: the result of the query (uses :bro:see:`Comm::COUNT`). function Store::size%(h: opaque of Store::Handle%): Store::QueryResult %{ + if ( ! comm_mgr->Enabled() ) + return comm::query_result(); + double timeout; comm::StoreQueryCallback* cb; comm::StoreHandleVal* handle; diff --git a/src/logging/Manager.h b/src/logging/Manager.h index 8130a1ddd4..5d3372fb9b 100644 --- a/src/logging/Manager.h +++ b/src/logging/Manager.h @@ -158,12 +158,30 @@ public: void Terminate(); #ifdef ENABLE_BROKER + /** + * Enable remote logs for a given stream. + * @param stream_id the stream to enable remote logs for. + * @param flags tune behavior of how log entries are sent to peer endpoints. + * @return true if remote logs are enabled. + */ bool EnableRemoteLogs(EnumVal* stream_id, int flags); + /** + * Disable remote logs for a given stream. + * @param stream_id the stream to disable remote logs for. + * @return true if remote logs are disabled. + */ bool DisableRemoteLogs(EnumVal* stream_id); + /** + * @return true if remote logs are enabled for a given stream. + */ bool RemoteLogsAreEnabled(EnumVal* stream_id); + /** + * @return the type which corresponds to the columns in a log entry for + * a given log stream. + */ RecordType* StreamColumns(EnumVal* stream_id); #endif From 9025b425344c150b5c06431b63cf3995c60916bb Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Tue, 17 Feb 2015 12:56:36 -0800 Subject: [PATCH 160/299] Updating submodule. --- aux/bro-aux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aux/bro-aux b/aux/bro-aux index 8c37b26823..fa145348ab 160000 --- a/aux/bro-aux +++ b/aux/bro-aux @@ -1 +1 @@ -Subproject commit 8c37b26823ada9c77614b2f8f781c11c8fe3d078 +Subproject commit fa145348abe15dcd5f8e52cc96e1fb758c092e36 From 818ba9127f4856f1e83c5e42eba0117adfeafdbe Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Tue, 17 Feb 2015 13:59:21 -0800 Subject: [PATCH 161/299] Update submodules. --- aux/bro-aux | 2 +- cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aux/bro-aux b/aux/bro-aux index fa145348ab..c409d529cf 160000 --- a/aux/bro-aux +++ b/aux/bro-aux @@ -1 +1 @@ -Subproject commit fa145348abe15dcd5f8e52cc96e1fb758c092e36 +Subproject commit c409d529cf0c3fa851d4720914badb04d0c6b7c2 diff --git a/cmake b/cmake index 9623367210..5e4e3507e2 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 962336721040fdf55a6b264f8bbc84153b54d9a5 +Subproject commit 5e4e3507e280c393778fd55fb0124217067e0078 From 093d4069206b9ae7ced009241cdd8e70aec1c4ec Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Tue, 17 Feb 2015 14:03:05 -0800 Subject: [PATCH 162/299] Updating plugin docs to recent changes. --- doc/devel/plugins.rst | 97 ++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 42 deletions(-) diff --git a/doc/devel/plugins.rst b/doc/devel/plugins.rst index c703345891..66ffba101f 100644 --- a/doc/devel/plugins.rst +++ b/doc/devel/plugins.rst @@ -3,7 +3,7 @@ Writing Bro Plugins =================== -Bro is internally moving to a plugin structure that enables extending +Bro internally provides plugin API that enables extending the system dynamically, without modifying the core code base. That way custom code remains self-contained and can be maintained, compiled, and installed independently. Currently, plugins can add the following @@ -42,13 +42,13 @@ certain structure. To get started, Bro's distribution provides a helper script ``aux/bro-aux/plugin-support/init-plugin`` that creates a skeleton plugin that can then be customized. Let's use that:: - # mkdir rot13-plugin - # cd rot13-plugin - # init-plugin Demo Rot13 + # init-plugin ./rot13-plugin Demo Rot13 -As you can see the script takes two arguments. The first is a -namespace the plugin will live in, and the second a descriptive name -for the plugin itself. Bro uses the combination of the two to identify +As you can see the script takes three arguments. The first is a +directory inside which the plugin skeleton will be create; it +shouldn't exist it. The second is namespace the plugin will live in, +and the third a descriptive name for the plugin itself relative to the +namespace. Bro uses the combination of namespace and name to identify a plugin. The namespace serves to avoid naming conflicts between plugins written by independent developers; pick, e.g., the name of your organisation. The namespace ``Bro`` is reserved for functionality @@ -82,18 +82,22 @@ The syntax of this file is just like any other ``*.bif`` file; we won't go into it here. Now we can already compile our plugin, we just need to tell the -configure script put in place by ``init-plugin`` where the Bro source -tree is located (Bro needs to have been built there first):: +configure script that ``init-plugin`` put in place where the Bro +source tree is located (Bro needs to have been built there first):: + # cd rot13-plugin # ./configure --bro-dist=/path/to/bro/dist && make [... cmake output ...] -Now our ``rot13-plugin`` directory has everything that it needs -for Bro to recognize it as a dynamic plugin. Once we point Bro to it, -it will pull it in automatically, as we can check with the ``-N`` +This builds the plugin in a subdirectory ``build/``. In fact, that +subdirectory *becomes* the plugin: when ``make`` finishes, ``build/`` +has everything it needs for Bro to recognize it as a dynamic plugin. + +Let's try that. Once we point Bro to the ``build/`` directory, it will +pull in our new plugin automatically, as we can check with the ``-N`` option:: - # export BRO_PLUGIN_PATH=/path/to/rot13-plugin + # export BRO_PLUGIN_PATH=/path/to/rot13-plugin/build # bro -N [...] Plugin: Demo::Rot13 - (dynamic, version 1) @@ -153,24 +157,28 @@ Once we install it, it works again:: The installed version went into ``/lib/bro/plugins/Demo_Rot13``. -We can distribute the plugin in either source or binary form by using -the Makefile's ``sdist`` and ``bdist`` target, respectively. Both -create corrsponding tarballs:: +One can distribute the plugin independently of Bro for others to use. +To distribute in source form, just remove the ``build/`` (``make +distclean`` does that) and then tar up the whole ``rot13-plugin/`` +directory. Others then follow the same process as above after +unpacking. To distribute the plugin in binary form, the build process +conviniently creates a corresponding tarball in ``build/dist/``. In +this case, it's called ``Demo_Rot13-0.1.tar.gz``, with the version +number coming out of the ``VERSION`` file that ``init-plugin`` put +into place. The binary tarball has everything needed to run the +plugin, but no further source files. Optionally, one can include +further files by specifying them in the plugin's ``CMakeLists.txt`` +through the ``bro_plugin_dist_files`` macro; the skeleton does that +for ``README``, ``VERSION``, ``CHANGES``, and ``COPYING``. To use the +plugin through the binary tarball, just unpack it and point +``BRO_PLUGIN_PATH`` there; or copy it into +``/lib/bro/plugins/`` directly. - # make sdist - [...] - Source distribution in build/sdist/Demo_Rot13.tar.gz - - # make bdist - [...] - Binary distribution in build/Demo_Rot13-darwin-x86_64.tar.gz - -The source archive will contain everything in the plugin directory -except any generated files. The binary archive will contain anything -needed to install and run the plugin, i.e., just what ``make install`` -puts into place as well. As the binary distribution is -platform-dependent, its name includes the OS and architecture the -plugin was built on. +Before distributing your plugin, you should edit some of the meta +files that ``init-plugin`` puts in place. Edit ``README`` and +``VERSION``, and update ``CHANGES`` when you make changes. Also put a +license file in place as ``COPYING``; if BSD is fine, you find a +template in ``COPYING.edit-me``. Plugin Directory Layout ======================= @@ -179,7 +187,7 @@ A plugin's directory needs to follow a set of conventions so that Bro (1) recognizes it as a plugin, and (2) knows what to load. While ``init-plugin`` takes care of most of this, the following is the full story. We'll use ```` to represent a plugin's top-level -directory. +directory. With the skeleton, ```` corresponds to ``build/``. ``/__bro_plugin__`` A file that marks a directory as containing a Bro plugin. The file @@ -205,6 +213,8 @@ directory. Directory with auto-generated Bro scripts that declare the plugin's bif elements. The files here are produced by ``bifcl``. +Any other files in ```` are ignored by Bro. + By convention, a plugin should put its custom scripts into sub folders of ``scripts/``, i.e., ``scripts//

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 literal 0 HcmV?d00001 From c268898e046008699602b5480e59e7d844308aeb Mon Sep 17 00:00:00 2001 From: Josh Liburdi Date: Sat, 14 Feb 2015 14:01:46 -0800 Subject: [PATCH 137/299] Add btest for FreeRDP pcap sample (NLA authentication) https://github.com/FreeRDP/FreeRDP/wiki/Network-Level-Authentication --- .../btest/Traces/rdp/nla_win7_win2k8r2.pcap | Bin 0 -> 134982 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 testing/btest/Traces/rdp/nla_win7_win2k8r2.pcap diff --git a/testing/btest/Traces/rdp/nla_win7_win2k8r2.pcap b/testing/btest/Traces/rdp/nla_win7_win2k8r2.pcap new file mode 100644 index 0000000000000000000000000000000000000000..3a6a2999eba4fa36f1655a95464494db44c8bede GIT binary patch literal 134982 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 e_S;kcZG8CMef3`TrFsiUL3u2o@ Date: Sun, 15 Feb 2015 09:24:28 -0800 Subject: [PATCH 138/299] Removed debug code for SSL --- scripts/base/protocols/rdp/main.bro | 67 ++--------- src/analyzer/protocol/rdp/RDP.cc | 3 - src/analyzer/protocol/rdp/events.bif | 33 +----- src/analyzer/protocol/rdp/rdp-analyzer.pac | 92 +++++---------- src/analyzer/protocol/rdp/rdp-protocol.pac | 125 +++++++-------------- 5 files changed, 80 insertions(+), 240 deletions(-) diff --git a/scripts/base/protocols/rdp/main.bro b/scripts/base/protocols/rdp/main.bro index 94aa26b6ec..0369cad3d4 100644 --- a/scripts/base/protocols/rdp/main.bro +++ b/scripts/base/protocols/rdp/main.bro @@ -14,7 +14,7 @@ export { id: conn_id &log; ## Cookie value used by the client machine. ## This is typically a username. - cookie: string &log &optional; + cookie: string &log &optional; ## Keyboard layout (language) of the client machine. keyboard_layout: string &log &optional; ## RDP client version used by the client machine. @@ -23,11 +23,8 @@ export { client_hostname: string &log &optional; ## Product ID of the client machine. client_product_id: string &log &optional; - ## Name of the server. - server_name: vector of string &log &optional; - ## Authentication result for the connection. This value is extracted from the payload for native authentication. - ## TODO: Perform heuristic authentication determination for NLA. - authentication_result: string &log &optional; + ## GCC result for the connection. This value is extracted from the payload for native encryption. + result: string &log &optional; ## Encryption level of the connection. encryption_level: string &log &optional; ## Encryption method of the connection. @@ -36,12 +33,6 @@ export { done: bool &default=F; }; - ## Variable to track if NTLM authentication is used. - global ntlm = F; - - ## Size in bytes of data sent by the server at which the RDP connection is presumed to be successful (NTLM authentication only). - const authentication_data_size = 1000 &redef; - ## Event that can be handled to access the rdp record as it is sent on ## to the loggin framework. global log_rdp: event(rec: Info); @@ -66,17 +57,6 @@ function rdp_done(c: connection, done: bool) { c$rdp$done = T; - # Not currently implemented -# if ( ntlm && use_conn_size_analyzer ) -# { -# if ( c$resp$size > authentication_data_size ) -# c$rdp$authentication_result = "Success (H)"; -# else c$rdp$authentication_result = "Undetermined"; -# } - - if ( c$rdp?$authentication_result && ( ! c$rdp?$encryption_method || ! c$rdp?$encryption_level ) ) - Reporter::error(fmt("Error parsing RDP security data in connection %s",c$uid)); - Log::write(RDP::LOG, c$rdp); skip_further_processing(c$id); set_record_packets(c$id, F); @@ -110,7 +90,7 @@ event rdp_tracker(c: connection) } } - # schedule the event to run again if necessary + # Schedule the event to run again if necessary schedule +5secs { rdp_tracker(c) }; } @@ -130,7 +110,7 @@ event connection_state_remove(c: connection) &priority=-5 rdp_done(c,T); } -event rdp_native_client_request(c: connection, cookie: string) &priority=5 +event rdp_client_request(c: connection, cookie: string) &priority=5 { if ( "Cookie" in clean(cookie) ) { @@ -142,7 +122,7 @@ event rdp_native_client_request(c: connection, cookie: string) &priority=5 } } -event rdp_native_client_info(c: connection, keyboard_layout: count, build: count, hostname: string, product_id: string) &priority=5 +event rdp_client_data(c: connection, keyboard_layout: count, build: count, hostname: string, product_id: string) &priority=5 { set_session(c); c$rdp$keyboard_layout = languages[keyboard_layout]; @@ -153,15 +133,15 @@ event rdp_native_client_info(c: connection, keyboard_layout: count, build: count schedule +5secs { rdp_tracker(c) }; } -event rdp_native_authentication(c: connection, result: count) &priority=5 +event rdp_result(c: connection, result: count) &priority=5 { set_session(c); - c$rdp$authentication_result = results[result]; + c$rdp$result = results[result]; schedule +5secs { rdp_tracker(c) }; } -event rdp_native_server_security(c: connection, encryption_method: count, encryption_level: count, random: string, certificate: string) &priority=5 +event rdp_server_security(c: connection, encryption_method: count, encryption_level: count) &priority=5 { set_session(c); c$rdp$encryption_method = encryption_methods[encryption_method]; @@ -169,32 +149,3 @@ event rdp_native_server_security(c: connection, encryption_method: count, encryp schedule +5secs { rdp_tracker(c) }; } - -event rdp_ntlm_client_request(c: connection, server: string) &priority=5 - { - set_session(c); - ntlm = T; - - if ( ! c$rdp?$server_name ) - c$rdp$server_name = vector(); - c$rdp$server_name[|c$rdp$server_name|] = server; - - schedule +5secs { rdp_tracker(c) }; - } - -event rdp_ntlm_server_response(c: connection, server: string) &priority=5 - { - set_session(c); - ntlm = T; - - if ( ! c$rdp?$server_name ) - c$rdp$server_name = vector(); - c$rdp$server_name[|c$rdp$server_name|] = server; - - schedule +5secs { rdp_tracker(c) }; - } - -event rdp_debug(c: connection, remainder: string) - { - Reporter::error(fmt("Debug RDP data generated in connection %s: %s",c$uid,remainder)); - } diff --git a/src/analyzer/protocol/rdp/RDP.cc b/src/analyzer/protocol/rdp/RDP.cc index 70cad773fe..aca184d844 100644 --- a/src/analyzer/protocol/rdp/RDP.cc +++ b/src/analyzer/protocol/rdp/RDP.cc @@ -1,9 +1,6 @@ #include "RDP.h" - #include "analyzer/protocol/tcp/TCP_Reassembler.h" - #include "Reporter.h" - #include "events.bif.h" using namespace analyzer::rdp; diff --git a/src/analyzer/protocol/rdp/events.bif b/src/analyzer/protocol/rdp/events.bif index dad76f801b..65917e98be 100644 --- a/src/analyzer/protocol/rdp/events.bif +++ b/src/analyzer/protocol/rdp/events.bif @@ -1,23 +1,9 @@ -## Generated for client-to-server RDP requests when NTLM authentication is used. -## -## c: The connection record for the underlying transport-layer session/flow. -## -## server: The RDP server name requested by the client. -event rdp_ntlm_client_request%(c: connection, server: string%); - -## Generated for server-to-client RDP responses when NTLM authentication is used. -## -## c: The connection record for the underlying transport-layer session/flow. -## -## server: The RDP server name responsed by the server. -event rdp_ntlm_server_response%(c: connection, server: string%); - ## Generated for X.224 client requests when native RDP encryption is used. ## ## c: The connection record for the underlying transport-layer session/flow. ## ## cookie: The cookie included in the request. -event rdp_native_client_request%(c: connection, cookie: string%); +event rdp_client_request%(c: connection, cookie: string%); ## Generated for MCS client requests when native RDP encryption is used. ## @@ -30,14 +16,14 @@ event rdp_native_client_request%(c: connection, cookie: string%); ## hostname: The hostname of the client machine (optional). ## ## product_id: The product ID of the client machine (optional). -event rdp_native_client_info%(c: connection, keyboard_layout: count, build: count, hostname: string, product_id: string%); +event rdp_client_data%(c: connection, keyboard_layout: count, build: count, hostname: string, product_id: string%); ## Generated for MCS server responses when native RDP encryption is used. ## ## c: The connection record for the underlying transport-layer session/flow. ## ## result: The 8-bit integer representing the GCC Conference Create Response result. -event rdp_native_authentication%(c: connection, result: count%); +event rdp_result%(c: connection, result: count%); ## Generated for MCS server responses when native RDP encryption is used. ## @@ -46,15 +32,4 @@ event rdp_native_authentication%(c: connection, result: count%); ## encryption_method: The 32-bit integer representing the encryption method used in the connection. ## ## encryption_level: The 32-bit integer representing the encryption level used in the connection. -## -## random: The random value used to derive session keys (optional). -## -## certificate: The certificate containing the server's public key information. -event rdp_native_server_security%(c: connection, encryption_method: count, encryption_level: count, random: string, certificate: string%); - -## Generated for unknown elements in RDP connections. Used for debugging and development purposes only. -## -## c: The connection record for the underlying transport-layer session/flow. -## -## remainder: The data to be debugged. -event rdp_debug%(c: connection, remainder: string%); +event rdp_server_security%(c: connection, encryption_method: count, encryption_level: count%); diff --git a/src/analyzer/protocol/rdp/rdp-analyzer.pac b/src/analyzer/protocol/rdp/rdp-analyzer.pac index f95ff9f589..04d64409bd 100644 --- a/src/analyzer/protocol/rdp/rdp-analyzer.pac +++ b/src/analyzer/protocol/rdp/rdp-analyzer.pac @@ -1,101 +1,59 @@ refine flow RDP_Flow += { - function proc_rdp_debug(debug: Debug): bool - %{ - BifEvent::generate_rdp_debug(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - bytestring_to_val(${debug.remainder})); + function proc_rdp_client_request(client_request: ClientRequest): bool + %{ + BifEvent::generate_rdp_client_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${client_request.cookie})); return true; %} - function proc_rdp_ntlm_server_response(ntlm_server: NTLMServerResponse): bool + function proc_rdp_result(gcc_response: GCC_Server_CreateResponse): bool %{ - BifEvent::generate_rdp_ntlm_server_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - bytestring_to_val(${ntlm_server.server_name})); - - return true; - %} - - function proc_rdp_ntlm_client_request(ntlm_client: NTLMClientRequest): bool - %{ - BifEvent::generate_rdp_ntlm_client_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - bytestring_to_val(${ntlm_client.server_name})); - - return true; - %} - - function proc_rdp_native_client_request(client_request: ClientRequest): bool - %{ - BifEvent::generate_rdp_native_client_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - bytestring_to_val(${client_request.cookie})); - - return true; - %} - - - function proc_rdp_native_authentication(gcc_response: GCC_Server_CreateResponse): bool - %{ - BifEvent::generate_rdp_native_authentication(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - ${gcc_response.result}); + BifEvent::generate_rdp_result(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + ${gcc_response.result}); return true; %} - function proc_rdp_native_client_info(ccore: ClientCore): bool + function proc_rdp_client_data(ccore: ClientCore): bool %{ - BifEvent::generate_rdp_native_client_info(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - ${ccore.keyboard_layout}, - ${ccore.client_build}, - bytestring_to_val(${ccore.client_name}), - bytestring_to_val(${ccore.dig_product_id})); + BifEvent::generate_rdp_client_data(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + ${ccore.keyboard_layout}, + ${ccore.client_build}, + bytestring_to_val(${ccore.client_name}), + bytestring_to_val(${ccore.dig_product_id})); return true; %} - function proc_rdp_native_server_security(ssd: ServerSecurityData): bool + function proc_rdp_server_security(ssd: ServerSecurityData): bool %{ - BifEvent::generate_rdp_native_server_security(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - ${ssd.encryption_method}, - ${ssd.encryption_level}, - bytestring_to_val(${ssd.server_random}), - bytestring_to_val(${ssd.server_certificate})); + BifEvent::generate_rdp_server_security(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + ${ssd.encryption_method}, + ${ssd.encryption_level}); return true; %} }; -refine typeattr Debug += &let { - proc: bool = $context.flow.proc_rdp_debug(this); -}; - -refine typeattr NTLMServerResponse += &let { - proc: bool = $context.flow.proc_rdp_ntlm_server_response(this); -}; - -refine typeattr NTLMClientRequest += &let { - proc: bool = $context.flow.proc_rdp_ntlm_client_request(this); -}; - refine typeattr ClientRequest += &let { - proc: bool = $context.flow.proc_rdp_native_client_request(this); + proc: bool = $context.flow.proc_rdp_client_request(this); }; refine typeattr ClientCore += &let { - proc: bool = $context.flow.proc_rdp_native_client_info(this); + proc: bool = $context.flow.proc_rdp_client_data(this); }; refine typeattr GCC_Server_CreateResponse += &let { - proc: bool = $context.flow.proc_rdp_native_authentication(this); + proc: bool = $context.flow.proc_rdp_result(this); }; refine typeattr ServerSecurityData += &let { - proc: bool = $context.flow.proc_rdp_native_server_security(this); + proc: bool = $context.flow.proc_rdp_server_security(this); }; diff --git a/src/analyzer/protocol/rdp/rdp-protocol.pac b/src/analyzer/protocol/rdp/rdp-protocol.pac index ea68040314..d9546dbdc9 100644 --- a/src/analyzer/protocol/rdp/rdp-protocol.pac +++ b/src/analyzer/protocol/rdp/rdp-protocol.pac @@ -1,8 +1,8 @@ type RDP_PDU(is_orig: bool) = record { - type: uint16; + type: uint8; switch: case type of { - 0x1603 -> ntlm_authentication: NTLMAuthentication; # NTLM authentication appears to be flagged by this 16-bit integer - default -> native_encryption: NativeEncryption; # assume native encryption, this should be the value of the TPKT version + 0x16 -> ssl_encryption: bytestring &restofdata &transient; # send to SSL analyzer in the future + default -> native_encryption: NativeEncryption; # TPKT version }; } &byteorder=bigendian; @@ -11,8 +11,9 @@ type RDP_PDU(is_orig: bool) = record { ###################################################################### type NativeEncryption = record { - pad: padding[2]; # remaining TPKT values - cotp: COTP; + tpkt_reserved: uint8; + tpkt_length: uint16; + cotp: COTP; }; type COTP = record { @@ -20,12 +21,12 @@ type COTP = record { pdu: uint8; switch: case pdu of { 0xe0 -> cRequest: ClientRequest; - 0xf0 -> hdr: Header; + 0xf0 -> hdr: COTPHeader; default -> data: bytestring &restofdata &transient; }; } &byteorder=littleendian; -type Header = record { +type COTPHeader = record { tpdu_number: uint8; application_defined_type: uint8; # this begins a BER encoded multiple octet variant, but can be safely skipped application_type: uint8; # this is value for the BER encoded octet variant above @@ -36,6 +37,11 @@ type Header = record { }; } &byteorder=littleendian; +type DataHdr = record { + type: uint16; + length: uint16; +} &byteorder=littleendian; + ###################################################################### # Client X.224 ###################################################################### @@ -130,7 +136,7 @@ type ServerHeader = record { network_header: DataHdr; net_data: padding[network_header.length - 4]; # skip this data security_header: DataHdr; - security_data: ServerSecurityData; # there is some issue / bug where the length reported by the security header overruns the end of the packet + security_data: ServerSecurityData; }; type GCC_Server_ConnectionData = record { @@ -152,11 +158,6 @@ type GCC_Server_CreateResponse = record { user_data_value_length: uint16; }; -type DataHdr = record { - type: uint16; - length: uint16; -} &byteorder=littleendian; - type ServerCoreData = record { version_major: uint16; version_minor: uint16; @@ -174,83 +175,40 @@ type ServerSecurityData = record { server_random_length: uint32 &byteorder=littleendian; server_cert_length: uint32 &byteorder=littleendian; server_random: bytestring &length=server_random_length; - server_certificate: bytestring &length=server_cert_length-8; # arbitrarily cutting off 8 chars so the certificate doesn't overrun the end of the packet + server_certificate: ServerCertificate; }; -###################################################################### -# NTLM Authentication -###################################################################### - -type NTLMAuthentication = record { - type: uint16; - switch: case type of { # there may be further type bytes that need to be added to this switch - 0x0100 -> client_request: NTLMClientRequest; - 0x0300 -> client_request2: NTLMClientRequest; - 0x0103 -> server_response: NTLMServerResponse; - 0x0104 -> server_response2: NTLMServerResponse; - default -> data: bytestring &restofdata &transient; +type ServerCertificate = record { + cert_type: uint8; + switch: case cert_type of { + 0x01 -> proprietary: ServerProprietary; + 0x02 -> ssl: SSL; }; +} &byteorder=littleendian; + +type ServerProprietary = record { + cert_type: uint8[3]; # remainder of cert_type value + signature_algorithm: uint32; + key_algorithm: uint32; + public_key_blob_type: uint16; + public_key_blob_length: uint16; + public_key_blob: PublicKeyBlob &length=public_key_blob_length; + signature_blob_type: uint16; + signature_blob_length: uint16; + signature_blob: bytestring &length=signature_blob_length; }; -###################################################################### -# NTLM Client -###################################################################### - -type NTLMClientRequest = record { - payload_length: uint8; # total payload length - pad1: padding[3]; # arbitrary 3 bytes - remaining_length1: uint8; # remaining length of the payload - pad2: padding[36]; # arbitrary 36 bytes - unknown_length: uint8; # an unknown length value - unknown_value1: padding[unknown_length]; # arbitrary padding for the length value above - pad3: padding[3]; # arbitrary 3 bytes - remainder_length2: uint8; # remaining length of the payload - unknown: uint8; # this unknown field affects the length between here and the beginning of the requested server name - switch: case unknown of { - 0x00 -> case1: uint8[7]; # jump 7 bytes - 0xff -> case2: uint8[12]; # jump 12 bytes - default -> case3: Debug; # debug if an unknown value is seen - }; - server_length: uint8; - server_name: bytestring &length=server_length; - data: bytestring &restofdata &transient; +type PublicKeyBlob = record { + magic: bytestring &length=4; + key_length: uint32; + bit_length: uint32; + public_exponent: uint32; + modulus: bytestring &length=key_length; }; -###################################################################### -# NTLM Server -###################################################################### - -type NTLMServerResponse = record { - unknown_value1: uint8; # 1 variable byte - unknown_value2: uint8[3]; # 3 bytes that may be static - unknown_value3: uint8; # 1 variable byte - unknown_value4: uint8[2]; # 2 bytes that may be static - unknown_length1: uint8; # an unknown length value - pad1: padding[unknown_length1]; # arbitrary padding for the length value above - unknown_value5: uint8[3]; # 3 bytes that may be static - unknown_value6: uint8; # 1 variable byte - unknown_value7: uint8[3]; # 3 bytes that may be static - unknown_value8: uint8; # 1 variable byte - unknown_value9: uint8[7]; # 7 bytes that may be static - unknown_value10: uint8[16]; # 16 bytes that may be static - unknown_value11: uint8[16]; # 16 bytes that may be static - unknown_value12: uint8; # 1 variable byte - unknown_value13: uint8; # 1 byte that may be static - unknown_value14: uint8; # 1 variable byte - unknown_value15: uint8; # 1 byte that may be static - unknown_value16: uint8; # 1 variable byte - unknown_value17: uint8[6]; # 6 bytes that may be static - server_length: uint8; # length of server name - server_name: bytestring &length=server_length; # server name - data: bytestring &restofdata &transient; -} &byteorder=bigendian; - -###################################################################### -# Debugging -###################################################################### - -type Debug = record { - remainder: bytestring &restofdata; +type SSL = record { + pad1: padding[11]; + x509_cert: bytestring &restofdata &transient; # send to x509 analyzer }; ###################################################################### @@ -311,3 +269,4 @@ function binary_to_int64(bs: bytestring): int64 return rval; %} + From 0648dafa5456952caa10057757acad264518ed4f Mon Sep 17 00:00:00 2001 From: Josh Liburdi Date: Sun, 15 Feb 2015 10:08:31 -0800 Subject: [PATCH 139/299] Removed scheduling of rdp_tracker event in server response events --- scripts/base/protocols/rdp/main.bro | 49 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/scripts/base/protocols/rdp/main.bro b/scripts/base/protocols/rdp/main.bro index 0369cad3d4..6f6af9d4cf 100644 --- a/scripts/base/protocols/rdp/main.bro +++ b/scripts/base/protocols/rdp/main.bro @@ -14,23 +14,23 @@ export { id: conn_id &log; ## Cookie value used by the client machine. ## This is typically a username. - cookie: string &log &optional; + cookie: string &log &optional; ## Keyboard layout (language) of the client machine. - keyboard_layout: string &log &optional; + keyboard_layout: string &log &optional; ## RDP client version used by the client machine. - client_build: string &log &optional; + client_build: string &log &optional; ## Hostname of the client machine. - client_hostname: string &log &optional; + client_hostname: string &log &optional; ## Product ID of the client machine. - client_product_id: string &log &optional; - ## GCC result for the connection. This value is extracted from the payload for native encryption. - result: string &log &optional; + client_product_id: string &log &optional; + ## GCC result for the connection. + result: string &log &optional; ## Encryption level of the connection. - encryption_level: string &log &optional; + encryption_level: string &log &optional; ## Encryption method of the connection. - encryption_method: string &log &optional; + encryption_method: string &log &optional; ## Track status of logging RDP connections. - done: bool &default=F; + done: bool &default=F; }; ## Event that can be handled to access the rdp record as it is sent on @@ -38,6 +38,10 @@ export { global log_rdp: event(rec: Info); } +redef record connection += { + rdp: Info &optional; + }; + const ports = { 3389/tcp }; redef likely_server_ports += { ports }; @@ -47,9 +51,15 @@ event bro_init() &priority=5 Analyzer::register_for_ports(Analyzer::ANALYZER_RDP, ports); } -redef record connection += { - rdp: Info &optional; - }; +function set_session(c: connection) + { + if ( ! c?$rdp ) + { + c$rdp = [$ts=network_time(),$id=c$id,$uid=c$uid]; + # Need to do this manually because the DPD framework does not seem to register the protocol (even though DPD is working) + add c$service["rdp"]; + } + } function rdp_done(c: connection, done: bool) { @@ -94,15 +104,6 @@ event rdp_tracker(c: connection) schedule +5secs { rdp_tracker(c) }; } -function set_session(c: connection) - { - if ( ! c?$rdp ) - { - c$rdp = [$ts=network_time(),$id=c$id,$uid=c$uid]; - add c$service["rdp"]; - } - } - event connection_state_remove(c: connection) &priority=-5 { # Log the RDP connection if the connection is removed but the session has not been marked as done @@ -137,8 +138,6 @@ event rdp_result(c: connection, result: count) &priority=5 { set_session(c); c$rdp$result = results[result]; - - schedule +5secs { rdp_tracker(c) }; } event rdp_server_security(c: connection, encryption_method: count, encryption_level: count) &priority=5 @@ -146,6 +145,4 @@ event rdp_server_security(c: connection, encryption_method: count, encryption_le set_session(c); c$rdp$encryption_method = encryption_methods[encryption_method]; c$rdp$encryption_level = encryption_levels[encryption_level]; - - schedule +5secs { rdp_tracker(c) }; } From af1f4be5294a6b734723d0b118e92c69b4177973 Mon Sep 17 00:00:00 2001 From: Josh Liburdi Date: Sun, 15 Feb 2015 10:16:16 -0800 Subject: [PATCH 140/299] Added comments and TODOs --- scripts/base/protocols/rdp/main.bro | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/base/protocols/rdp/main.bro b/scripts/base/protocols/rdp/main.bro index 6f6af9d4cf..a1026208de 100644 --- a/scripts/base/protocols/rdp/main.bro +++ b/scripts/base/protocols/rdp/main.bro @@ -34,7 +34,7 @@ export { }; ## Event that can be handled to access the rdp record as it is sent on - ## to the loggin framework. + ## to the logging framework. global log_rdp: event(rec: Info); } @@ -56,7 +56,8 @@ function set_session(c: connection) if ( ! c?$rdp ) { c$rdp = [$ts=network_time(),$id=c$id,$uid=c$uid]; - # Need to do this manually because the DPD framework does not seem to register the protocol (even though DPD is working) + ## Need to do this manually because the DPD framework does not seem to register the protocol (even though DPD is working) + ## TODO: Find out why DPD framework isn't working add c$service["rdp"]; } } @@ -113,12 +114,14 @@ event connection_state_remove(c: connection) &priority=-5 event rdp_client_request(c: connection, cookie: string) &priority=5 { + ## Possibly better to avoid this clean up and use regex in binpac to extract the cookie value if ( "Cookie" in clean(cookie) ) { set_session(c); local cookie_val = sub(cookie,/Cookie.*\=/,""); c$rdp$cookie = sub(cookie_val,/\x0d\x0a.*$/,""); + ## Schedule the rdp_tracker event so remaining data can be collected schedule +5secs { rdp_tracker(c) }; } } @@ -131,6 +134,8 @@ event rdp_client_data(c: connection, keyboard_layout: count, build: count, hostn c$rdp$client_hostname = gsub(cat(hostname),/\\0/,""); c$rdp$client_product_id = gsub(cat(product_id),/\\0/,""); + ## Schedule the rdp_tracker event so remaining data can be collected + ## This is scheduled twice because the cookie in rdp_client_request may not exist schedule +5secs { rdp_tracker(c) }; } From a3ab9f5b09517d469383419f150896aa0fb29834 Mon Sep 17 00:00:00 2001 From: Josh Liburdi Date: Sun, 15 Feb 2015 10:18:52 -0800 Subject: [PATCH 141/299] Added comments and TODOs --- scripts/base/protocols/rdp/main.bro | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/base/protocols/rdp/main.bro b/scripts/base/protocols/rdp/main.bro index a1026208de..718fb3fe87 100644 --- a/scripts/base/protocols/rdp/main.bro +++ b/scripts/base/protocols/rdp/main.bro @@ -62,6 +62,8 @@ function set_session(c: connection) } } +## Currently rdp_done and rdp_tracker mimic the SSH analyzer for disabling analysis, but there might be a better method +## Once the DPD framework bug is fixed, we could possibly use the same method as SSL analyzer function rdp_done(c: connection, done: bool) { if ( done ) @@ -91,8 +93,8 @@ event rdp_tracker(c: connection) if ( connection_exists(id) ) { - # If the RDP connection has been alive for more than 5secs, log it - # This duration should be sufficient to collect the data that needs to be logged + ## If the RDP connection has been alive for more than 5secs, log it + ## This duration should be sufficient to collect the data that needs to be logged local diff = network_time() - c$rdp$ts; if ( diff > 5secs ) { @@ -101,13 +103,13 @@ event rdp_tracker(c: connection) } } - # Schedule the event to run again if necessary + ## Schedule the event to run again if necessary schedule +5secs { rdp_tracker(c) }; } event connection_state_remove(c: connection) &priority=-5 { - # Log the RDP connection if the connection is removed but the session has not been marked as done + ## Log the RDP connection if the connection is removed but the session has not been marked as done if ( c?$rdp && ! c$rdp$done ) rdp_done(c,T); } From 90bfbf900295c425ea548e1ec0fe237b301d9505 Mon Sep 17 00:00:00 2001 From: Josh Liburdi Date: Sun, 15 Feb 2015 22:43:31 -0800 Subject: [PATCH 142/299] Added comments, changed logging events to reduce analyzer errors --- scripts/base/protocols/rdp/main.bro | 165 ++++++++++++++++------------ 1 file changed, 94 insertions(+), 71 deletions(-) diff --git a/scripts/base/protocols/rdp/main.bro b/scripts/base/protocols/rdp/main.bro index 718fb3fe87..c4309e3686 100644 --- a/scripts/base/protocols/rdp/main.bro +++ b/scripts/base/protocols/rdp/main.bro @@ -29,10 +29,24 @@ export { encryption_level: string &log &optional; ## Encryption method of the connection. encryption_method: string &log &optional; + + ## The analyzer ID used for the analyzer instance attached + ## to each connection. It is not used for logging since it's a + ## meaningless arbitrary number. + analyzer_id: count &optional; ## Track status of logging RDP connections. done: bool &default=F; }; + ## If true, detach the RDP analyzer from the connection to prevent + ## continuing to process encrypted traffic. Helps with performance + ## (especially with large file transfers). + const disable_analyzer_after_detection = T &redef; + + ## The amount of time to monitor an RDP session from when it is first + ## identified. When this interval is reached, the session is logged. + const rdp_interval = 10secs &redef; + ## Event that can be handled to access the rdp record as it is sent on ## to the logging framework. global log_rdp: event(rec: Info); @@ -51,81 +65,71 @@ event bro_init() &priority=5 Analyzer::register_for_ports(Analyzer::ANALYZER_RDP, ports); } +# Verify that the RDP session contains +# RDP data before writing it to the log. +function verify_rdp(c: connection) + { + local info = c$rdp; + if ( info?$cookie || info?$keyboard_layout || info?$result ) + Log::write(RDP::LOG,info); + else + Reporter::error("RDP analyzer was initialized but no data was found"); + } + +event log_record(c: connection, remove_analyzer: bool) + { + # If the record was logged, then stop processing. + if ( c$rdp$done ) + return; + + # If the analyzer is no logger attached, then + # log the record and stop processing. + if ( ! remove_analyzer ) + { + c$rdp$done = T; + verify_rdp(c); + return; + } + + # If the value rdp_interval has passed since the + # RDP session was started, then log the record. + local diff = network_time() - c$rdp$ts; + if ( diff > rdp_interval ) + { + c$rdp$done = T; + verify_rdp(c); + + # Remove the analyzer if it is still attached. + if ( remove_analyzer && disable_analyzer_after_detection && c$rdp?$analyzer_id ) + { + disable_analyzer(c$id, c$rdp$analyzer_id); + delete c$rdp$analyzer_id; + } + + return; + } + # If the analyzer is attached and the duration + # to monitor the RDP session was not met, then + # reschedule the logging event. + else + schedule +rdp_interval { log_record(c,remove_analyzer) }; + } + function set_session(c: connection) { if ( ! c?$rdp ) - { + { c$rdp = [$ts=network_time(),$id=c$id,$uid=c$uid]; - ## Need to do this manually because the DPD framework does not seem to register the protocol (even though DPD is working) - ## TODO: Find out why DPD framework isn't working - add c$service["rdp"]; - } + # The RDP session is scheduled to be logged from + # the time it is first initiated. + schedule +rdp_interval { log_record(c,T) }; + } } -## Currently rdp_done and rdp_tracker mimic the SSH analyzer for disabling analysis, but there might be a better method -## Once the DPD framework bug is fixed, we could possibly use the same method as SSL analyzer -function rdp_done(c: connection, done: bool) - { - if ( done ) - { - c$rdp$done = T; - - Log::write(RDP::LOG, c$rdp); - skip_further_processing(c$id); - set_record_packets(c$id, F); - } - } - -event rdp_tracker(c: connection) - { - if ( c$rdp$done ) - return; - - local id = c$id; - - if ( ! connection_exists(id) ) - { - rdp_done(c,T); - return; - } - - lookup_connection(id); - - if ( connection_exists(id) ) - { - ## If the RDP connection has been alive for more than 5secs, log it - ## This duration should be sufficient to collect the data that needs to be logged - local diff = network_time() - c$rdp$ts; - if ( diff > 5secs ) - { - rdp_done(c,T); - return; - } - } - - ## Schedule the event to run again if necessary - schedule +5secs { rdp_tracker(c) }; - } - -event connection_state_remove(c: connection) &priority=-5 - { - ## Log the RDP connection if the connection is removed but the session has not been marked as done - if ( c?$rdp && ! c$rdp$done ) - rdp_done(c,T); - } - event rdp_client_request(c: connection, cookie: string) &priority=5 { - ## Possibly better to avoid this clean up and use regex in binpac to extract the cookie value - if ( "Cookie" in clean(cookie) ) - { - set_session(c); - local cookie_val = sub(cookie,/Cookie.*\=/,""); - c$rdp$cookie = sub(cookie_val,/\x0d\x0a.*$/,""); - - ## Schedule the rdp_tracker event so remaining data can be collected - schedule +5secs { rdp_tracker(c) }; - } + set_session(c); + c$rdp$cookie = cookie; } event rdp_client_data(c: connection, keyboard_layout: count, build: count, hostname: string, product_id: string) &priority=5 @@ -135,10 +139,6 @@ event rdp_client_data(c: connection, keyboard_layout: count, build: count, hostn c$rdp$client_build = builds[build]; c$rdp$client_hostname = gsub(cat(hostname),/\\0/,""); c$rdp$client_product_id = gsub(cat(product_id),/\\0/,""); - - ## Schedule the rdp_tracker event so remaining data can be collected - ## This is scheduled twice because the cookie in rdp_client_request may not exist - schedule +5secs { rdp_tracker(c) }; } event rdp_result(c: connection, result: count) &priority=5 @@ -153,3 +153,26 @@ event rdp_server_security(c: connection, encryption_method: count, encryption_le c$rdp$encryption_method = encryption_methods[encryption_method]; c$rdp$encryption_level = encryption_levels[encryption_level]; } + +event protocol_confirmation(c: connection, atype: Analyzer::Tag, aid: count) &priority=5 + { + if ( atype == Analyzer::ANALYZER_RDP ) + { + set_session(c); + c$rdp$analyzer_id = aid; + } + } + +event protocol_violation(c: connection, atype: Analyzer::Tag, aid: count, reason: string) &priority=5 + { + # If a protocol violation occurs, then log the record immediately. + if ( c?$rdp ) + schedule +0secs { log_record(c,F) }; + } + +event connection_state_remove(c: connection) &priority=-5 + { + # If the connection is removed, then log the record immediately. + if ( c?$rdp ) + schedule +0secs { log_record(c,F) }; + } From 0ef8a106df3f23d5946b7e934400dfcea472b6f4 Mon Sep 17 00:00:00 2001 From: Josh Liburdi Date: Sun, 15 Feb 2015 22:44:00 -0800 Subject: [PATCH 143/299] Moved DPD to each individual event process --- src/analyzer/protocol/rdp/rdp-analyzer.pac | 30 ++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/analyzer/protocol/rdp/rdp-analyzer.pac b/src/analyzer/protocol/rdp/rdp-analyzer.pac index 04d64409bd..28fb4afa6a 100644 --- a/src/analyzer/protocol/rdp/rdp-analyzer.pac +++ b/src/analyzer/protocol/rdp/rdp-analyzer.pac @@ -1,16 +1,18 @@ refine flow RDP_Flow += { - function proc_rdp_client_request(client_request: ClientRequest): bool + function proc_rdp_client_request(client_request: Client_Request): bool %{ - BifEvent::generate_rdp_client_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - bytestring_to_val(${client_request.cookie})); + connection()->bro_analyzer()->ProtocolConfirmation(); - return true; + BifEvent::generate_rdp_client_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${client_request.cookie_value})); + + return true; %} - - function proc_rdp_result(gcc_response: GCC_Server_CreateResponse): bool + function proc_rdp_result(gcc_response: GCC_Server_Create_Response): bool %{ + connection()->bro_analyzer()->ProtocolConfirmation(); BifEvent::generate_rdp_result(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), ${gcc_response.result}); @@ -19,8 +21,9 @@ refine flow RDP_Flow += { %} - function proc_rdp_client_data(ccore: ClientCore): bool + function proc_rdp_client_data(ccore: Client_Core_Data): bool %{ + connection()->bro_analyzer()->ProtocolConfirmation(); BifEvent::generate_rdp_client_data(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), ${ccore.keyboard_layout}, @@ -31,8 +34,9 @@ refine flow RDP_Flow += { return true; %} - function proc_rdp_server_security(ssd: ServerSecurityData): bool + function proc_rdp_server_security(ssd: Server_Security_Data): bool %{ + connection()->bro_analyzer()->ProtocolConfirmation(); BifEvent::generate_rdp_server_security(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), ${ssd.encryption_method}, @@ -42,18 +46,18 @@ refine flow RDP_Flow += { %} }; -refine typeattr ClientRequest += &let { +refine typeattr Client_Request += &let { proc: bool = $context.flow.proc_rdp_client_request(this); }; -refine typeattr ClientCore += &let { +refine typeattr Client_Core_Data += &let { proc: bool = $context.flow.proc_rdp_client_data(this); }; -refine typeattr GCC_Server_CreateResponse += &let { +refine typeattr GCC_Server_Create_Response += &let { proc: bool = $context.flow.proc_rdp_result(this); }; -refine typeattr ServerSecurityData += &let { +refine typeattr Server_Security_Data += &let { proc: bool = $context.flow.proc_rdp_server_security(this); }; From b1614b7fe9900f759bcdff193f6833633203073c Mon Sep 17 00:00:00 2001 From: Josh Liburdi Date: Sun, 15 Feb 2015 22:45:16 -0800 Subject: [PATCH 144/299] Modified how cookie value is handled --- src/analyzer/protocol/rdp/rdp-protocol.pac | 81 +++++++++++----------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/src/analyzer/protocol/rdp/rdp-protocol.pac b/src/analyzer/protocol/rdp/rdp-protocol.pac index d9546dbdc9..a89e622539 100644 --- a/src/analyzer/protocol/rdp/rdp-protocol.pac +++ b/src/analyzer/protocol/rdp/rdp-protocol.pac @@ -2,7 +2,7 @@ type RDP_PDU(is_orig: bool) = record { type: uint8; switch: case type of { 0x16 -> ssl_encryption: bytestring &restofdata &transient; # send to SSL analyzer in the future - default -> native_encryption: NativeEncryption; # TPKT version + default -> native_encryption: Native_Encryption; # TPKT version }; } &byteorder=bigendian; @@ -10,7 +10,7 @@ type RDP_PDU(is_orig: bool) = record { # Native Encryption ###################################################################### -type NativeEncryption = record { +type Native_Encryption = record { tpkt_reserved: uint8; tpkt_length: uint16; cotp: COTP; @@ -20,24 +20,24 @@ type COTP = record { length: uint8; pdu: uint8; switch: case pdu of { - 0xe0 -> cRequest: ClientRequest; - 0xf0 -> hdr: COTPHeader; + 0xe0 -> cRequest: Client_Request; + 0xf0 -> hdr: COTP_Header; default -> data: bytestring &restofdata &transient; }; } &byteorder=littleendian; -type COTPHeader = record { +type COTP_Header = record { tpdu_number: uint8; application_defined_type: uint8; # this begins a BER encoded multiple octet variant, but can be safely skipped application_type: uint8; # this is value for the BER encoded octet variant above - switch: case application_type of { - 0x65 -> cHeader: ClientHeader; # 0x65 is a client - 0x66 -> sHeader: ServerHeader; # 0x66 is a server - default -> data: bytestring &restofdata &transient; + switch: case application_type of { # this seems to cause a binpac exception error + 0x65 -> cHeader: Client_Header; # 0x65 is a client + 0x66 -> sHeader: Server_Header; # 0x66 is a server + default -> data: bytestring &restofdata; }; } &byteorder=littleendian; -type DataHdr = record { +type Data_Header = record { type: uint16; length: uint16; } &byteorder=littleendian; @@ -46,19 +46,20 @@ type DataHdr = record { # Client X.224 ###################################################################### -type ClientRequest = record { +type Client_Request = record { destination_reference: uint16; source_reference: uint16; flow_control: uint8; - cookie: bytestring &restofdata; # cookie value is a variable length field, so everything is captured + cookie_mstshash: RE/Cookie: mstshash\=/; # &check would be better here, but it is not implemented + cookie_value: RE/[^\x0d]*/; # the value is anything up to \x0d }; ###################################################################### # Client MCS ###################################################################### -type ClientHeader = record { - type_length: padding[3]; # BER encoded long variant, can be safely skipped for now +type Client_Header = record { + type_length: uint8[3]; # BER encoded long variant, can be safely skipped for now calling_domain_selector: ASN1OctetString; called_domain_selector: ASN1OctetString; upward_flag: ASN1Boolean; @@ -69,20 +70,20 @@ type ClientHeader = record { maximum_parameters: ASN1SequenceMeta; max_parameters_pad: padding[maximum_parameters.encoding.length]; user_data_length: uint32; # BER encoded OctetString and long variant, can be safely skipped for now - gcc_connection_data: GCC_Client_ConnectionData; - gcc_client_create_request: GCC_Client_CreateRequest; - core_header: DataHdr; - core_data: ClientCore; + gcc_connection_data: GCC_Client_Connection_Data; + gcc_client_create_request: GCC_Client_Create_Request; + core_header: Data_Header; + core_data: Client_Core_Data; remainder: bytestring &restofdata &transient; # everything after core_data can be discarded }; -type GCC_Client_ConnectionData = record { +type GCC_Client_Connection_Data = record { key_object_length: uint16; key_object: uint8[key_object_length]; connect_data_connect_pdu: uint16; } &byteorder=bigendian; -type GCC_Client_CreateRequest = record { +type GCC_Client_Create_Request = record { extension_bit: uint8; privileges: uint8; numeric_length: uint8; @@ -95,7 +96,7 @@ type GCC_Client_CreateRequest = record { user_data_value_length: uint16; }; -type ClientCore = record { +type Client_Core_Data = record { version_major: uint16; version_minor: uint16; desktop_width: uint16; @@ -122,30 +123,30 @@ type ClientCore = record { # Server MCS ###################################################################### -type ServerHeader = record { - type_length: padding[3]; # BER encoded long variant, can be safely skipped for now +type Server_Header = record { + type_length: uint8[3]; # BER encoded long variant, can be safely skipped for now connect_response_result: ASN1Enumerated; connect_response_called_id: ASN1Integer; connect_response_domain_parameters: ASN1SequenceMeta; domain_parameters_pad: padding[connect_response_domain_parameters.encoding.length]; # skip this data user_data_length: uint32; # BER encoded OctetString and long variant, can be safely skipped for now - gcc_connection_data: GCC_Server_ConnectionData; - gcc_create_response: GCC_Server_CreateResponse; - core_header: DataHdr; + gcc_connection_data: GCC_Server_Connection_Data; + gcc_create_response: GCC_Server_Create_Response; + core_header: Data_Header; core_data: padding[core_header.length - 4]; # skip this data - network_header: DataHdr; + network_header: Data_Header; net_data: padding[network_header.length - 4]; # skip this data - security_header: DataHdr; - security_data: ServerSecurityData; + security_header: Data_Header; + security_data: Server_Security_Data; }; -type GCC_Server_ConnectionData = record { +type GCC_Server_Connection_Data = record { key_object_length: uint16; key_object: uint8[key_object_length]; connect_data_connect_pdu: uint8; } &byteorder=bigendian; -type GCC_Server_CreateResponse = record { +type GCC_Server_Create_Response = record { extension_bit: uint8; node_id: uint8[2]; tag_length: uint8; @@ -158,47 +159,47 @@ type GCC_Server_CreateResponse = record { user_data_value_length: uint16; }; -type ServerCoreData = record { +type Server_Core_Data = record { version_major: uint16; version_minor: uint16; client_requested_protocols: uint32; }; -type ServerNetworkData = record { +type Server_Network_Data = record { mcs_channel_id: uint16; channel_count: uint16; }; -type ServerSecurityData = record { +type Server_Security_Data = record { encryption_method: uint32; encryption_level: uint32; server_random_length: uint32 &byteorder=littleendian; server_cert_length: uint32 &byteorder=littleendian; server_random: bytestring &length=server_random_length; - server_certificate: ServerCertificate; + server_certificate: Server_Certificate; }; -type ServerCertificate = record { +type Server_Certificate = record { cert_type: uint8; switch: case cert_type of { - 0x01 -> proprietary: ServerProprietary; + 0x01 -> proprietary: Server_Proprietary; 0x02 -> ssl: SSL; }; } &byteorder=littleendian; -type ServerProprietary = record { +type Server_Proprietary = record { cert_type: uint8[3]; # remainder of cert_type value signature_algorithm: uint32; key_algorithm: uint32; public_key_blob_type: uint16; public_key_blob_length: uint16; - public_key_blob: PublicKeyBlob &length=public_key_blob_length; + public_key_blob: Public_Key_Blob &length=public_key_blob_length; signature_blob_type: uint16; signature_blob_length: uint16; signature_blob: bytestring &length=signature_blob_length; }; -type PublicKeyBlob = record { +type Public_Key_Blob = record { magic: bytestring &length=4; key_length: uint32; bit_length: uint32; From 8a5bb0f6a7e0410098dcb2fbd7948ef98fefb8ed Mon Sep 17 00:00:00 2001 From: jshlbrd Date: Sun, 15 Feb 2015 23:04:31 -0800 Subject: [PATCH 145/299] Added check for connection existence Added a check for connection existence before trying to remove the RDP analyzer from a connection. --- scripts/base/protocols/rdp/main.bro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/base/protocols/rdp/main.bro b/scripts/base/protocols/rdp/main.bro index c4309e3686..1f120d1b98 100644 --- a/scripts/base/protocols/rdp/main.bro +++ b/scripts/base/protocols/rdp/main.bro @@ -100,7 +100,7 @@ event log_record(c: connection, remove_analyzer: bool) verify_rdp(c); # Remove the analyzer if it is still attached. - if ( remove_analyzer && disable_analyzer_after_detection && c$rdp?$analyzer_id ) + if ( remove_analyzer && disable_analyzer_after_detection && connection_exists(c$id) && c$rdp?$analyzer_id ) { disable_analyzer(c$id, c$rdp$analyzer_id); delete c$rdp$analyzer_id; From 10071ffddf7718e0bcb7cd2ae6bd560914c1ffd7 Mon Sep 17 00:00:00 2001 From: jshlbrd Date: Sun, 15 Feb 2015 23:05:11 -0800 Subject: [PATCH 146/299] Fixed typo --- scripts/base/protocols/rdp/main.bro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/base/protocols/rdp/main.bro b/scripts/base/protocols/rdp/main.bro index 1f120d1b98..2e3c3f8892 100644 --- a/scripts/base/protocols/rdp/main.bro +++ b/scripts/base/protocols/rdp/main.bro @@ -82,7 +82,7 @@ event log_record(c: connection, remove_analyzer: bool) if ( c$rdp$done ) return; - # If the analyzer is no logger attached, then + # If the analyzer is no longer attached, then # log the record and stop processing. if ( ! remove_analyzer ) { From dade1936be9e9a0dcc656587b57668326b663073 Mon Sep 17 00:00:00 2001 From: jshlbrd Date: Sun, 15 Feb 2015 23:06:36 -0800 Subject: [PATCH 147/299] Update dpd.sig --- scripts/base/protocols/rdp/dpd.sig | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/base/protocols/rdp/dpd.sig b/scripts/base/protocols/rdp/dpd.sig index 35aa8f9257..4ecb7999af 100644 --- a/scripts/base/protocols/rdp/dpd.sig +++ b/scripts/base/protocols/rdp/dpd.sig @@ -1,5 +1,3 @@ -# Generated by binpac_quickstart - signature dpd_rdp_client_request { ip-proto == tcp payload /.*Cookie: mstshash\=.*/ From 55a0b344af9a63e6b4fe2215587a9d24a4cb9910 Mon Sep 17 00:00:00 2001 From: jshlbrd Date: Sun, 15 Feb 2015 23:09:50 -0800 Subject: [PATCH 148/299] Delete nla_win7_win2k8r2.pcap --- .../btest/Traces/rdp/nla_win7_win2k8r2.pcap | Bin 134982 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 testing/btest/Traces/rdp/nla_win7_win2k8r2.pcap diff --git a/testing/btest/Traces/rdp/nla_win7_win2k8r2.pcap b/testing/btest/Traces/rdp/nla_win7_win2k8r2.pcap deleted file mode 100644 index 3a6a2999eba4fa36f1655a95464494db44c8bede..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 134982 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 e_S;kcZG8CMef3`TrFsiUL3u2o@ Date: Sun, 15 Feb 2015 23:10:05 -0800 Subject: [PATCH 149/299] Delete RDP-004.pcap --- testing/btest/Traces/rdp/RDP-004.pcap | Bin 142910 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 testing/btest/Traces/rdp/RDP-004.pcap diff --git a/testing/btest/Traces/rdp/RDP-004.pcap b/testing/btest/Traces/rdp/RDP-004.pcap deleted file mode 100644 index a26dd5637f82d2d4ad3d74b79c84814644c6ea53..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142910 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