From: Tom Peters Date: Thu, 18 Sep 2014 19:42:56 +0000 (-0400) Subject: scan() refactoring X-Git-Tag: 3.0.0-233~1412 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fb4e6b4c26cb9cc546ad3f26dc84ff4c4c38079c;p=thirdparty%2Fsnort3.git scan() refactoring --- diff --git a/src/service_inspectors/nhttp_inspect/CMakeLists.txt b/src/service_inspectors/nhttp_inspect/CMakeLists.txt index a702fc691..63148f6d9 100644 --- a/src/service_inspectors/nhttp_inspect/CMakeLists.txt +++ b/src/service_inspectors/nhttp_inspect/CMakeLists.txt @@ -51,6 +51,8 @@ set (FILE_LIST nhttp_field.h nhttp_stream_splitter.cc nhttp_stream_splitter.h + nhttp_splitter.cc + nhttp_splitter.h ) if (STATIC_INSPECTORS) diff --git a/src/service_inspectors/nhttp_inspect/Makefile.am b/src/service_inspectors/nhttp_inspect/Makefile.am index e46e68be9..b32aa79df 100644 --- a/src/service_inspectors/nhttp_inspect/Makefile.am +++ b/src/service_inspectors/nhttp_inspect/Makefile.am @@ -25,6 +25,7 @@ nhttp_test_input.cc nhttp_test_input.h \ nhttp_flow_data.cc nhttp_flow_data.h \ nhttp_transaction.cc nhttp_transaction.h \ nhttp_stream_splitter.cc nhttp_stream_splitter.h \ +nhttp_splitter.cc nhttp_splitter.h \ nhttp_scratch_pad.h \ nhttp_enum.h \ nhttp_test_manager.cc nhttp_test_manager.h \ diff --git a/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h b/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h index b97fbac78..d4c41ddeb 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h @@ -31,6 +31,7 @@ #include #include "stream/stream_api.h" +#include "nhttp_splitter.h" class NHttpTransaction; @@ -60,8 +61,8 @@ private: void half_reset(NHttpEnums::SourceId source_id); // StreamSplitter internal data - int64_t octets_seen[2] = { 0, 0 }; - int num_crlf[2] = { 0, 0 }; + NHttpStartSplitter start_splitter[2]; + NHttpHeaderSplitter header_splitter[2]; uint32_t peek_ahead_octets[2] = { 0, 0 }; uint32_t unused_octets_visible[2] = { 0, 0 }; uint32_t header_octets_visible[2] = { 0, 0 }; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_splitter.cc b/src/service_inspectors/nhttp_inspect/nhttp_splitter.cc new file mode 100644 index 000000000..151499c56 --- /dev/null +++ b/src/service_inspectors/nhttp_inspect/nhttp_splitter.cc @@ -0,0 +1,81 @@ +/**************************************************************************** + * +** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved. + * Copyright (C) 2003-2013 Sourcefire, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation. You may not use, modify or + * distribute this program under any other version of the GNU General + * Public License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ****************************************************************************/ + +// +// @author Tom Peters +// +// @brief Section-specific splitters +// + +#include "nhttp_splitter.h" + +using namespace NHttpEnums; + +SectionType NHttpStartSplitter::split(const uint8_t* buffer, uint32_t length) { + for (uint32_t k = 0; k < length; k++) { + // Count the alternating and characters we have seen in a row + if (((buffer[k] == '\r') && (num_crlf == 0)) || + ((buffer[k] == '\n') && (num_crlf == 1))) { + num_crlf++; + if (num_crlf < 2) { + continue; + } + } + else { + num_crlf = 0; + continue; + } + num_flush = k+1; + // If the first two octets are CRLF then they must be discarded. + return ((octets_seen + k + 1) == 2) ? SEC_DISCARD : SEC_REQUEST; + } + octets_seen += length; + return SEC__NOTPRESENT; +} + +SectionType NHttpHeaderSplitter::split(const uint8_t* buffer, uint32_t length) { + for (uint32_t k = 0; k < length; k++) { + // Count the alternating and characters we have seen in a row + if (((buffer[k] == '\r') && (num_crlf%2 == 0)) || + ((buffer[k] == '\n') && (num_crlf%2 == 1))) { + num_crlf++; + if ((num_crlf == 2) && (octets_seen + k + 1) == 2) { + num_flush = k+1; + return SEC_HEADER; + } + if (num_crlf < 4) { + continue; + } + } + else { + num_crlf = 0; + continue; + } + num_flush = k+1; + return SEC_HEADER; + } + octets_seen += length; + return SEC__NOTPRESENT; +} + + + diff --git a/src/service_inspectors/nhttp_inspect/nhttp_splitter.h b/src/service_inspectors/nhttp_inspect/nhttp_splitter.h new file mode 100644 index 000000000..6bf3bdf21 --- /dev/null +++ b/src/service_inspectors/nhttp_inspect/nhttp_splitter.h @@ -0,0 +1,63 @@ +/**************************************************************************** + * +** Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved. + * Copyright (C) 2003-2013 Sourcefire, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation. You may not use, modify or + * distribute this program under any other version of the GNU General + * Public License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ****************************************************************************/ + +// +// @author Tom Peters +// +// @brief NHttpSplitter class and subclasses declarations +// + +#ifndef NHTTP_SPLITTER_H +#define NHTTP_SPLITTER_H + +#include "nhttp_enum.h" + +//------------------------------------------------------------------------- +// NHttpSplitter class +//------------------------------------------------------------------------- + +class NHttpSplitter { +public: + virtual void reset() { octets_seen = 0; num_crlf = 0; num_flush = 0; }; + virtual NHttpEnums::SectionType split(const uint8_t* buffer, uint32_t length) = 0; + virtual ~NHttpSplitter() = default; + uint32_t get_num_flush() { return num_flush; }; + uint32_t get_octets_seen() { return octets_seen; }; + +protected: + uint32_t octets_seen = 0; + uint32_t num_crlf = 0; + uint32_t num_flush = 0; +}; + +class NHttpStartSplitter : public NHttpSplitter { +public: + NHttpEnums::SectionType split(const uint8_t* buffer, uint32_t length); +}; + +class NHttpHeaderSplitter : public NHttpSplitter { +public: + NHttpEnums::SectionType split(const uint8_t* buffer, uint32_t length); +}; + +#endif + diff --git a/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc b/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc index 646027733..80048f640 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc @@ -34,6 +34,7 @@ #include "nhttp_enum.h" #include "nhttp_test_manager.h" #include "nhttp_test_input.h" +#include "nhttp_splitter.h" #include "nhttp_stream_splitter.h" #include "nhttp_inspect.h" @@ -55,14 +56,14 @@ void NHttpStreamSplitter::prepare_flush(NHttpFlowData* session_data, uint32_t* f else { NHttpTestManager::get_test_input_source()->flush(num_octets); } - session_data->octets_seen[source_id] = 0; - session_data->num_crlf[source_id] = 0; session_data->peek_ahead_octets[source_id] = 0; session_data->unused_octets_visible[source_id] = length - num_octets; session_data->header_octets_visible[source_id] = 0; } StreamSplitter::Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* data, uint32_t length, uint32_t, uint32_t* flush_offset) { + assert(length <= 63780); + // When the system begins providing TCP connection close information this won't always be false. FIXIT-H bool tcp_close = false; @@ -102,86 +103,64 @@ StreamSplitter::Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* dat 0, session_data->header_octets_visible[source_id], length); return StreamSplitter::FLUSH; } + // Did we peek ahead and not find the complete headers? + if (session_data->peek_ahead_octets[source_id] > 0) { + assert(length == session_data->peek_ahead_octets[source_id]); + session_data->peek_ahead_octets[source_id] = 0; + return StreamSplitter::SEARCH; + } switch (type) { case SEC_REQUEST: case SEC_STATUS: - case SEC_HEADER: case SEC_CHUNKHEAD: - case SEC_TRAILER: + case SEC_HEADER: + case SEC_TRAILER: { paf_max = 63780; - for (uint32_t k = session_data->peek_ahead_octets[source_id]; k < length; k++) { - session_data->octets_seen[source_id]++; - // Count the alternating and characters we have seen in a row - if (((data[k] == '\r') && (session_data->num_crlf[source_id]%2 == 0)) || - ((data[k] == '\n') && (session_data->num_crlf[source_id]%2 == 1))) { - session_data->num_crlf[source_id]++; - } - else { - session_data->num_crlf[source_id] = 0; - } - - // If the first two octets are CRLF then flush them separately. We are 1) DISCARDing CRLF some - // 1.0 implementation put following previous message, 2) DISCARDing CRLF between chunk and following - // chunk header, and 3) flushing normal empty header or trailer. - if ((session_data->num_crlf[source_id] == 2) && (session_data->octets_seen[source_id] == 2)) { - prepare_flush(session_data, flush_offset, source_id, - ((type == SEC_REQUEST) || (type == SEC_STATUS) || (type == SEC_CHUNKHEAD)) ? SEC_DISCARD : type, - tcp_close && (k == length-1), 0, k+1, length); - return StreamSplitter::FLUSH; - } - // The start line and chunk header section always end with the first - else if ((session_data->num_crlf[source_id] == 2) && - ((type == SEC_REQUEST) || (type == SEC_STATUS) || (type == SEC_CHUNKHEAD))) { - prepare_flush(session_data, flush_offset, source_id, type, tcp_close && (k == length-1), 0, k+1, length); - if ((type == SEC_REQUEST) || (type == SEC_STATUS)) { - // Look ahead to see if entire header section is already here so we can aggregate it for detection. - for (uint32_t m = k+1; m < length; m++) { - session_data->octets_seen[source_id]++; - // Count the alternating and characters we have seen in a row - if (((data[m] == '\r') && (session_data->num_crlf[source_id]%2 == 0)) || - ((data[m] == '\n') && (session_data->num_crlf[source_id]%2 == 1))) { - session_data->num_crlf[source_id]++; - } - else { - session_data->num_crlf[source_id] = 0; - } - if ( (session_data->num_crlf[source_id] == 4) || - ((session_data->num_crlf[source_id] == 2) && (session_data->octets_seen[source_id] == 2))) { - session_data->header_octets_visible[source_id] = m-k; - return StreamSplitter::FLUSH; - } - } - session_data->peek_ahead_octets[source_id] = length - (k+1); - } - return StreamSplitter::FLUSH; - } - // The header and trailer sections always end with the first double - else if (session_data->num_crlf[source_id] == 4) { - prepare_flush(session_data, flush_offset, source_id, type, tcp_close && (k == length-1), 0, k+1, length); - return StreamSplitter::FLUSH; - } - // We must do this to protect ourself from buffer overrun. - else if (session_data->octets_seen[source_id] >= 63780) { + NHttpSplitter* splitter = ((type == SEC_HEADER) || (type == SEC_TRAILER)) ? + (NHttpSplitter*)&session_data->header_splitter[source_id] : + (NHttpSplitter*)&session_data->start_splitter[source_id]; + const uint32_t max_length = (length <= (63780 - splitter->get_octets_seen())) ? length : + (63780 - splitter->get_octets_seen()); + const SectionType split_result = splitter->split(data, max_length); + if (split_result == SEC__NOTPRESENT) { + if (splitter->get_octets_seen() == 63780) { // FIXIT-M need to implement processing and detection instead of just discarding this data session_data->type_expected[source_id] = SEC_ABORT; return StreamSplitter::ABORT; } + if (!tcp_close) { + // Incomplete headers wait patiently for more data + return StreamSplitter::SEARCH; + } + prepare_flush(session_data, flush_offset, source_id, type, true, INF_TRUNCATED, length, length); + splitter->reset(); + return StreamSplitter::FLUSH; } - session_data->peek_ahead_octets[source_id] = 0; - // Incomplete headers wait patiently for more data - if (!tcp_close) { - return StreamSplitter::SEARCH; - } - // Discard the oddball case where the new "message" starts with - else if ((session_data->octets_seen[source_id] == 1) && (session_data->num_crlf[source_id] == 1)) { - prepare_flush(session_data, flush_offset, source_id, SEC_DISCARD, true, 0, length, length); + const uint32_t flush_octets = session_data->peek_ahead_octets[source_id] + splitter->get_num_flush(); + if (split_result == SEC_DISCARD) { + prepare_flush(session_data, flush_offset, source_id, SEC_DISCARD, tcp_close && (flush_octets == length), 0, + flush_octets, length); + splitter->reset(); + return StreamSplitter::FLUSH; } - // TCP connection close, flush the partial header - else { - prepare_flush(session_data, flush_offset, source_id, type, true, INF_TRUNCATED, length, length); + prepare_flush(session_data, flush_offset, source_id, type, tcp_close && (flush_octets == length), 0, + flush_octets, length); + splitter->reset(); + if ((type == SEC_REQUEST) || (type == SEC_STATUS)) { + // Look ahead to see if entire header section is already here so we can aggregate it for detection. + NHttpSplitter* peek_splitter = &session_data->header_splitter[source_id]; + const SectionType peek_result = peek_splitter->split(data + flush_octets, length - flush_octets); + if (peek_result == SEC_HEADER) { + session_data->header_octets_visible[source_id] = peek_splitter->get_num_flush(); + peek_splitter->reset(); + } + else { + session_data->peek_ahead_octets[source_id] = length - flush_octets; + } } return StreamSplitter::FLUSH; + } case SEC_BODY: case SEC_CHUNKBODY: paf_max = 16384 - session_data->chunk_buffer_length[source_id]; @@ -191,7 +170,7 @@ StreamSplitter::Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* dat } else { // The TCP connection has closed and this is the possibly incomplete final section - prepare_flush(session_data, flush_offset, source_id, type, true, 0, length, length); + prepare_flush(session_data, flush_offset, source_id, type, true, 0, length, length); } return StreamSplitter::FLUSH; case SEC_ABORT: