flatbuffers serialization format
* hyperscan >= 4.4.0 from https://github.com/01org/hyperscan to build new
- the regex and sd_pattern rule options and hyperscan search engine
+ the regex and sd_pattern rule options and hyperscan search engine.
+ Hyperscan is large so it recommended to follow their instructions for
+ building it as a shared library.
* iconv from https://ftp.gnu.org/pub/gnu/libiconv/ for converting
UTF16-LE filenames to UTF8 (usually included in glibc)
void show(snort::SnortConfig*) override { snort::LogMessage("Http2Inspect\n"); }
void eval(snort::Packet* p) override;
void clear(snort::Packet* p) override;
- void tinit() override { }
- void tterm() override { }
Http2StreamSplitter* get_splitter(bool is_client_to_server) override
{
return new Http2StreamSplitter(is_client_to_server);
}
}
octets_seen += length;
- return SCAN_NOTFOUND;
+ return SCAN_NOT_FOUND;
}
HttpStartCutter::ValidationResult HttpRequestCutter::validate(uint8_t octet, HttpInfractions*,
}
}
octets_seen += length;
- return SCAN_NOTFOUND;
+ return SCAN_NOT_FOUND;
}
ScanResult HttpBodyClCutter::cut(const uint8_t*, uint32_t length, HttpInfractions*,
// Are we skipping through the rest of this chunked body to the trailers and the next message?
const bool discard_mode = (flow_target == 0);
- if (new_section)
- {
- new_section = false;
- octets_seen = 0;
- num_good_chunks = 0;
- }
-
for (int32_t k=0; k < static_cast<int32_t>(length); k++)
{
switch (curr_state)
{
data_seen = 0;
num_flush = k+1;
- new_section = true;
return SCAN_FOUND_PIECE;
}
break;
{
data_seen = 0;
num_flush = k+1;
- new_section = true;
return SCAN_FOUND_PIECE;
}
break;
}
octets_seen += length;
- return SCAN_NOTFOUND;
+ return SCAN_NOT_FOUND;
}
virtual uint32_t get_num_head_lines() const { return 0; }
virtual bool get_is_broken_chunk() const { return false; }
virtual uint32_t get_num_good_chunks() const { return 0; }
+ virtual void soft_reset() {}
protected:
- // number of octets processed by previous cut() calls that returned NOTFOUND
+ // number of octets processed by previous cut() calls that returned NOT_FOUND
uint32_t octets_seen = 0;
uint32_t num_crlf = 0;
uint32_t num_flush = 0;
override;
bool get_is_broken_chunk() const override { return curr_state == HttpEnums::CHUNK_BAD; }
uint32_t get_num_good_chunks() const override { return num_good_chunks; }
+ void soft_reset() override { octets_seen = 0; num_good_chunks = 0; }
private:
uint32_t data_seen = 0;
uint32_t num_leading_ws = 0;
uint32_t num_zeros = 0;
uint32_t digits_seen = 0;
- bool new_section = false;
uint32_t num_good_chunks = 0; // that end in the current section
};
PEG_CONCURRENT_SESSIONS, PEG_MAX_CONCURRENT_SESSIONS, PEG_COUNT_MAX };
// Result of scanning by splitter
-enum ScanResult { SCAN_NOTFOUND, SCAN_FOUND, SCAN_FOUND_PIECE, SCAN_DISCARD, SCAN_DISCARD_PIECE,
+enum ScanResult { SCAN_NOT_FOUND, SCAN_FOUND, SCAN_FOUND_PIECE, SCAN_DISCARD, SCAN_DISCARD_PIECE,
SCAN_ABORT };
// State machine for chunk parsing
// The following methods are for convenience of debug and test output only!
uint64_t get_raw() const { return
- (events_generated & std::bitset<MAX>(0xFFFFFFFFFFFFFFFF)).to_ulong(); }
+ (events_generated & std::bitset<MAX>(0xFFFFFFFFFFFFFFFF)).to_ulong(); }
uint64_t get_raw2() const { return
- ((events_generated >> BASE_1XX_EVENTS) & std::bitset<MAX>(0xFFFFFFFFFFFFFFFF)).to_ulong(); }
+ ((events_generated >> BASE_1XX_EVENTS) & std::bitset<MAX>(0xFFFFFFFFFFFFFFFF)).to_ulong(); }
uint64_t get_raw3() const { return
- ((events_generated >> BASE_2XX_EVENTS) & std::bitset<MAX>(0xFFFFFFFFFFFFFFFF)).to_ulong(); }
+ ((events_generated >> BASE_2XX_EVENTS) & std::bitset<MAX>(0xFFFFFFFFFFFFFFFF)).to_ulong(); }
private:
static const unsigned BASE_1XX_EVENTS = 100;
static const unsigned BASE_2XX_EVENTS = 200;
public:
static const Field FIELD_NULL;
- Field(int32_t length, const uint8_t* start, bool own_the_buffer_ = false) : len(length),
- own_the_buffer(own_the_buffer_), strt(start) { assert(length <= HttpEnums::MAX_OCTETS); }
+ Field(int32_t length, const uint8_t* start, bool own_the_buffer_ = false) : strt(start),
+ len(length), own_the_buffer(own_the_buffer_) { assert(length <= HttpEnums::MAX_OCTETS); }
explicit Field(int32_t length) : len(length) { assert(length<=0); }
Field() = default;
~Field() { if (own_the_buffer) delete[] strt; }
private:
Field& operator=(const Field&) = delete;
+ const uint8_t* strt = nullptr;
int32_t len = HttpEnums::STAT_NOT_COMPUTE;
bool own_the_buffer = false;
- const uint8_t* strt = nullptr;
};
#endif
// 0 element refers to client request, 1 element refers to server response
- // FIXIT-P reorganize HttpFlowData to minimize void space
-
// *** StreamSplitter internal data - scan()
HttpCutter* cutter[2] = { nullptr, nullptr };
uint8_t* section_buffer[2] = { nullptr, nullptr };
uint32_t section_total[2] = { 0, 0 };
uint32_t section_offset[2] = { 0, 0 };
- HttpEnums::ChunkState chunk_state[2] = { HttpEnums::CHUNK_NEWLINES,
- HttpEnums::CHUNK_NEWLINES };
uint32_t chunk_expected_length[2] = { 0, 0 };
uint32_t running_total[2] = { 0, 0 };
+ HttpEnums::ChunkState chunk_state[2] = { HttpEnums::CHUNK_NEWLINES,
+ HttpEnums::CHUNK_NEWLINES };
// *** StreamSplitter internal data - scan() => reassemble()
uint32_t num_excess[2] = { 0, 0 };
- bool is_broken_chunk[2] = { false, false };
uint32_t num_good_chunks[2] = { 0, 0 };
uint32_t octets_expected[2] = { 0, 0 };
+ bool is_broken_chunk[2] = { false, false };
bool strict_length[2] = { false, false };
// *** StreamSplitter => Inspector (facts about the most recent message section)
HttpEnums::SectionType section_type[2] = { HttpEnums::SEC__NOT_COMPUTE,
HttpEnums::SEC__NOT_COMPUTE };
- bool tcp_close[2] = { false, false };
int32_t num_head_lines[2] = { HttpEnums::STAT_NOT_PRESENT, HttpEnums::STAT_NOT_PRESENT };
+ bool tcp_close[2] = { false, false };
bool zero_byte_workaround[2];
// Infractions and events are associated with a specific message and are stored in the
// *** Inspector => StreamSplitter (facts about the message section that is coming next)
HttpEnums::SectionType type_expected[2] = { HttpEnums::SEC_REQUEST, HttpEnums::SEC_STATUS };
// length of the data from Content-Length field
+ z_stream* compress_stream[2] = { nullptr, nullptr };
+ uint64_t zero_nine_expected = 0;
int64_t data_length[2] = { HttpEnums::STAT_NOT_PRESENT, HttpEnums::STAT_NOT_PRESENT };
uint32_t section_size_target[2] = { 0, 0 };
uint32_t section_size_max[2] = { 0, 0 };
HttpEnums::CompressId compression[2] = { HttpEnums::CMP_NONE, HttpEnums::CMP_NONE };
- z_stream* compress_stream[2] = { nullptr, nullptr };
- uint64_t zero_nine_expected = 0;
-
HttpEnums::DetectionStatus detection_status[2] = { HttpEnums::DET_ON, HttpEnums::DET_ON };
// *** Inspector's internal data about the current message
- HttpEnums::VersionId version_id[2] = { HttpEnums::VERS__NOT_PRESENT,
- HttpEnums::VERS__NOT_PRESENT };
- HttpEnums::MethodId method_id = HttpEnums::METH__NOT_PRESENT;
- int32_t status_code_num = HttpEnums::STAT_NOT_PRESENT;
- int64_t file_depth_remaining[2] = { HttpEnums::STAT_NOT_PRESENT,
- HttpEnums::STAT_NOT_PRESENT };
- int64_t detect_depth_remaining[2] = { HttpEnums::STAT_NOT_PRESENT,
- HttpEnums::STAT_NOT_PRESENT };
- snort::MimeSession* mime_state[2] = { nullptr, nullptr };
- snort::UtfDecodeSession* utf_state = nullptr; // SRC_SERVER only
- fd_session_t* fd_state = nullptr; // SRC_SERVER only
struct FdCallbackContext
{
HttpInfractions* infractions = nullptr;
HttpEventGen* events = nullptr;
};
FdCallbackContext fd_alert_context; // SRC_SERVER only
+ snort::MimeSession* mime_state[2] = { nullptr, nullptr };
+ snort::UtfDecodeSession* utf_state = nullptr; // SRC_SERVER only
+ fd_session_t* fd_state = nullptr; // SRC_SERVER only
+ int64_t file_depth_remaining[2] = { HttpEnums::STAT_NOT_PRESENT,
+ HttpEnums::STAT_NOT_PRESENT };
+ int64_t detect_depth_remaining[2] = { HttpEnums::STAT_NOT_PRESENT,
+ HttpEnums::STAT_NOT_PRESENT };
uint64_t expected_trans_num[2] = { 1, 1 };
- HttpMsgSection* latest_section = nullptr;
-
// number of user data octets seen so far (regular body or chunks)
int64_t body_octets[2] = { HttpEnums::STAT_NOT_PRESENT, HttpEnums::STAT_NOT_PRESENT };
+ int32_t status_code_num = HttpEnums::STAT_NOT_PRESENT;
+ HttpMsgSection* latest_section = nullptr;
+ HttpEnums::VersionId version_id[2] = { HttpEnums::VERS__NOT_PRESENT,
+ HttpEnums::VERS__NOT_PRESENT };
+ HttpEnums::MethodId method_id = HttpEnums::METH__NOT_PRESENT;
- // Transaction management including pipelining
- // FIXIT-L pipeline deserves to be its own class
- HttpTransaction* transaction[2] = { nullptr, nullptr };
+ // *** Transaction management including pipelining
static const int MAX_PIPELINE = 100; // requests seen - responses seen <= MAX_PIPELINE
+ HttpTransaction* transaction[2] = { nullptr, nullptr };
HttpTransaction** pipeline = nullptr;
- int pipeline_front = 0;
- int pipeline_back = 0;
+ int16_t pipeline_front = 0;
+ int16_t pipeline_back = 0;
bool pipeline_overflow = false;
bool pipeline_underflow = false;
void delete_pipeline();
#ifdef REG_TEST
- void show(FILE* out_file) const;
-
static uint64_t instance_count;
uint64_t seq_num;
+
+ void show(FILE* out_file) const;
#endif
};
void show(snort::SnortConfig*) override { snort::LogMessage("HttpInspect\n"); }
void eval(snort::Packet* p) override;
void clear(snort::Packet* p) override;
- void tinit() override { }
- void tterm() override { }
HttpStreamSplitter* get_splitter(bool is_client_to_server) override
{
return new HttpStreamSplitter(is_client_to_server, this);
void do_js_normalization(const Field& input, Field& output);
Field detect_data;
- const bool detection_section;
Field classic_client_body; // URI normalization applied
Field decoded_body;
Field decompressed_pdf_swf_body;
Field js_norm_body;
+ const bool detection_section;
};
#endif
void HttpMsgBodyCl::print_section(FILE* output)
{
HttpMsgSection::print_section_title(output, "Content-Length body");
- fprintf(output, "Content-Length %" PRIi64 ", octets seen %" PRIi64 "\n", data_length,
- body_octets);
+ fprintf(output, "octets seen %" PRIi64 "\n", body_octets);
print_body_section(output);
}
#endif
{
public:
HttpMsgBodyCl(const uint8_t* buffer, const uint16_t buf_size, HttpFlowData* session_data_,
- HttpEnums::SourceId source_id_, bool buf_owner, snort::Flow* flow_, const HttpParaList* params_)
- : HttpMsgBody(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_),
- data_length(session_data->data_length[source_id]) {}
+ HttpEnums::SourceId source_id_, bool buf_owner, snort::Flow* flow_,
+ const HttpParaList* params_)
+ : HttpMsgBody(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_) {}
void update_flow() override;
#ifdef REG_TEST
void print_section(FILE* output) override;
#endif
-
-protected:
- int64_t data_length;
};
#endif
{
public:
HttpMsgBodyOld(const uint8_t* buffer, const uint16_t buf_size, HttpFlowData* session_data_,
- HttpEnums::SourceId source_id_, bool buf_owner, snort::Flow* flow_, const HttpParaList* params_)
- : HttpMsgBody(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_),
- data_length(session_data->data_length[source_id]) {}
+ HttpEnums::SourceId source_id_, bool buf_owner, snort::Flow* flow_,
+ const HttpParaList* params_)
+ : HttpMsgBody(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_) {}
void update_flow() override;
#ifdef REG_TEST
void print_section(FILE* output) override;
#endif
-
-protected:
- int64_t data_length;
};
#endif
void create_norm_head_list();
void derive_header_name_id(int index);
- std::bitset<MAX> headers_present = 0;
- int32_t num_headers = HttpEnums::STAT_NOT_COMPUTE;
+ Field classic_raw_header; // raw headers with cookies spliced out
+ Field classic_norm_header; // URI normalization applied
+ Field classic_norm_cookie; // URI normalization applied to concatenated cookie values
Field* header_line = nullptr;
Field* header_name = nullptr;
HttpEnums::HeaderId* header_name_id = nullptr;
Field* header_value = nullptr;
- Field classic_raw_header; // raw headers with cookies spliced out
- Field classic_norm_header; // URI normalization applied
- Field classic_norm_cookie; // URI normalization applied to concatenated cookie values
-
struct NormalizedHeader
{
NormalizedHeader(HttpEnums::HeaderId id_) : id(id_) {}
- const HttpEnums::HeaderId id;
- int count;
+
Field norm;
NormalizedHeader* next;
+ int32_t count;
+ const HttpEnums::HeaderId id;
};
-
- NormalizedHeader* norm_heads = nullptr;
NormalizedHeader* get_header_node(HttpEnums::HeaderId k) const;
+ NormalizedHeader* norm_heads = nullptr;
+
+ int32_t num_headers = HttpEnums::STAT_NOT_COMPUTE;
+ std::bitset<MAX> headers_present = 0;
};
#endif
const Field& get_true_ip_addr();
private:
- // Dummy configurations to support MIME processing
- MailLogConfig mime_conf;
- snort::DecodeConfig decode_conf;
-
void prepare_body();
void setup_file_processing();
void setup_encoding_decompression();
void setup_utf_decoding();
void setup_pdf_swf_decompression();
- bool detection_section = true;
+ // Dummy configurations to support MIME processing
+ MailLogConfig mime_conf;
+ snort::DecodeConfig decode_conf;
Field true_ip;
Field true_ip_addr;
+ bool detection_section = true;
+
#ifdef REG_TEST
void print_section(FILE* output) override;
#endif
const HttpParaList* params_) :
msg_text(buf_size, buffer, buf_owner),
session_data(session_data_),
- source_id(source_id_),
flow(flow_),
- trans_num(session_data->expected_trans_num[source_id]),
params(params_),
- transaction(HttpTransaction::attach_my_transaction(session_data, source_id)),
- tcp_close(session_data->tcp_close[source_id]),
+ transaction(HttpTransaction::attach_my_transaction(session_data, source_id_)),
+ trans_num(session_data->expected_trans_num[source_id_]),
+ status_code_num((source_id_ == SRC_SERVER) ? session_data->status_code_num : STAT_NOT_PRESENT),
+ source_id(source_id_),
version_id(session_data->version_id[source_id]),
method_id((source_id == SRC_CLIENT) ? session_data->method_id : METH__NOT_PRESENT),
- status_code_num((source_id == SRC_SERVER) ? session_data->status_code_num : STAT_NOT_PRESENT)
+ tcp_close(session_data->tcp_close[source_id])
{
assert((source_id == SRC_CLIENT) || (source_id == SRC_SERVER));
}
params_);
const Field msg_text;
-
HttpFlowData* const session_data;
- const HttpEnums::SourceId source_id;
snort::Flow* const flow;
- uint64_t trans_num;
const HttpParaList* const params;
HttpTransaction* const transaction;
- const bool tcp_close;
-
+ uint64_t trans_num;
+ int32_t status_code_num;
+ const HttpEnums::SourceId source_id;
HttpEnums::VersionId version_id;
HttpEnums::MethodId method_id;
- int32_t status_code_num;
+ const bool tcp_close;
// Convenience methods shared by multiple subclasses
void add_infraction(int infraction);
public:
HttpStreamSplitter(bool is_client_to_server, HttpInspect* my_inspector_) :
snort::StreamSplitter(is_client_to_server),
- source_id(is_client_to_server ? HttpEnums::SRC_CLIENT : HttpEnums::SRC_SERVER),
- my_inspector(my_inspector_) { }
+ my_inspector(my_inspector_),
+ source_id(is_client_to_server ? HttpEnums::SRC_CLIENT : HttpEnums::SRC_SERVER) {}
Status scan(snort::Flow* flow, const uint8_t* data, uint32_t length, uint32_t not_used,
uint32_t* flush_offset) override;
const snort::StreamBuffer reassemble(snort::Flow* flow, unsigned total, unsigned, const
uint32_t length, HttpEnums::CompressId& compression, z_stream*& compress_stream,
bool at_start, HttpInfractions* infractions, HttpEventGen* events);
- const HttpEnums::SourceId source_id;
HttpInspect* const my_inspector;
+ const HttpEnums::SourceId source_id;
};
#endif
session_data->section_size_target[source_id], session_data->section_size_max[source_id]);
switch (cut_result)
{
- case SCAN_NOTFOUND:
+ case SCAN_NOT_FOUND:
if (cutter->get_octets_seen() == MAX_OCTETS)
{
*session_data->get_infractions(source_id) += INF_ENDLESS_HEADER;
delete cutter;
cutter = nullptr;
}
+ else
+ cutter->soft_reset();
return StreamSplitter::FLUSH;
case SCAN_FOUND:
case SCAN_FOUND_PIECE:
delete cutter;
cutter = nullptr;
}
+ else
+ cutter->soft_reset();
return StreamSplitter::FLUSH;
}
default:
HttpUri(const uint8_t* start, int32_t length, HttpEnums::MethodId method_id_,
const HttpParaList::UriParam& uri_param_, HttpInfractions* infractions_,
HttpEventGen* events_) :
- uri(length, start), method_id(method_id_), uri_param(uri_param_),
- infractions(infractions_), events(events_)
+ uri(length, start), infractions(infractions_), events(events_), method_id(method_id_),
+ uri_param(uri_param_)
{ normalize(); }
const Field& get_uri() const { return uri; }
HttpEnums::UriType get_uri_type() { return uri_type; }
private:
const Field uri;
- const HttpEnums::MethodId method_id;
- const HttpParaList::UriParam& uri_param;
- HttpInfractions* infractions;
- HttpEventGen* events;
Field scheme;
Field authority;
Field query;
Field fragment;
- HttpEnums::UriType uri_type = HttpEnums::URI__NOT_COMPUTE;
Field host_norm;
Field path_norm;
Field query_norm;
Field fragment_norm;
Field classic_norm;
+ HttpInfractions* const infractions;
+ HttpEventGen* const events;
size_t abs_path_hash = 0;
+ HttpEnums::UriType uri_type = HttpEnums::URI__NOT_COMPUTE;
+ const HttpEnums::MethodId method_id;
+ const HttpParaList::UriParam& uri_param;
void normalize();
void parse_uri();