]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
scan() refactoring
authorTom Peters <thopeter@cisco.com>
Thu, 18 Sep 2014 19:42:56 +0000 (15:42 -0400)
committerTom Peters <thopeter@cisco.com>
Thu, 18 Sep 2014 19:42:56 +0000 (15:42 -0400)
src/service_inspectors/nhttp_inspect/CMakeLists.txt
src/service_inspectors/nhttp_inspect/Makefile.am
src/service_inspectors/nhttp_inspect/nhttp_flow_data.h
src/service_inspectors/nhttp_inspect/nhttp_splitter.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_splitter.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc

index a702fc6918f969675ee776486f8ece8ce848e917..63148f6d9c42757f1ba13d423c8c648a764218f5 100644 (file)
@@ -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)
index e46e68be9dab4f7c85317593a9fcdb68ad9a36e2..b32aa79df66785996af054207fdea0651c79cdc6 100644 (file)
@@ -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 \
index b97fbac78826db5b7588f608dedb3be6e0592cbc..d4c41ddebd360efe17102d7e913ff5b9bb15ccb6 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <stdio.h>
 #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 (file)
index 0000000..151499c
--- /dev/null
@@ -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 <thopeter@cisco.com>
+//
+//  @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 <CR> and <LF> 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 <CR> and <LF> 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 (file)
index 0000000..6bf3bdf
--- /dev/null
@@ -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 <thopeter@cisco.com>
+//
+//  @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
+
index 646027733f6578433af8c56ceb5743b93c73172b..80048f6404c44670f93bd01d53f63284ee14495f 100644 (file)
@@ -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 <CR> and <LF> 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 <CRLF>
-            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 <CR> and <LF> 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 <CRLF>
-            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 <CR><close>
-        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: