]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Changes for PAF implementation. File deletes in a future commit.
authorTom Peters <thopeter@cisco.com>
Wed, 4 Jun 2014 16:53:22 +0000 (12:53 -0400)
committerTom Peters <thopeter@cisco.com>
Wed, 4 Jun 2014 16:53:22 +0000 (12:53 -0400)
36 files changed:
src/service_inspectors/nhttp_inspect/CMakeLists.txt
src/service_inspectors/nhttp_inspect/Makefile.am
src/service_inspectors/nhttp_inspect/nhttp_api.cc
src/service_inspectors/nhttp_inspect/nhttp_api.h
src/service_inspectors/nhttp_inspect/nhttp_enum.h
src/service_inspectors/nhttp_inspect/nhttp_flow_data.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_flow_data.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_head_norm.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_inspect.cc
src/service_inspectors/nhttp_inspect/nhttp_inspect.h
src/service_inspectors/nhttp_inspect/nhttp_module.cc
src/service_inspectors/nhttp_inspect/nhttp_module.h
src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msg_body.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msg_head.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msg_head.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msg_section.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_scratch_pad.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_str_to_code.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_str_to_code.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_tables.cc
src/service_inspectors/nhttp_inspect/nhttp_test_input.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_test_input.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_test_msgs.txt [new file with mode: 0644]

index 3c62e9bca86a178369b65da83fb701d5296d95d8..17cbfae4bd3c015ca0288ef2d294b6d9f374a529 100644 (file)
@@ -2,23 +2,36 @@
 set (FILE_LIST
     nhttp_inspect.cc
     nhttp_inspect.h
-    nhttp_msgheader.cc
-    nhttp_msgheader.h
-    nhttp_headnorm.cc
-    nhttp_headnorm.h
-    nhttp_strtocode.cc
-    nhttp_strtocode.h
-    nhttp_api.cc
-    nhttp_api.h
+    nhttp_msg_section.cc
+    nhttp_msg_section.h
+    nhttp_msg_head_shared.cc
+    nhttp_msg_head_shared.h
+    nhttp_msg_head.cc
+    nhttp_msg_head.h
+    nhttp_msg_body.cc
+    nhttp_msg_body.h
+    nhttp_msg_chunk_head.cc
+    nhttp_msg_chunk_head.h
+    nhttp_msg_chunk_body.cc
+    nhttp_msg_chunk_body.h
+    nhttp_msg_trailer.cc
+    nhttp_msg_trailer.h
+    nhttp_head_norm.cc
+    nhttp_head_norm.h
+    nhttp_str_to_code.cc
+    nhttp_str_to_code.h
+    nhttp_api.cc nhttp_api.h
     nhttp_tables.cc
     nhttp_module.cc
     nhttp_module.h
-    nhttp_testinput.cc
-    nhttp_testinput.h
-    nhttp_flowdata.cc
-    nhttp_flowdata.h
-    nhttp_scratchpad.h
+    nhttp_test_input.cc
+    nhttp_test_input.h
+    nhttp_flow_data.cc
+    nhttp_flow_data.h
+    nhttp_scratch_pad.h
     nhttp_enum.h
+    nhttp_stream_splitter.cc
+    nhttp_stream_splitter.h
 )
 
 #if (STATIC_INSPECTORS)
index db9ffca58c08099cf757855255c277fca6859e95..05f0765a64eb3c467c034ba378ea1aba5a42c120 100644 (file)
@@ -2,15 +2,22 @@ AUTOMAKE_OPTIONS=foreign no-dependencies
 
 file_list = \
 nhttp_inspect.cc nhttp_inspect.h \
-nhttp_msgheader.cc nhttp_msgheader.h \
-nhttp_headnorm.cc nhttp_headnorm.h \
-nhttp_strtocode.cc nhttp_strtocode.h \
+nhttp_msg_section.cc nhttp_msg_section.h \
+nhttp_msg_head_shared.cc nhttp_msg_head_shared.h \
+nhttp_msg_head.cc nhttp_msg_head.h \
+nhttp_msg_body.cc nhttp_msg_body.h \
+nhttp_msg_chunk_head.cc nhttp_msg_chunk_head.h \
+nhttp_msg_chunk_body.cc nhttp_msg_chunk_body.h \
+nhttp_msg_trailer.cc nhttp_msg_trailer.h \
+nhttp_head_norm.cc nhttp_head_norm.h \
+nhttp_str_to_code.cc nhttp_str_to_code.h \
 nhttp_api.cc nhttp_api.h \
 nhttp_tables.cc \
 nhttp_module.cc nhttp_module.h \
-nhttp_testinput.cc nhttp_testinput.h \
-nhttp_flowdata.cc nhttp_flowdata.h \
-nhttp_scratchpad.h nhttp_enum.h
+nhttp_test_input.cc nhttp_test_input.h \
+nhttp_flow_data.cc nhttp_flow_data.h \
+nhttp_stream_splitter.cc nhttp_stream_splitter.h \
+nhttp_scratch_pad.h nhttp_enum.h
 
 
 #if STATIC_INSPECTORS
index 16873b125b48c7ec410b32bd1cb503a2a95a6045..d6f6f19fb77f8fe44978bb68fcfac53455a5a18e 100644 (file)
 #include <sys/types.h>
 
 #include "snort.h"
-#include "framework/parameter.h"
-#include "framework/module.h"
-#include "framework/inspector.h"
-#include "flow/flow.h"
+#include "target_based/sftarget_protocol_reference.h"
 #include "nhttp_enum.h"
-#include "nhttp_flowdata.h"
-#include "nhttp_scratchpad.h"
 #include "nhttp_module.h"
-#include "nhttp_strtocode.h"
-#include "nhttp_headnorm.h"
-#include "nhttp_msgheader.h"
-#include "nhttp_testinput.h"
-#include "nhttp_api.h"
 #include "nhttp_inspect.h"
+#include "nhttp_api.h"
+
+int16_t NHttpApi::appProtocolId;
 
 Module* NHttpApi::nhttp_mod_ctor() { return new NHttpModule; }
 
@@ -55,53 +48,49 @@ const char* NHttpApi::nhttp_myName = "nhttp_inspect";
 
 void NHttpApi::nhttp_init()
 {
-    printf("nhttp_init()\n");
     NHttpFlowData::init();
+    appProtocolId = AddProtocolReference("nhttp");
 }
 
 void NHttpApi::nhttp_term()
 {
-    printf("nhttp_term()\n");
 }
 
 Inspector* NHttpApi::nhttp_ctor(Module* mod)
 {
     const NHttpModule* nhttpMod = (NHttpModule*) mod;
-    printf("nhttp_ctor()\n");
     return new NHttpInspect(nhttpMod->get_test_mode());
 }
 
 void NHttpApi::nhttp_dtor(Inspector* p)
 {
-    printf("nhttp_dtor()\n");
     delete p;
 }
 
 void NHttpApi::nhttp_pinit()
 {
-    printf("nhttp_pinit()\n");
     NHttpInspect::msgHead = new NHttpMsgHeader;
+    NHttpInspect::msgBody = new NHttpMsgBody;
+    NHttpInspect::msgChunkHead = new NHttpMsgChunkHead;
+    NHttpInspect::msgChunkBody = new NHttpMsgChunkBody;
+    NHttpInspect::msgTrailer = new NHttpMsgTrailer;
 }
 
 void NHttpApi::nhttp_pterm()
 {
-    printf("nhttp_pterm()\n");
     delete NHttpInspect::msgHead;
 }
 
 void NHttpApi::nhttp_sum()
 {
-    printf("nhttp_sum()\n");
 }
 
 void NHttpApi::nhttp_stats()
 {
-    printf("nhttp_stats()\n");
 }
 
 void NHttpApi::nhttp_reset()
 {
-    printf("nhttp_reset()\n");
 }
 
 const InspectApi NHttpApi::nhttp_api =
@@ -123,7 +112,7 @@ const InspectApi NHttpApi::nhttp_api =
     NHttpApi::nhttp_dtor,
     NHttpApi::nhttp_pinit,
     NHttpApi::nhttp_pterm,
-    nullptr, // ssn
+    nullptr,
     NHttpApi::nhttp_sum,
     NHttpApi::nhttp_stats,
     NHttpApi::nhttp_reset
index 79f4ec0e905f3d40bbd7c363850e9e1f45803ecf..4335e79020d3ae0446d7a2e8fd0f6081849eecf1 100644 (file)
 #ifndef NHTTP_API_H
 #define NHTTP_API_H
 
+#include "framework/parameter.h"
+#include "framework/module.h"
+#include "framework/inspector.h"
+
 class NHttpApi {
 public:
     static const InspectApi nhttp_api;
+    static int16_t appProtocolId;
 private:
     NHttpApi() = delete;
     static Module* nhttp_mod_ctor();
@@ -43,7 +48,6 @@ private:
     static void nhttp_dtor(Inspector* p);
     static void nhttp_pinit();
     static void nhttp_pterm();
-    static void nhttp_purge();
     static void nhttp_sum();
     static void nhttp_stats();
     static void nhttp_reset();
index 46355c6662680095c45af7a96bc575412f592e71..5ee313f3caacd3d8aaf984dc8dba2e83d62ea4bf 100644 (file)
 #ifndef NHTTP_ENUM_H
 #define NHTTP_ENUM_H
 
-#define GID_HTTP_CLIENT 119
-#define GID_HTTP_SERVER 120
+#define NHTTP_GID 119
 
 namespace NHttpEnums {
 
+static const uint32_t MAXOCTETS = 63780;
+
 // Field status codes for when no valid value is present in length or integer value. Positive values are actual length or field value.
 typedef enum { STAT_NOTCONFIGURED=-5, STAT_NOTCOMPUTE=-4, STAT_INSUFMEMORY=-3, STAT_PROBLEMATIC=-2, STAT_NOTPRESENT=-1, STAT_EMPTYSTRING=0, STAT_OTHER=1 } StatusCode;
 
 // Message originator--client or server
-typedef enum { SRC__NOTCOMPUTE=-4, SRC__PROBLEMATIC=-2, SRC_CLIENT=1, SRC_SERVER } SourceId;
+typedef enum { SRC__NOTCOMPUTE=-4, SRC_CLIENT=0, SRC_SERVER=1 } SourceId;
 
 // Type of message section
-typedef enum { SEC__PROBLEMATIC=-2, SEC_HEADER = 2, SEC_BODY, SEC_CHUNK } SectionType;
+typedef enum { SEC__NOTCOMPUTE=-4, SEC__NOTPRESENT=-1, SEC_HEADER = 2, SEC_BODY, SEC_CHUNKHEAD, SEC_CHUNKBODY, SEC_TRAILER, SEC_DISCARD, SEC_CLOSED, SEC_ABORT } SectionType;
 
 // List of possible HTTP versions. Version 0.9 omitted because 0.9 predates creation of the HTTP/X.Y token. There would never be a message with "HTTP/0.9"
 typedef enum { VERS__NOTCOMPUTE=-4, VERS__PROBLEMATIC=-2, VERS__NOTPRESENT=-1, VERS__OTHER=1, VERS_1_0, VERS_1_1, VERS_2_0 } VersionId;
@@ -63,11 +64,12 @@ typedef enum { HEAD__NOTCOMPUTE=-4, HEAD__INSUFMEMORY=-3, HEAD__PROBLEMATIC=-2,
    HEAD_CONTENT_LOCATION, HEAD_CONTENT_MD5, HEAD_CONTENT_RANGE, HEAD_CONTENT_TYPE, HEAD_EXPIRES, HEAD_LAST_MODIFIED, HEAD__MAXVALUE } HeaderId;
 
 // All the infractions we might find while parsing and analyzing a message
-typedef enum { INF_TRUNCATED=0x1, /*INF_CANTFINDVERS=0x2,*/ /*INF_STARTTOOSHORT=0x4,*/ INF_BADREQLINE=0x8, INF_BADSTATLINE=0x10, INF_TOOMANYHEADERS=0x20,
-   INF_BADHEADER=0x40, INF_BADSTATCODE=0x80, INF_UNKNOWNVERSION=0x100, INF_BADVERSION=0x200, INF_NOSCRATCH=0x400, INF_BADHEADERREPS=0x800, INF_BADHEADERDATA=0x1000 } Infraction;
+typedef enum { INF_TRUNCATED=0x1, INF_HEADTOOLONG=0x2, /*INF_STARTTOOSHORT=0x4,*/ INF_BADREQLINE=0x8, INF_BADSTATLINE=0x10, INF_TOOMANYHEADERS=0x20,
+   INF_BADHEADER=0x40, INF_BADSTATCODE=0x80, INF_UNKNOWNVERSION=0x100, INF_BADVERSION=0x200, INF_NOSCRATCH=0x400, INF_BADHEADERREPS=0x800, INF_BADHEADERDATA=0x1000,
+   INF_BROKENCHUNK=0x2000, INF_BADCHUNKSIZE=0x4000, INF_BADPHRASE= 0x8000 } Infraction;
 
 // Formats for output from a header normalization function
-typedef enum { NORM_NULL, NORM_FIELD, NORM_INTEGER, NORM_ENUM, NORM_ENUMLIST } NormFormat;
+typedef enum { NORM_NULL, NORM_FIELD, NORM_INT64, NORM_ENUM64, NORM_ENUM64LIST } NormFormat;
 
 // Transfer codings
 typedef enum { TRANSCODE__OTHER=1, TRANSCODE_CHUNKED, TRANSCODE_IDENTITY, TRANSCODE_GZIP, TRANSCODE_COMPRESS, TRANSCODE_DEFLATE } Transcoding;
@@ -81,5 +83,55 @@ typedef struct {
     int32_t length = -1;
 } field;
 
+typedef enum 
+{
+    EVENT_ASCII = 1,
+    EVENT_DOUBLE_DECODE, 
+    EVENT_U_ENCODE, 
+    EVENT_BARE_BYTE, 
+    EVENT_OBSOLETE_1,
+    EVENT_UTF_8, 
+    EVENT_IIS_UNICODE, 
+    EVENT_MULTI_SLASH, 
+    EVENT_IIS_BACKSLASH, 
+    EVENT_SELF_DIR_TRAV, 
+    EVENT_DIR_TRAV,
+    EVENT_APACHE_WS,
+    EVENT_IIS_DELIMITER,
+    EVENT_NON_RFC_CHAR,
+    EVENT_OVERSIZE_DIR,
+    EVENT_LARGE_CHUNK,
+    EVENT_PROXY_USE,
+    EVENT_WEBROOT_DIR,
+    EVENT_LONG_HDR,
+    EVENT_MAX_HEADERS,
+    EVENT_MULTIPLE_CONTLEN,
+    EVENT_CHUNK_SIZE_MISMATCH,
+    EVENT_INVALID_TRUEIP,
+    EVENT_MULTIPLE_HOST_HDRS,
+    EVENT_LONG_HOSTNAME,
+    EVENT_EXCEEDS_SPACES,
+    EVENT_CONSECUTIVE_SMALL_CHUNKS,
+    EVENT_UNBOUNDED_POST,
+    EVENT_MULTIPLE_TRUEIP_IN_SESSION,
+    EVENT_BOTH_TRUEIP_XFF_HDRS,
+    EVENT_UNKNOWN_METHOD,
+    EVENT_SIMPLE_REQUEST,
+    EVENT_UNESCAPED_SPACE_URI,
+    EVENT_PIPELINE_MAX,
+    EVENT_ANOM_SERVER,
+    EVENT_INVALID_STATCODE,
+    EVENT_NO_CONTLEN,
+    EVENT_UTF_NORM_FAIL,
+    EVENT_UTF7,
+    EVENT_DECOMPR_FAILED,
+    EVENT_CONSECUTIVE_SMALL_CHUNKS_S,
+    EVENT_MSG_SIZE_EXCEPTION,
+    EVENT_JS_OBFUSCATION_EXCD,
+    EVENT_JS_EXCESS_WS,
+    EVENT_MIXED_ENCODINGS,
+    EVENT_MAXVALUE
+} EventSid;
+
 #endif
 
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_flow_data.cc b/src/service_inspectors/nhttp_inspect/nhttp_flow_data.cc
new file mode 100644 (file)
index 0000000..e82fd6d
--- /dev/null
@@ -0,0 +1,53 @@
+/****************************************************************************
+ *
+** 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      Flow Data object used to store session information with Streams
+//
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include "snort.h"
+#include "nhttp_enum.h"
+#include "nhttp_flow_data.h"
+
+using namespace NHttpEnums;
+
+unsigned NHttpFlowData::nhttp_flow_id = 0;
+
+NHttpFlowData::NHttpFlowData() : FlowData(nhttp_flow_id) {}
+
+void NHttpFlowData::halfReset(SourceId sourceId) {
+    assert((sourceId == SRC_CLIENT) || (sourceId == SRC_SERVER));
+    dataLength[sourceId] = STAT_NOTPRESENT;
+    octetsExpected[sourceId] = STAT_NOTPRESENT;
+    bodySections[sourceId] = STAT_NOTPRESENT;
+    bodyOctets[sourceId] = STAT_NOTPRESENT;
+    numChunks[sourceId] = STAT_NOTPRESENT;
+    chunkSections[sourceId] = STAT_NOTPRESENT;
+    chunkOctets[sourceId] = STAT_NOTPRESENT;
+}
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h b/src/service_inspectors/nhttp_inspect/nhttp_flow_data.h
new file mode 100644 (file)
index 0000000..50374c3
--- /dev/null
@@ -0,0 +1,92 @@
+/****************************************************************************
+ *
+** 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      Converts protocol constant string to enum
+//
+
+#ifndef NHTTP_FLOW_DATA_H
+#define NHTTP_FLOW_DATA_H
+
+#include "stream/stream_api.h"
+
+class NHttpInspect;
+class NHttpMsgSection;
+class NHttpMsgHeader;
+class NHttpMsgBody;
+class NHttpMsgChunkHead;
+class NHttpMsgChunkBody;
+class NHttpMsgTrailer;
+class NHttpTestInput;
+
+class NHttpFlowData : public FlowData
+{
+public:
+    NHttpFlowData();
+    static unsigned nhttp_flow_id;
+    static void init() { nhttp_flow_id = FlowData::get_flow_id(); };
+
+    friend class NHttpInspect;
+    friend class NHttpMsgSection;
+    friend class NHttpMsgHeader;
+    friend class NHttpMsgBody;
+    friend class NHttpMsgChunkHead;
+    friend class NHttpMsgChunkBody;
+    friend class NHttpMsgTrailer;
+    friend class NHttpTestInput;
+    friend class NHttpStreamSplitter;
+private:
+    void halfReset(NHttpEnums::SourceId sourceId);
+
+    // StreamSplitter => Inspector (facts about the most recent message section)
+    NHttpEnums::SourceId sourceId = NHttpEnums::SRC__NOTCOMPUTE;
+    NHttpEnums::SectionType sectionType = NHttpEnums::SEC__NOTCOMPUTE;
+    bool tcpClose = false;
+    uint64_t infractions = 0;
+
+    // Inspector => StreamSplitter (facts about the message section that is coming next)
+    // 0 element refers to client request, 1 element refers to server response
+    NHttpEnums::SectionType typeExpected[2] = { NHttpEnums::SEC_HEADER, NHttpEnums::SEC_HEADER };
+    int64_t octetsExpected[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };    // expected size of the upcoming body or chunk body section      
+
+    // Inspector's internal data about the current message
+    int64_t dataLength[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };        // length of the data from Content-Length field or chunk header.      
+    int64_t bodySections[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };      // number of body sections seen so far including chunk headers
+    int64_t bodyOctets[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };        // number of user data octets seen so far (either regular body or chunks)
+    int64_t numChunks[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };         // number of chunks seen so far
+    int64_t chunkSections[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };     // number of sections seen so far in the current chunk
+    int64_t chunkOctets[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };       // number of user data octets seen so far in the current chunk including terminating CRLF
+};
+
+#endif
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc
new file mode 100644 (file)
index 0000000..57d5236
--- /dev/null
@@ -0,0 +1,203 @@
+/****************************************************************************
+ *
+** 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      HeaderNormalizer class
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "snort.h"
+#include "snort_types.h"
+
+#include "nhttp_enum.h"
+#include "nhttp_str_to_code.h"
+#include "nhttp_head_norm.h"
+
+using namespace NHttpEnums;
+
+// This derivation removes embedded CRLFs (wrapping), omits leading and trailing linear white space, and replaces internal strings of <SP> and <LF> with a single <SP>
+int32_t HeaderNormalizer::deriveHeaderContent(const uint8_t *value, int32_t length, uint8_t *buffer) {
+    int32_t outLength = 0;
+    bool lastWhite = true;
+    for (int32_t k=0; k < length; k++) {
+        if ((value[k] == '\r') && (k+1 < length) && (value[k+1] == '\n')) k++;
+        else if ((value[k] != ' ') && (value[k] != '\t')) {
+            lastWhite = false;
+            buffer[outLength++] = value[k];
+        }
+        else if (!lastWhite) {
+            lastWhite = true;
+            buffer[outLength++] = ' ';
+        }
+    }
+    if ((outLength > 0) && (buffer[outLength - 1] == ' ')) outLength--;
+    return outLength;
+}
+
+void HeaderNormalizer::normalize(ScratchPad &scratchPad, uint64_t &infractions, HeaderId headId, const HeaderId headerNameId[], const field headerValue[], int32_t numHeaders,
+        field &resultField) const {
+    // This method normalizes the header field value for headId.
+    if (format == NORM_NULL) {
+        resultField.length = STAT_NOTCONFIGURED;
+        return;
+    }
+
+    // Search Header IDs from all the headers in this message. A critical issue is whether the header can be present more than once in a message. concatenateRepeats means the
+    // header can be present more than once. The standard normalization is to concatenate all the repeated field values into a comma-separated list. Otherwise there should not
+    // be more than one instance of this header. infractRepeats causes us to inspect for improper repeated headers. Regardless of whether we look for these extra values only
+    // the first value will be normalized.
+
+    int numMatches = 0;
+    int32_t bufferLength = 0;
+    int firstMatch = -1;
+    for (int k=0; k < numHeaders; k++) {
+        if (headerNameId[k] == HEAD__NOTCOMPUTE) break;
+        if (headerNameId[k] == headId) {
+            numMatches++;
+            if (numMatches == 1) firstMatch = k;
+            if ((numMatches == 1) || concatenateRepeats) bufferLength += headerValue[k].length;
+            if (!concatenateRepeats && !infractRepeats) break;
+        }
+    }
+    if (numMatches == 0) {
+        resultField.length = STAT_NOTPRESENT;
+        return;
+    }
+    if (infractRepeats && (numMatches >= 2)) infractions |= INF_BADHEADERREPS;
+
+    // The scratchPad provides the space to store the normalized value. We are allocating twice as much memory as we need to store the normalized field value. The raw field
+    // value will be copied into one half of the buffer. Concatenation and white space normalization happen during this step. Next a series of normalization functions will
+    // transform the value into final form. Each normalization copies the value from one half of the buffer to the other. Based on whether the number of normalization functions
+    // is odd or even, the initial placement in the buffer is chosen so that the final normalization leaves the field value at the front of the buffer. The buffer space actually
+    // used is locked down in the scratchPad. The remainder of the first half and all of the second half are returned to the scratchPad for future use.
+    if (concatenateRepeats) bufferLength += numMatches - 1;    // allow space for concatenation commas
+    // Round up to multiple of eight so that both halves are 64-bit aligned.
+    // 200 is a "way too big" fudge factor to allow for modest expansion of field size during normalization. Needs improvement.
+    bufferLength += (8-bufferLength%8)%8 + 200;
+    uint8_t * const scratch = scratchPad.request(2*bufferLength);
+    if (scratch == nullptr) {
+        resultField.length = STAT_INSUFMEMORY;
+        return;
+    }
+
+    uint8_t * const frontHalf = scratch;
+    uint8_t * const backHalf = scratch + bufferLength;
+    uint8_t *working = (numNormalizers%2 == 0) ? frontHalf : backHalf;
+    int currMatch = firstMatch;
+    int32_t dataLength = 0;
+    for (int j=0; j < numMatches; j++) {
+        if (j >= 1) {
+            *working++ = ',';
+            dataLength++;
+            while (headerNameId[++currMatch] != headId);
+        }
+        int32_t growth = deriveHeaderContent(headerValue[currMatch].start, headerValue[currMatch].length, working);
+        working += growth;
+        dataLength += growth;
+        if (!concatenateRepeats) break;
+    }
+
+    for (int i=0; i < numNormalizers; i++) {
+        if (i%2 != numNormalizers%2) dataLength = normalizer[i](backHalf, dataLength, frontHalf, infractions, normArg[i]);
+        else                         dataLength = normalizer[i](frontHalf, dataLength, backHalf, infractions, normArg[i]);
+        if (dataLength <= 0) {
+            resultField.length = dataLength;
+            return;
+        }
+    }
+    resultField.start = scratch;
+    resultField.length = dataLength;
+    scratchPad.commit(dataLength);
+}
+
+// Collection of stock normalization functions. This will probably grow throughout the life of the software. New functions must follow the standard signature.
+// The void* at the end is for any special configuration data the function requires.
+
+int32_t normDecimalInteger(const uint8_t* inBuf, int32_t inLength, uint8_t* outBuf, uint64_t& infractions, const void *) {
+    // Limited to 18 decimal digits, not including leading zeros, to fit comfortably into int64_t
+    int64_t total = 0;
+    int nonLeadingZeros = 0;
+    for (int32_t k=0; k < inLength; k++) {
+        int value = inBuf[k] - '0';
+        if (nonLeadingZeros || (value != 0)) nonLeadingZeros++;
+        if (nonLeadingZeros > 18) {
+            infractions |= INF_BADHEADERDATA;
+            return STAT_PROBLEMATIC;
+        }
+        if ((value < 0) || (value > 9)) {
+            infractions |= INF_BADHEADERDATA;
+            return STAT_PROBLEMATIC;
+        }
+        total = total*10 + value;
+    }
+    ((int64_t*)outBuf)[0] = total;
+    return sizeof(int64_t);
+}
+
+
+int32_t norm2Lower(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t&, const void *) {
+    for (int32_t k=0; k < inLength; k++) {
+        outBuf[k] = ((inBuf[k] < 'A') || (inBuf[k] > 'Z')) ? inBuf[k] : inBuf[k] - ('A' - 'a');
+    }
+    return inLength;
+}
+
+
+int32_t normStrCode(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t&, const void *table) {
+    ((int64_t*)outBuf)[0] = strToCode(inBuf, inLength, (const StrCode*)table);
+    return sizeof(int64_t);
+}
+
+int32_t normSeqStrCode(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t&, const void *table) {
+    int32_t numCodes = 0;
+    const uint8_t* start = inBuf;
+    while (true) {
+        int32_t length;
+        for (length = 0; (start + length < inBuf + inLength) && (start[length] != ','); length++);
+        if (length == 0) ((uint32_t*)outBuf)[numCodes++] = STAT_EMPTYSTRING;
+        else ((int64_t*)outBuf)[numCodes++] = strToCode(start, length, (const StrCode*)table);
+        if (start + length >= inBuf + inLength) break;
+        start += length + 1;
+    }
+    return numCodes * sizeof(int64_t);
+}
+
+// Remove all space and tab characters (known as LWS or linear white space in the RFC)
+int32_t normRemoveLws(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t&, const void *) {
+    int32_t length = 0;
+    for (int32_t k = 0; k < inLength; k++) {
+        if ((inBuf[k] != ' ') && (inBuf[k] != '\t')) outBuf[length++] = inBuf[k];
+    }
+    return length;
+}
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.h b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.h
new file mode 100644 (file)
index 0000000..126fe88
--- /dev/null
@@ -0,0 +1,80 @@
+/****************************************************************************
+ *
+** 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      HeaderNormalizer class declaration
+//
+
+#ifndef NHTTP_HEAD_NORM_H
+#define NHTTP_HEAD_NORM_H
+
+#include "nhttp_scratch_pad.h"
+
+//-------------------------------------------------------------------------
+// HeaderNormalizer class
+// Strategies for normalizing HTTP header field values
+//-------------------------------------------------------------------------
+
+// Three normalization functions per HeaderNormalizer seems likely to be enough. Nothing subtle will break if you choose to expand it to four or more. Just a whole bunch of
+// signatures and initializers to update.
+// When defining a HeaderNormalizer don't leave holes in the normalizer list. E.g. if you have two normalizers they must be first and second. If you do first and third
+// instead it won't explode but the third one won't be used either.
+
+class HeaderNormalizer {
+public:
+    constexpr HeaderNormalizer(NHttpEnums::NormFormat _format, bool _concatenateRepeats, bool _infractRepeats, int32_t (*f1)(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*),
+       const void *f1Arg, int32_t (*f2)(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*), const void *f2Arg, int32_t (*f3)(const uint8_t*, int32_t, uint8_t*, uint64_t&,
+       const void*), const void *f3Arg) :
+          format(_format),
+          concatenateRepeats(_concatenateRepeats),
+          infractRepeats(_infractRepeats),
+          normalizer { f1, f2, f3 },
+          normArg { f1Arg, f2Arg, f3Arg },
+          numNormalizers((f1 != nullptr) + (f1 != nullptr)*(f2 != nullptr) + (f1 != nullptr)*(f2 != nullptr)*(f3 != nullptr)) {};
+    void normalize(ScratchPad &scratchPad, uint64_t &infractions, NHttpEnums::HeaderId headId, const NHttpEnums::HeaderId headerNameId[], const field headerName[], int32_t numHeaders,
+       field &resultField) const;
+    NHttpEnums::NormFormat getFormat() const {return format;};
+
+private:
+    static int32_t deriveHeaderContent(const uint8_t *value, int32_t length, uint8_t *buffer);
+
+    const NHttpEnums::NormFormat format;
+    const bool concatenateRepeats;
+    const bool infractRepeats;
+    int32_t (* const normalizer[3])(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*);
+    const void * normArg[3];
+    const int numNormalizers;
+};
+
+// Normalizer functions
+
+int32_t normDecimalInteger(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
+int32_t norm2Lower(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
+int32_t normStrCode(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*);
+int32_t normSeqStrCode(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*);
+int32_t normRemoveLws(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
+
+#endif
+
+
index fb6e1966acd7aa2ff74c3a4f7448bb6eb88d4a29..2eef1eea80ab6324cbc268922d28ccaa860029bb 100644 (file)
 #include <stdexcept>
 
 #include "snort.h"
-#include "framework/inspector.h"
-#include "flow/flow.h"
+#include "stream/stream_api.h"
 #include "nhttp_enum.h"
-#include "nhttp_scratchpad.h"
-#include "nhttp_strtocode.h"
-#include "nhttp_headnorm.h"
-#include "nhttp_flowdata.h"
-#include "nhttp_msgheader.h"
-#include "nhttp_testinput.h"
+#include "nhttp_stream_splitter.h"
 #include "nhttp_api.h"
 #include "nhttp_inspect.h"
 
-const char* NHttpInspect::testInputFile = "nhttptestmsgs.txt";
-const char* NHttpInspect::testOutputPrefix = "nhttpresults/testcase";
+using namespace NHttpEnums;
+
 THREAD_LOCAL NHttpMsgHeader* NHttpInspect::msgHead;
+THREAD_LOCAL NHttpMsgBody* NHttpInspect::msgBody;
+THREAD_LOCAL NHttpMsgChunkHead* NHttpInspect::msgChunkHead;
+THREAD_LOCAL NHttpMsgChunkBody* NHttpInspect::msgChunkBody;
+THREAD_LOCAL NHttpMsgTrailer* NHttpInspect::msgTrailer;
 
-NHttpInspect::NHttpInspect(bool _test_mode) : test_mode(_test_mode)
+NHttpInspect::NHttpInspect(bool test_mode)
 {
-    printf("NHttpInspect constructor()\n");
-    if (test_mode) {
-        testInput = new NHttpTestInput(testInputFile);
+    NHttpTestInput::test_mode = test_mode;
+    if (NHttpTestInput::test_mode) {
+        NHttpTestInput::testInput = new NHttpTestInput(testInputFile);
     }
 }
 
 NHttpInspect::~NHttpInspect ()
 {
-    printf("NHttpInspect destructor()\n");
-    if (test_mode) {
-        delete testInput;
+    if (NHttpTestInput::test_mode) {
+        delete NHttpTestInput::testInput;
         if (testOut) fclose(testOut);
     }
 }
 
 bool NHttpInspect::enabled ()
 {
-    printf("NHttpInspect enabled()\n");
     return true;
 }
 
-void NHttpInspect::configure (SnortConfig *sc, const char*, char *args)
+bool NHttpInspect::configure (SnortConfig *)
 {
-    printf("NHttpInspect configure()\n");
+    return true;
 }
 
-int NHttpInspect::verify(SnortConfig* sc)
+int NHttpInspect::verify(SnortConfig*)
 {
-    printf("NHttpInspect verify()\n");
     return 0; // 0 = good, -1 = bad
 }
 
 void NHttpInspect::pinit()
 {
-    printf("NHttpInspect pinit()\n");
 }
 
 void NHttpInspect::pterm()
 {
-    printf("NHttpInspect pterm()\n");
 }
 
 void NHttpInspect::show(SnortConfig*)
 {
-    printf("NHttpInspect show()\n");
     LogMessage("NHttpInspect\n");
 }
 
 void NHttpInspect::eval (Packet* p)
 {
-    printf("NHttpInspect eval()\n");
+    // Only packets from the StreamSplitter can be processed
+    if (!PacketHasPAFPayload(p)) return;
 
     Flow *flow = p->flow;
     NHttpFlowData* sessionData = (NHttpFlowData*)flow->get_application_data(NHttpFlowData::nhttp_flow_id);
-    if (sessionData == nullptr) flow->set_application_data(sessionData = new NHttpFlowData);
-
-    if (!test_mode) msgHead->loadMessage(p->data, p->dsize, sessionData);
+    assert(sessionData);
+
+    NHttpMsgSection *msgSect = nullptr;
+
+    if (!NHttpTestInput::test_mode) {
+        switch (sessionData->sectionType) {
+          case SEC_HEADER: msgSect = msgHead; break;
+          case SEC_BODY: msgSect = msgBody; break;
+          case SEC_CHUNKHEAD: msgSect = msgChunkHead; break;
+          case SEC_CHUNKBODY: msgSect = msgChunkBody; break;
+          case SEC_TRAILER: msgSect = msgTrailer; break;
+          case SEC_DISCARD: return;
+          default: assert(0); return;
+        }
+        msgSect->loadSection(p->data, p->dsize, sessionData);
+    }
     else {
         uint8_t *testBuffer;
-        int32_t testLength;
-        if ((testLength = testInput->ntiGet(&testBuffer, sessionData, testNumber)) > 0) {
-            msgHead->loadMessage(testBuffer, testLength, sessionData);
+        uint16_t testLength;
+        if ((testLength = NHttpTestInput::testInput->toEval(&testBuffer, testNumber)) > 0) {
+            switch (sessionData->sectionType) {
+              case SEC_HEADER: msgSect = msgHead; break;
+              case SEC_BODY: msgSect = msgBody; break;
+              case SEC_CHUNKHEAD: msgSect = msgChunkHead; break;
+              case SEC_CHUNKBODY: msgSect = msgChunkBody; break;
+              case SEC_TRAILER: msgSect = msgTrailer; break;
+              case SEC_DISCARD: return;
+              default: assert(0); return;
+            }
+            msgSect->loadSection(testBuffer, testLength, sessionData);
         }
         else {
-            printf("Out of test data.\n");
+            printf("Zero length test data.\n");
             return;
         }
     }
+    msgSect->initSection();
+    msgSect->analyze();
+    msgSect->updateFlow();
+    msgSect->genEvents();
+    msgSect->legacyClients();
 
-    msgHead->analyze();
-
-    msgHead->genEvents();
-
-    // Interface to the old Snort clients
-    msgHead->oldClients();
-
-    if (!test_mode) msgHead->printMessage(stdout);
+    if (!NHttpTestInput::test_mode) msgSect->printMessage(stdout);
     else {
         if (testNumber != fileTestNumber) {
             if (testOut) fclose (testOut);
             fileTestNumber = testNumber;
             char fileName[100];
-            sprintf(fileName, "%s%d.txt", testOutputPrefix, testNumber);
+            snprintf(fileName, sizeof(fileName), "%s%" PRIi64 ".txt", testOutputPrefix, testNumber);
             if ((testOut = fopen(fileName, "w+")) == nullptr) throw std::runtime_error("Cannot open test output file");
         }
-        msgHead->printMessage(testOut);
+        msgSect->printMessage(testOut);
     }
 }
 
 
 
 
-
-
index 4a1d577e12a9cfe4e1e5856f8fa8d517bd9c9447..8c893d6707f034f3c1776818f5486543e66a0a43 100644 (file)
 // NHttpInspect class
 //-------------------------------------------------------------------------
 
+#include "framework/inspector.h"
+#include "nhttp_msg_head.h"
+#include "nhttp_msg_body.h"
+#include "nhttp_msg_chunk_head.h"
+#include "nhttp_msg_chunk_body.h"
+#include "nhttp_msg_trailer.h"
+#include "nhttp_stream_splitter.h"
+#include "nhttp_test_input.h"
+
+class NHttpApi;
+
 class NHttpInspect : public Inspector {
 public:
     NHttpInspect(bool _test_mode);
     ~NHttpInspect();
 
-    void configure(SnortConfig*, const char*, char *args);
+    bool configure(SnortConfig*);
     int verify(SnortConfig*);
     void show(SnortConfig*);
     void eval(Packet*);
     bool enabled();
     void pinit();
     void pterm();
+    NHttpStreamSplitter* get_splitter(bool isClientToServer) { return new NHttpStreamSplitter(isClientToServer); };
 
 private:
     friend NHttpApi;
     static THREAD_LOCAL NHttpMsgHeader *msgHead;
+    static THREAD_LOCAL NHttpMsgBody *msgBody;
+    static THREAD_LOCAL NHttpMsgChunkHead *msgChunkHead;
+    static THREAD_LOCAL NHttpMsgChunkBody *msgChunkBody;
+    static THREAD_LOCAL NHttpMsgTrailer *msgTrailer;
 
     // Test mode
-    bool test_mode;
-    static const char *testInputFile;
-    static const char *testOutputPrefix;
-    NHttpTestInput *testInput = nullptr;
+    const char *testInputFile = "nhttp_test_msgs.txt";
+    const char *testOutputPrefix = "nhttpresults/testcase";
     FILE *testOut = nullptr;
     int64_t testNumber = 0;
     int64_t fileTestNumber = -1;
index 81e0e9a504b6c17dbc17976ad32ccf11b481e26d..3ef609a6aca1a1ac3ef71030b0ec803feab1eb27 100644 (file)
 //  @brief      Module class for NHttpInspect
 //
 
-
 #include <assert.h>
 #include <string.h>
 #include <sys/types.h>
 #include "snort.h"
-#include "framework/module.h"
 #include "nhttp_enum.h"
 #include "nhttp_module.h"
 
+NHttpModule::NHttpModule() : Module("nhttp_inspect", nhttpParams, nhttpEvents) {
+}
+
+
 const Parameter NHttpModule::nhttpParams[] =
     {{ "test_mode", Parameter::PT_BOOL, nullptr, "false", "read HTTP messages from text file" },
      { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }};
 
 bool NHttpModule::begin(const char*, int, SnortConfig*) {
-    printf("NHttpModule begin()\n");
     test_mode = false;
     return true;
 }
 
 bool NHttpModule::end(const char*, int, SnortConfig*) {
-    printf("NHttpModule end()\n");
     return true;
 }
 
 bool NHttpModule::set(const char*, Value &val, SnortConfig*) {
-    printf("NHttpModule set()\n");
     if (val.is("test_mode")) {
         test_mode = val.get_bool();
         return true;
@@ -60,6 +59,6 @@ bool NHttpModule::set(const char*, Value &val, SnortConfig*) {
 }
 
 unsigned NHttpModule::get_gid() const {
-    return GID_HTTP_CLIENT;
+    return NHTTP_GID;
 }
 
index 37c55b0ba01da8ebf885504df800b6785b092fd8..e2bead00c3c8476ac4df5b94bab9a3e362c78d49 100644 (file)
 #ifndef NHTTP_MODULE_H
 #define NHTTP_MODULE_H
 
+#include "framework/module.h"
+
 class NHttpModule : public Module
 {
 public:
-    NHttpModule() : Module("nhttp_inspect", nhttpParams) { };
+    NHttpModule();
     bool begin(const char*, int, SnortConfig*);
     bool end(const char*, int, SnortConfig*);
     bool set(const char*, Value&, SnortConfig*);
@@ -40,6 +42,7 @@ public:
     bool get_test_mode() const { return test_mode; };
 private:
     static const Parameter nhttpParams[];
+    static const RuleMap nhttpEvents[];
     bool test_mode = false;
 };
 
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc
new file mode 100644 (file)
index 0000000..4e426cd
--- /dev/null
@@ -0,0 +1,129 @@
+/****************************************************************************
+ *
+** 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      NHttpMsgBody class analyzes individual HTTP message bodies. Message chunks are handled in NHttpMsgChunk.
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "snort.h"
+#include "nhttp_enum.h"
+#include "nhttp_msg_body.h"
+
+using namespace NHttpEnums;
+
+void NHttpMsgBody::loadSection(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_) {
+    NHttpMsgSection::loadSection(buffer, bufSize, sessionData_);
+
+    dataLength = sessionData->dataLength[sourceId];
+    bodySections = sessionData->bodySections[sourceId];
+    bodyOctets = sessionData->bodyOctets[sourceId];
+}
+
+void NHttpMsgBody::initSection() {
+    data.length = STAT_NOTCOMPUTE;
+}
+
+void NHttpMsgBody::analyze() {
+    bodySections++;
+    bodyOctets += length;
+    data.start = msgText;
+    data.length = length;
+
+    // The following statement tests for the case where streams underfulfilled flush due to a TCP connection close
+    if ((length < 16384) && (bodyOctets < dataLength)) tcpClose = true;
+    if (tcpClose && (bodyOctets < dataLength)) infractions |= INF_TRUNCATED;
+}
+
+void NHttpMsgBody::genEvents() {
+    if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
+}
+
+void NHttpMsgBody::printMessage(FILE *output) const {
+    NHttpMsgSection::printMessageTitle(output, "body");
+    fprintf(output, "Expected data length %" PRIi64 ", sections seen %" PRIi64 ", octets seen %" PRIi64 "\n", dataLength, bodySections, bodyOctets);
+    printInterval(output, "Data", data.start, data.length);
+    NHttpMsgSection::printMessageWrapup(output);
+}
+
+void NHttpMsgBody::updateFlow() const {
+    if (tcpClose) {
+        sessionData->typeExpected[sourceId] = SEC_CLOSED;
+        sessionData->halfReset(sourceId);
+    }
+    else if (bodyOctets < dataLength) {
+        // More body coming
+        sessionData->bodySections[sourceId] = bodySections;
+        sessionData->bodyOctets[sourceId] = bodyOctets;
+    }
+    else {
+        // End of message
+        sessionData->typeExpected[sourceId] = SEC_HEADER;
+        sessionData->halfReset(sourceId);
+    }
+}
+
+
+// Legacy support function. Puts message fields into the buffers used by old Snort.
+void NHttpMsgBody::legacyClients() const {
+    ClearHttpBuffers();
+    if (data.length > 0) SetHttpBuffer(HTTP_BUFFER_CLIENT_BODY, data.start, (unsigned)data.length);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_body.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_body.h
new file mode 100644 (file)
index 0000000..86c919f
--- /dev/null
@@ -0,0 +1,74 @@
+/****************************************************************************
+ *
+** 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      NHttpMsgBody class declaration
+//
+
+#ifndef NHTTP_MSG_BODY_H
+#define NHTTP_MSG_BODY_H
+
+#include "nhttp_msg_section.h"
+
+//-------------------------------------------------------------------------
+// NHttpMsgBody class
+//-------------------------------------------------------------------------
+
+class NHttpMsgBody : public NHttpMsgSection {
+public:
+    NHttpMsgBody() {};
+    void loadSection(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_);
+    void initSection();
+    void analyze();
+    void printMessage(FILE *output) const;
+    void genEvents();
+    void updateFlow() const;
+    void legacyClients() const;
+
+protected:
+    int64_t dataLength;
+    int64_t bodySections;
+    int64_t bodyOctets;
+
+    field data;
+};
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.cc
new file mode 100644 (file)
index 0000000..33dfdc7
--- /dev/null
@@ -0,0 +1,146 @@
+/****************************************************************************
+ *
+** 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      NHttpMsgChunkBody class analyzes data portion (not start line) of an HTTP chunk.
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "snort.h"
+#include "nhttp_enum.h"
+#include "nhttp_msg_chunk_body.h"
+
+using namespace NHttpEnums;
+
+void NHttpMsgChunkBody::loadSection(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_) {
+    NHttpMsgBody::loadSection(buffer, bufSize, sessionData_);
+
+    numChunks = sessionData->numChunks[sourceId];
+    chunkSections = sessionData->chunkSections[sourceId];
+    chunkOctets = sessionData->chunkOctets[sourceId];
+}
+
+void NHttpMsgChunkBody::initSection() {
+    NHttpMsgBody::initSection();
+}
+
+void NHttpMsgChunkBody::analyze() {
+    bodySections++;
+    chunkOctets += length;
+    bodyOctets += length;
+    int termCrlfBytes = 0;
+    if (chunkOctets > dataLength) {
+        // Final <CR><LF> are not data and do not belong in octet total or data field
+        termCrlfBytes = chunkOctets - dataLength;
+        assert(termCrlfBytes <= 2);
+        bodyOctets -= termCrlfBytes;
+        // Check for correct CRLF termination. Beware the section might break just before chunk end.
+        if ( ! ( ((termCrlfBytes == 2) && (length >= 2) && (msgText[length-2] == '\r') && (msgText[length-1] == '\n')) ||
+                 ((termCrlfBytes == 2) && (length == 1) && (msgText[length-1] == '\n')) ||
+                 ((termCrlfBytes == 1) && (msgText[length-1] == '\r')) ) ) {
+            infractions |= INF_BROKENCHUNK;
+        }
+    }
+
+    data.start = msgText;
+    data.length = length - termCrlfBytes;
+
+    chunkSections++;
+    // The following statement tests for the case where streams underfulfilled flush due to a TCP connection close
+    if ((length < 16384) && (bodyOctets + termCrlfBytes < dataLength + 2)) tcpClose = true;
+    if (tcpClose) infractions |= INF_TRUNCATED;
+}
+
+void NHttpMsgChunkBody::genEvents() {
+    if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
+}
+
+void NHttpMsgChunkBody::printMessage(FILE *output) const {
+    NHttpMsgSection::printMessageTitle(output, "chunk body");
+    fprintf(output, "Expected chunk length %" PRIi64 ", cumulative sections %" PRIi64 ", cumulative octets %" PRIi64 "\n", dataLength, bodySections, bodyOctets);
+    fprintf(output, "cumulative chunk sections %" PRIi64 ", cumulative chunk octets %" PRIi64 "\n", chunkSections, chunkOctets);
+    printInterval(output, "Data", data.start, data.length);
+    NHttpMsgSection::printMessageWrapup(output);
+}
+
+void NHttpMsgChunkBody::updateFlow() const {
+    if (tcpClose) {
+        sessionData->typeExpected[sourceId] = SEC_CLOSED;
+        sessionData->halfReset(sourceId);
+    }
+    else if (chunkOctets < dataLength + 2) {
+        sessionData->bodySections[sourceId] = bodySections;
+        sessionData->bodyOctets[sourceId] = bodyOctets;
+        sessionData->chunkSections[sourceId] = chunkSections;
+        sessionData->chunkOctets[sourceId] = chunkOctets;
+    }
+    else {
+        sessionData->typeExpected[sourceId] = SEC_CHUNKHEAD;
+        sessionData->octetsExpected[sourceId] = STAT_NOTPRESENT;
+        sessionData->dataLength[sourceId] = STAT_NOTPRESENT;
+        sessionData->bodySections[sourceId] = bodySections;
+        sessionData->bodyOctets[sourceId] = bodyOctets;
+        sessionData->chunkSections[sourceId] = STAT_NOTPRESENT;
+        sessionData->chunkOctets[sourceId] = STAT_NOTPRESENT;
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.h
new file mode 100644 (file)
index 0000000..6da7b02
--- /dev/null
@@ -0,0 +1,71 @@
+/****************************************************************************
+ *
+** 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      NHttpMsgChunkBody class declaration
+//
+
+#ifndef NHTTP_MSG_CHUNK_BODY_H
+#define NHTTP_MSG_CHUNK_BODY_H
+
+#include "nhttp_msg_body.h"
+
+//-------------------------------------------------------------------------
+// NHttpMsgChunkBody class
+//-------------------------------------------------------------------------
+
+class NHttpMsgChunkBody : public NHttpMsgBody {
+public:
+    NHttpMsgChunkBody() {};
+    void loadSection(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_);
+    void initSection();
+    void analyze();
+    void printMessage(FILE *output) const;
+    void genEvents();
+    void updateFlow() const;
+
+private:
+    int64_t numChunks;
+    int64_t chunkSections;
+    int64_t chunkOctets;
+};
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.cc
new file mode 100644 (file)
index 0000000..edb87d3
--- /dev/null
@@ -0,0 +1,157 @@
+/****************************************************************************
+ *
+** 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      NHttpMsgChunkHead class analyzes header line for a chunk.
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "snort.h"
+#include "nhttp_enum.h"
+#include "nhttp_msg_chunk_head.h"
+
+using namespace NHttpEnums;
+
+// Convert the hexadecimal chunk length.
+// RFC says that zero may be written with multiple digits "000000".
+// Arbitrary limit of 15 hex digits not including leading zeros ensures in a simple way against 64-bit overflow and should be
+// vastly bigger than any legitimate chunk.
+void NHttpMsgChunkHead::deriveChunkLength() {
+    if (chunkSize.length <= 0) {
+        dataLength = STAT_PROBLEMATIC;
+        infractions |= INF_BADCHUNKSIZE;
+        return;
+    }
+    dataLength = 0;
+    int nonLeadingZeros = 0;
+    for (int k=0; k < chunkSize.length; k++) {
+        if (nonLeadingZeros || (chunkSize.start[k] != '0')) nonLeadingZeros++;
+        if (nonLeadingZeros > 15) {
+            dataLength = STAT_PROBLEMATIC;
+            infractions |= INF_BADCHUNKSIZE;
+            return;
+        }
+
+        dataLength *= 16;
+        if ((chunkSize.start[k] >= '0') && (chunkSize.start[k] <= '9')) dataLength += chunkSize.start[k] - '0';
+        else if ((chunkSize.start[k] >= 'A') && (chunkSize.start[k] <= 'F')) dataLength += chunkSize.start[k] - 'A' + 10;
+        else if ((chunkSize.start[k] >= 'a') && (chunkSize.start[k] <= 'f')) dataLength += chunkSize.start[k] - 'a' + 10;
+        else {
+            dataLength = STAT_PROBLEMATIC;
+            infractions |= INF_BADCHUNKSIZE;
+            return;
+        }
+    }
+}
+
+void NHttpMsgChunkHead::loadSection(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_) {
+    NHttpMsgSection::loadSection(buffer, bufSize, sessionData_);
+
+    bodySections = sessionData->bodySections[sourceId];
+    numChunks = sessionData->numChunks[sourceId];
+}
+
+void NHttpMsgChunkHead::initSection() {
+    startLine.length = STAT_NOTCOMPUTE;
+    chunkSize.length = STAT_NOTCOMPUTE;
+    chunkExtensions.length = STAT_NOTCOMPUTE;
+}
+
+void NHttpMsgChunkHead::analyze() {
+    bodySections++;
+    // First section in a new chunk is just the start line.
+    numChunks++;
+    startLine.start = msgText;
+    if (!tcpClose) startLine.length = length - 2;
+    else startLine.length = findCrlf(startLine.start, length, false);
+    chunkSize.start = msgText;
+    // Start line format is chunk size in hex followed by optional semicolon and extensions field
+    for (chunkSize.length = 0; (chunkSize.length < startLine.length) && (startLine.start[chunkSize.length] != ';'); chunkSize.length++);
+    if (chunkSize.length == startLine.length) {
+        chunkExtensions.length = STAT_NOTPRESENT;
+    }
+    else if (chunkSize.length == startLine.length - 1) {
+        chunkExtensions.length = STAT_EMPTYSTRING;
+    }
+    else {
+        chunkExtensions.start = msgText + chunkSize.length + 1;
+        chunkExtensions.length = startLine.length - chunkSize.length - 1;
+    }
+    deriveChunkLength();
+    if (tcpClose) infractions |= INF_TRUNCATED;
+}
+
+void NHttpMsgChunkHead::genEvents() {
+    if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
+}
+
+void NHttpMsgChunkHead::printMessage(FILE *output) const {
+    NHttpMsgSection::printMessageTitle(output, "chunk header");
+    fprintf(output, "Chunk size: %" PRIi64 "\n", dataLength);
+    printInterval(output, "Chunk extensions", chunkExtensions.start, chunkExtensions.length);
+    NHttpMsgSection::printMessageWrapup(output);
+}
+
+void NHttpMsgChunkHead::updateFlow() const {
+    if (tcpClose) {
+        sessionData->typeExpected[sourceId] = SEC_CLOSED;
+        sessionData->halfReset(sourceId);
+    }
+    else if (dataLength > 0) {
+        sessionData->typeExpected[sourceId] = SEC_CHUNKBODY;
+        sessionData->octetsExpected[sourceId] = dataLength+2;
+        sessionData->bodySections[sourceId] = bodySections;
+        sessionData->numChunks[sourceId] = numChunks;
+        sessionData->dataLength[sourceId] = dataLength;
+        sessionData->chunkSections[sourceId] = 0;
+        sessionData->chunkOctets[sourceId] = 0;
+    }
+    else {
+        // This was zero-length last chunk, trailer comes next
+        sessionData->typeExpected[sourceId] = SEC_TRAILER;
+        sessionData->halfReset(sourceId);
+    }
+}
+
+
+// Legacy support function. Puts message fields into the buffers used by old Snort.
+void NHttpMsgChunkHead::legacyClients() const {
+    ClearHttpBuffers();
+    if (startLine.length > 0) SetHttpBuffer(HTTP_BUFFER_CLIENT_BODY, startLine.start, (unsigned)startLine.length);
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.h
new file mode 100644 (file)
index 0000000..a63dc48
--- /dev/null
@@ -0,0 +1,78 @@
+/****************************************************************************
+ *
+** 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      NHttpMsgChunkHead class declaration
+//
+
+#ifndef NHTTP_MSG_CHUNK_HEAD_H
+#define NHTTP_MSG_CHUNK_HEAD_H
+
+#include "nhttp_msg_section.h"
+
+//-------------------------------------------------------------------------
+// NHttpMsgChunkHead class
+//-------------------------------------------------------------------------
+
+class NHttpMsgChunkHead : public NHttpMsgSection {
+public:
+    NHttpMsgChunkHead() {};
+    void loadSection(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_);
+    void initSection();
+    void analyze();
+    void printMessage(FILE *output) const;
+    void genEvents();
+    void updateFlow() const;
+    void legacyClients() const;
+
+private:
+    void deriveChunkLength();
+
+    field startLine;
+    field chunkSize;
+    field chunkExtensions;
+
+    int64_t dataLength;
+    int64_t bodySections;
+    int64_t numChunks;
+};
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_head.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_head.cc
new file mode 100644 (file)
index 0000000..df7b13d
--- /dev/null
@@ -0,0 +1,319 @@
+/****************************************************************************
+ *
+** 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      NHttpMsgHeader class analyzes individual HTTP message headers.
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "snort.h"
+#include "nhttp_enum.h"
+#include "nhttp_test_input.h"  // &&& temporary to support TCP close workaround
+#include "nhttp_msg_head.h"
+
+using namespace NHttpEnums;
+
+// Reinitialize everything derived in preparation for analyzing a new message
+void NHttpMsgHeader::initSection() {
+    NHttpMsgSharedHead::initSection();
+    startLine.length = STAT_NOTCOMPUTE;
+    version.length = STAT_NOTCOMPUTE;
+    versionId = VERS__NOTCOMPUTE;
+    method.length = STAT_NOTCOMPUTE;
+    methodId = METH__NOTCOMPUTE;
+    uri.length = STAT_NOTCOMPUTE;
+    statusCode.length = STAT_NOTCOMPUTE;
+    statusCodeNum = STAT_NOTCOMPUTE;
+    reasonPhrase.length = STAT_NOTCOMPUTE;
+}
+
+// All the header processing that is done for every message (i.e. not just-in-time) is done here.
+void NHttpMsgHeader::analyze() {
+    NHttpMsgSharedHead::analyze();
+    if (sourceId == SRC_CLIENT) {
+        parseRequestLine();
+        deriveMethodId();
+    }
+    else if (sourceId == SRC_SERVER) {
+        parseStatusLine();
+        deriveStatusCodeNum();
+    }
+    deriveVersionId();
+}
+
+// All we do here is separate the start line from the header fields.
+// It is so complicated because 1) there might not be any header fields and 2) the message may have been truncated by a TCP connection close.
+// The asserts are very useful in test mode because they pick up bad test case data that we are not designed to handle. Otherwise they should
+// never go off unless PAF is broken and feeding us bad stuff.
+void NHttpMsgHeader::parseWhole() {
+    startLine.start = msgText;
+    startLine.length = findCrlf(startLine.start, length, false);
+    // findCrtl() guarentees that either the start line is the whole message or there must be at least two more characters and the first two are <CR><LF>.
+    assert((length == startLine.length) || ((length >= startLine.length+2) && !memcmp(msgText + startLine.length, "\r\n", 2)));
+
+// &&& The else clause is a workaround for the lack of TCP close notification support in the framework.
+// &&& Eventually the contents of the if clause should become the only code and the if-statement should go away.
+if (NHttpTestInput::test_mode) {
+    // We trust PAF. !tcpClose guarentees that either there are exactly four more characters <CR><LF><CR><LF> or there are at least seven more characters
+    // with the first two being <CR><LF> and the last four being <CR><LF><CR><LF>.
+    assert(tcpClose ||
+           ((length == startLine.length+4) && !memcmp(msgText + startLine.length, "\r\n\r\n", 4)) ||
+           ((length >= startLine.length+7) && !memcmp(msgText + startLine.length, "\r\n", 2) && !memcmp(msgText + length - 4, "\r\n\r\n", 4)));
+}
+else {
+    // We trust PAF. Therefore if the invariants don't hold it must be because we got the leftovers after a TCP close.
+    // So we kludge the close notification as a workaround.
+    if ( ! ( ((length == startLine.length+4) && !memcmp(msgText + startLine.length, "\r\n\r\n", 4)) ||
+             ((length >= startLine.length+7) && !memcmp(msgText + startLine.length, "\r\n", 2) && !memcmp(msgText + length - 4, "\r\n\r\n", 4)) ) ) tcpClose = true;
+}
+
+    // The following if-else ladder puts the extremely common normal cases at the beginning and the rare pathological cases at the end
+    // Normal case with header fields
+    if (!tcpClose && (length >= startLine.length+7)) {
+        headers.start = msgText + startLine.length + 2;
+        headers.length = length - startLine.length - 6;
+    }
+    // Normal case no header fields (only a start line)
+    else if (!tcpClose) {
+        headers.length = STAT_NOTPRESENT;
+    }
+    // Normal case with header fields and TCP connection close
+    else if ((length >= startLine.length+7) && !memcmp(msgText+length-4, "\r\n\r\n", 4)) {
+        headers.start = msgText + startLine.length + 2;
+        headers.length = length - startLine.length - 6;
+    }
+    // Normal case no header fields and TCP connection close
+    else if ((length == startLine.length + 4) && !memcmp(msgText+length-2, "\r\n", 2)) {
+        headers.length = STAT_NOTPRESENT;
+    }
+    // Abnormal cases truncated by TCP connection close
+    else {
+        infractions |= INF_TRUNCATED;
+        // Either start line incomplete or start line complete but no leftover octets for anything else
+        if (length <= startLine.length+2) {
+            headers.length = STAT_NOTPRESENT;
+        }
+        // Start line complete followed by lone <CR>
+        else if ((length == startLine.length+3) && (msgText[length-1] == '\r')) {
+            headers.length = STAT_NOTPRESENT;
+        }
+        // Truncation occurred somewhere in the header fields
+        else {
+            headers.start = msgText + startLine.length + 2;
+            headers.length = length - startLine.length - 2;
+            // When present, remove partial <CR><LF><CR><LF> sequence from the very end
+            if ((length > startLine.length+6) && !memcmp(msgText+length-3, "\r\n\r", 3)) headers.length -= 3;
+            else if ((length > startLine.length+5) && !memcmp(msgText+length-2, "\r\n", 2)) headers.length -= 2;
+            else if ((length > startLine.length+4) && (msgText[length-1] == '\r')) headers.length -= 1;
+        }
+    }
+}
+
+void NHttpMsgHeader::parseRequestLine() {
+    // There should be exactly two spaces. One following the method and one before "HTTP/".
+    // Eventually we may need to cater to certain format errors, but for now exact match or treat as error.
+    // <method><SP><URI><SP>HTTP/X.Y
+    if (startLine.start[startLine.length-9] != ' ') {
+        // space before "HTTP" missing or in wrong place
+        infractions |= INF_BADREQLINE;
+        return;
+    }
+
+    int space = -1;
+    for (int32_t k=0; k < startLine.length-9; k++) {
+        if (startLine.start[k] == ' ') {
+            if (space == -1) space = k;
+            else {
+                // too many spaces
+                infractions |= INF_BADREQLINE;
+                return;
+            }
+        }
+    }
+    if ((space <= 0)) {
+        // no first space or a leading space
+        infractions |= INF_BADREQLINE;
+        return;
+    }
+
+    method.start = startLine.start;
+    method.length = space;
+    uri.start = startLine.start + method.length + 1;
+    uri.length = startLine.length - method.length - 10;
+    version.start = startLine.start + (startLine.length - 8);
+    version.length = 8;
+    assert (startLine.length == method.length + uri.length + version.length + 2);
+}
+
+void NHttpMsgHeader::parseStatusLine() {
+    // Eventually we may need to cater to certain format errors, but for now exact match or treat as error.
+    // HTTP/X.Y<SP>###<SP><text>
+    if ((startLine.length < 13) || (startLine.start[8] != ' ') || (startLine.start[12] != ' ')) {
+        infractions |= INF_BADSTATLINE;
+        return;
+    }
+    version.start = startLine.start;
+    version.length = 8;
+    statusCode.start = startLine.start + 9;
+    statusCode.length = 3;
+    reasonPhrase.start = startLine.start + 13;
+    reasonPhrase.length = startLine.length - 13;
+    for (int32_t k = 0; k < reasonPhrase.length; k++) {
+        if ((reasonPhrase.start[k] <= 31) || (reasonPhrase.start[k] >= 127)) {
+            // Illegal character in reason phrase
+            infractions |= INF_BADPHRASE;
+            break;
+        }
+    }
+    assert (startLine.length == version.length + statusCode.length + reasonPhrase.length + 2);
+}
+
+void NHttpMsgHeader::deriveStatusCodeNum() {
+    if (statusCode.length != 3) {
+        statusCodeNum = STAT_PROBLEMATIC;
+        return;
+    }
+    if ((statusCode.start[0] < '0') || (statusCode.start[0] > '9') || (statusCode.start[1] < '0') || (statusCode.start[1] > '9') ||
+       (statusCode.start[2] < '0') || (statusCode.start[2] > '9')) {
+        infractions |= INF_BADSTATCODE;
+        statusCodeNum = STAT_PROBLEMATIC;
+        return;
+    }
+    statusCodeNum = (statusCode.start[0] - '0') * 100 + (statusCode.start[1] - '0') * 10 + (statusCode.start[2] - '0');
+    if ((statusCodeNum < 100) || (statusCodeNum > 599)) {
+        infractions |= INF_BADSTATCODE;
+    }
+}
+
+void NHttpMsgHeader::deriveVersionId() {
+    if (version.length != 8) {
+        versionId = VERS__PROBLEMATIC;
+        return;
+    }
+    if (memcmp(version.start, "HTTP/", 5) || (version.start[6] != '.')) {
+        versionId = VERS__PROBLEMATIC;
+        infractions |= INF_BADVERSION;
+    }
+    else if ((version.start[5] == '1') && (version.start[7] == '1')) {
+        versionId = VERS_1_1;
+    }
+    else if ((version.start[5] == '1') && (version.start[7] == '0')) {
+        versionId = VERS_1_0;
+    }
+    else if ((version.start[5] == '2') && (version.start[7] == '0')) {
+        versionId = VERS_2_0;
+    }
+    else if ((version.start[5] >= '0') && (version.start[5] <= '9') && (version.start[7] >= '0') && (version.start[7] <= '9')) {
+        versionId = VERS__OTHER;
+        infractions |= INF_UNKNOWNVERSION;
+    }
+    else {
+        versionId = VERS__PROBLEMATIC;
+        infractions |= INF_BADVERSION;
+    }
+}
+
+void NHttpMsgHeader::deriveMethodId() {
+    methodId = (MethodId) strToCode(method.start, method.length, methodList);
+}
+
+void NHttpMsgHeader::genEvents() {
+    if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
+}
+
+void NHttpMsgHeader::printMessage(FILE *output) const {
+    NHttpMsgSection::printMessageTitle(output, "header");
+
+    if (sourceId != SRC__NOTCOMPUTE) fprintf(output, "Source Id: %d\n", sourceId);
+    if (versionId != VERS__NOTCOMPUTE) fprintf(output, "Version Id: %d\n", versionId);
+    if (methodId != METH__NOTCOMPUTE) fprintf(output, "Method Id: %d\n", methodId);
+    if (statusCodeNum != STAT_NOTCOMPUTE) fprintf(output, "Status Code Num: %d\n", statusCodeNum);
+    printInterval(output, "Reason Phrase", reasonPhrase.start, reasonPhrase.length);
+    printInterval(output, "URI", uri.start, uri.length);
+
+    NHttpMsgSharedHead::printMessageHead(output);
+    NHttpMsgSection::printMessageWrapup(output);
+}
+
+
+void NHttpMsgHeader::updateFlow() const {
+    const uint64_t disasterMask = INF_BADREQLINE | INF_BADSTATLINE | INF_BROKENCHUNK | INF_BADCHUNKSIZE;
+
+    // The following logic to determine body type is by no means the last word on this topic.
+    if (tcpClose) {
+        sessionData->typeExpected[sourceId] = SEC_CLOSED;
+        sessionData->halfReset(sourceId);
+    }
+    else if (infractions & disasterMask) {
+        sessionData->typeExpected[sourceId] = SEC_ABORT;
+        sessionData->halfReset(sourceId);
+    }
+    else if ((sourceId == SRC_SERVER) && ((statusCodeNum <= 199) || (statusCodeNum == 204) || (statusCodeNum == 304))) {
+        // No body allowed by RFC for these response codes
+        sessionData->typeExpected[sourceId] = SEC_HEADER;
+        sessionData->halfReset(sourceId);
+    }
+    // If there is a Transfer-Encoding header, see if the last of the encoded values is "chunked".
+    else if ( (headerValueNorm[HEAD_TRANSFER_ENCODING].length > 0) &&
+         ((*(int64_t *)(headerValueNorm[HEAD_TRANSFER_ENCODING].start + (headerValueNorm[HEAD_TRANSFER_ENCODING].length - 8))) == TRANSCODE_CHUNKED) ) {
+        // Chunked body
+        sessionData->typeExpected[sourceId] = SEC_CHUNKHEAD;
+        sessionData->bodySections[sourceId] = 0;
+        sessionData->bodyOctets[sourceId] = 0;
+        sessionData->numChunks[sourceId] = 0;
+    }
+    else if ((headerValueNorm[HEAD_CONTENT_LENGTH].length > 0) && (*(int64_t*)headerValueNorm[HEAD_CONTENT_LENGTH].start > 0)) {
+        // Regular body
+        sessionData->typeExpected[sourceId] = SEC_BODY;
+        sessionData->octetsExpected[sourceId] = *(int64_t*)headerValueNorm[HEAD_CONTENT_LENGTH].start;
+        sessionData->dataLength[sourceId] = *(int64_t*)headerValueNorm[HEAD_CONTENT_LENGTH].start;
+        sessionData->bodySections[sourceId] = 0;
+        sessionData->bodyOctets[sourceId] = 0;
+    }
+    else {
+        // No body
+        sessionData->typeExpected[sourceId] = SEC_HEADER;
+        sessionData->halfReset(sourceId);
+    }
+}
+
+// Legacy support function. Puts message fields into the buffers used by old Snort.
+void NHttpMsgHeader::legacyClients() const {
+    NHttpMsgSharedHead::legacyClients();
+    if (method.length > 0) SetHttpBuffer(HTTP_BUFFER_METHOD, method.start, (unsigned)method.length);
+    if (uri.length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_URI, uri.start, (unsigned)uri.length);
+    if (uri.length > 0) SetHttpBuffer(HTTP_BUFFER_URI, uri.start, (unsigned)uri.length);
+    if (statusCode.length > 0) SetHttpBuffer(HTTP_BUFFER_STAT_CODE, statusCode.start, (unsigned)statusCode.length);
+    if (reasonPhrase.length > 0) SetHttpBuffer(HTTP_BUFFER_STAT_MSG, reasonPhrase.start, (unsigned)reasonPhrase.length);
+}
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_head.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_head.h
new file mode 100644 (file)
index 0000000..fbab3ee
--- /dev/null
@@ -0,0 +1,94 @@
+/****************************************************************************
+ *
+** 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      NHttpMsgHeader class declaration
+//
+
+#ifndef NHTTP_MSG_HEAD_H
+#define NHTTP_MSG_HEAD_H
+
+#include "nhttp_msg_head_shared.h"
+
+//-------------------------------------------------------------------------
+// NHttpMsgHeader class
+//-------------------------------------------------------------------------
+
+class NHttpMsgHeader: public NHttpMsgSharedHead {
+public:
+    NHttpMsgHeader() {};
+    void initSection();
+    void analyze();
+    void printMessage(FILE *output) const;
+    void genEvents();
+    void updateFlow() const;
+    void legacyClients() const;
+
+private:
+    // Code conversion tables are for turning token strings into enums.
+    static const StrCode methodList[];
+
+    // "Parse" methods cut things into pieces. "Derive" methods convert things into a new format such as an integer or enum token. "Normalize" methods convert
+    // things into a standard form without changing the underlying format.
+    void parseWhole();
+    void parseRequestLine();
+    void parseStatusLine();
+    void deriveStatusCodeNum();
+    void deriveVersionId();
+    void deriveMethodId();
+
+    // This is where all the derived values, extracted message parts, and normalized values are.
+    // Note that this is all scalars, buffer pointers, and buffer sizes. The actual buffers are in the message buffer (raw pieces) or the
+    // scratchPad (normalized pieces).
+    field startLine;
+    field version;
+    NHttpEnums::VersionId versionId;
+    field method;
+    NHttpEnums::MethodId methodId;
+    field uri;
+    field statusCode;
+    int32_t statusCodeNum;
+    field reasonPhrase;
+};
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc
new file mode 100644 (file)
index 0000000..a2279c8
--- /dev/null
@@ -0,0 +1,166 @@
+/****************************************************************************
+ *
+** 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      NHttpMsgSharedHead virtual class rolls up all the common elements of header processing and trailer processing.
+//
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "snort.h"
+#include "nhttp_enum.h"
+#include "nhttp_msg_head_shared.h"
+
+using namespace NHttpEnums;
+
+// Reinitialize everything derived in preparation for analyzing a new message
+void NHttpMsgSharedHead::initSection() {
+    headers.length = STAT_NOTCOMPUTE;
+    numHeaders = STAT_NOTCOMPUTE;
+    for(int k = 0; k < MAXHEADERS; k++) {
+        headerLine[k].length = STAT_NOTCOMPUTE;
+        headerName[k].length = STAT_NOTCOMPUTE;
+        headerNameId[k] = HEAD__NOTCOMPUTE;
+        headerValue[k].length = STAT_NOTCOMPUTE;
+    }
+    for (int k = 1; k < HEAD__MAXVALUE; k++) {
+        headerValueNorm[k].length = STAT_NOTCOMPUTE;
+    }
+}
+
+// All the header processing that is done for every message (i.e. not just-in-time) is done here.
+void NHttpMsgSharedHead::analyze() {
+    parseWhole();
+    parseHeaderBlock();
+    parseHeaderLines();
+    for (int j=0; j < MAXHEADERS; j++) {
+        if (headerName[j].length <= 0) break;
+        deriveHeaderNameId(j);
+    }
+    for (int k=1; k <= numNorms; k++) {
+        headerNorms[k]->normalize(scratchPad, infractions, (HeaderId)k, headerNameId, headerValue, MAXHEADERS, headerValueNorm[k]);
+    }
+}
+
+// Divide up the block of header fields into individual header field lines.
+void NHttpMsgSharedHead::parseHeaderBlock() {
+    if (headers.length <= 0) return;
+    int32_t bytesused = 0;
+    numHeaders = 0;
+    while (bytesused < headers.length) {
+        headerLine[numHeaders].start = headers.start + bytesused;
+        headerLine[numHeaders].length = findCrlf(headerLine[numHeaders].start, headers.length - bytesused, true);
+        bytesused += headerLine[numHeaders++].length + 2;
+        if (numHeaders >= MAXHEADERS) {
+             break;
+        }
+    }
+    if (bytesused < headers.length) {
+        infractions |= INF_TOOMANYHEADERS;
+    }
+}
+
+// Divide header field lines into field name and field value
+void NHttpMsgSharedHead::parseHeaderLines() {
+    int colon;
+    for (int k=0; k < numHeaders; k++) {
+        for (colon=0; colon < headerLine[k].length; colon++) {
+            if (headerLine[k].start[colon] == ':') break;
+        }
+        if (colon < headerLine[k].length) {
+            headerName[k].start = headerLine[k].start;
+            headerName[k].length = colon;
+            headerValue[k].start = headerLine[k].start + colon + 1;
+            headerValue[k].length = headerLine[k].length - colon - 1;
+        }
+        else {
+            infractions |= INF_BADHEADER;
+        }
+    }
+}
+
+void NHttpMsgSharedHead::deriveHeaderNameId(int index) {
+     if (headerName[index].length <= 0) return;
+    // Normalize header field name to lower case for matching purposes
+    uint8_t *lowerName;
+    if ((lowerName = scratchPad.request(headerName[index].length)) == nullptr) {
+        infractions |= INF_NOSCRATCH;
+        headerNameId[index] = HEAD__INSUFMEMORY;
+        return;
+    }
+    int32_t lowerLength = norm2Lower(headerName[index].start, headerName[index].length, lowerName, infractions, nullptr);
+    headerNameId[index] = (HeaderId) strToCode(lowerName, lowerLength, headerList);
+}
+
+void NHttpMsgSharedHead::genEvents() {
+    if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
+}
+
+// Legacy support function. Puts message fields into the buffers used by old Snort.
+void NHttpMsgSharedHead::legacyClients() const {
+    ClearHttpBuffers();
+
+    if (headers.length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_HEADER, headers.start, (unsigned)headers.length);
+    if (headers.length > 0) SetHttpBuffer(HTTP_BUFFER_HEADER, headers.start, (unsigned)headers.length);
+    for (int k=0; (headerNameId[k] != HEAD__NOTCOMPUTE) && (k < MAXHEADERS); k++) {
+        if (((headerNameId[k] == HEAD_COOKIE) && (sourceId == SRC_CLIENT)) || ((headerNameId[k] == HEAD_SET_COOKIE) && (sourceId == SRC_SERVER))) {
+            if (headerValue[k].length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_COOKIE, headerValue[k].start, (unsigned)headerValue[k].length);
+            break;
+        }
+    }
+
+    if ((sourceId == SRC_CLIENT) && (headerValueNorm[HEAD_COOKIE].length > 0))
+       SetHttpBuffer(HTTP_BUFFER_COOKIE, headerValueNorm[HEAD_COOKIE].start, (unsigned)headerValueNorm[HEAD_COOKIE].length);
+    else if ((sourceId == SRC_SERVER) && (headerValueNorm[HEAD_SET_COOKIE].length > 0))
+       SetHttpBuffer(HTTP_BUFFER_COOKIE, headerValueNorm[HEAD_SET_COOKIE].start, (unsigned)headerValueNorm[HEAD_SET_COOKIE].length);
+}
+
+void NHttpMsgSharedHead::printMessageHead(FILE *output) const {
+    char titleBuf[100];
+    if (numHeaders != STAT_NOTCOMPUTE) fprintf(output, "Number of headers: %d\n", numHeaders);
+    for (int j=0; j < numHeaders && j < 200; j++) {
+        snprintf(titleBuf, sizeof(titleBuf), "Header ID %d", headerNameId[j]);
+        printInterval(output, titleBuf, headerValue[j].start, headerValue[j].length);
+    }
+    for (int k=1; k <= numNorms; k++) {
+        if (headerValueNorm[k].length != STAT_NOTPRESENT) {
+            snprintf(titleBuf, sizeof(titleBuf), "Normalized header %d", k);
+            printInterval(output, titleBuf, headerValueNorm[k].start, headerValueNorm[k].length, true);
+        }
+    }
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.h
new file mode 100644 (file)
index 0000000..0716564
--- /dev/null
@@ -0,0 +1,105 @@
+/****************************************************************************
+ *
+** 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      NHttpMsgSharedHead class declaration
+//
+
+#ifndef NHTTP_MSG_HEAD_SHARED_H
+#define NHTTP_MSG_HEAD_SHARED_H
+
+#include "nhttp_str_to_code.h"
+#include "nhttp_head_norm.h"
+#include "nhttp_msg_section.h"
+
+//-------------------------------------------------------------------------
+// NHttpMsgSharedHead class
+//-------------------------------------------------------------------------
+
+class NHttpMsgSharedHead: public NHttpMsgSection {
+public:
+    void initSection();
+    void analyze();
+    void genEvents();
+    void legacyClients() const;
+
+protected:
+    // Header normalization. There should be one of these for every different way we can process a header field value.
+    static const HeaderNormalizer NORMALIZER_NIL;
+    static const HeaderNormalizer NORMALIZER_BASIC;
+    static const HeaderNormalizer NORMALIZER_CAT;
+    static const HeaderNormalizer NORMALIZER_NOREPEAT;
+    static const HeaderNormalizer NORMALIZER_DECIMAL;
+    static const HeaderNormalizer NORMALIZER_TRANSCODE;
+
+    // Master table of known header fields and their normalization strategies.
+    static const HeaderNormalizer* const headerNorms[];
+    static const int32_t numNorms;
+
+    // Code conversion tables are for turning token strings into enums.
+    static const StrCode headerList[];
+    static const StrCode transCodeList[];
+
+    // "Parse" methods cut things into pieces. "Derive" methods convert things into a new format such as an integer or enum token. "Normalize" methods convert
+    // things into a standard form without changing the underlying format.
+    virtual void parseWhole() = 0;
+    void parseHeaderBlock();
+    void parseHeaderLines();
+    void deriveHeaderNameId(int index);
+
+    void printMessageHead(FILE *output) const;
+
+    // This is where all the derived values, extracted message parts, and normalized values are.
+    // Note that this is all scalars, buffer pointers, and buffer sizes. The actual buffers are in the message buffer (raw pieces) or the
+    // scratchPad (normalized pieces).
+    field headers;
+    static const int MAXHEADERS = 200;  // I'm an arbitrary number. Need to revisit.
+    int32_t numHeaders;
+    field headerLine[MAXHEADERS];
+    field headerName[MAXHEADERS];
+    NHttpEnums::HeaderId headerNameId[MAXHEADERS];
+    field headerValue[MAXHEADERS];
+    field headerValueNorm[NHttpEnums::HEAD__MAXVALUE];
+};
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc
new file mode 100644 (file)
index 0000000..255b442
--- /dev/null
@@ -0,0 +1,107 @@
+/****************************************************************************
+ *
+** 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      NHttpMsgSection class is virtual parent for classes that analyze individual HTTP message sections.
+//
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "snort.h"
+#include "nhttp_enum.h"
+#include "nhttp_msg_section.h"
+
+using namespace NHttpEnums;
+
+// Return the number of octets before the first CRLF. Return length if CRLF not present.
+//
+// wrappable: CRLF does not count in a header field when immediately followed by <SP> or <LF>. These whitespace characters
+// at the beginning of the next line indicate that the previous header has wrapped and is continuing on the next line.
+uint32_t NHttpMsgSection::findCrlf(const uint8_t* buffer, int32_t length, bool wrappable) {
+    for (int32_t k=0; k < length-1; k++) {
+        if ((buffer[k] == '\r') && (buffer[k+1] == '\n'))
+            if (!wrappable || (k+2 >= length) || ((buffer[k+2] != ' ') && (buffer[k+2] != '\t'))) return k;
+    }
+    return length;
+}
+
+// Load a new message section
+void NHttpMsgSection::loadSection(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_) {
+    length = bufsize;
+    memcpy(rawBuf, buffer, length);
+
+    sessionData = sessionData_;
+    infractions = sessionData->infractions;
+    sourceId = sessionData->sourceId;
+    tcpClose = sessionData->tcpClose;
+
+    scratchPad.reinit();
+}
+
+void NHttpMsgSection::printInterval(FILE *output, const char* name, const uint8_t *text, int32_t length, bool intVals) {
+    if ((length == STAT_NOTPRESENT) || (length == STAT_NOTCOMPUTE)) return;
+    int outCount = fprintf(output, "%s, length = %d, ", name, length);
+    if (length <= 0) {
+        fprintf(output, "\n");
+        return;
+    }
+    if (text == nullptr) {
+        fprintf(output, "nullptr\n");
+        return;
+    }
+    if (length > 1000) length = 1000;    // Limit the amount of data printed
+    for (int k=0; k < length; k++) {
+        if ((text[k] >= 0x20) && (text[k] <= 0x7E)) fprintf(output, "%c", (char)text[k]);
+        else if (text[k] == 0xD) fprintf(output, "~");
+        else if (text[k] == 0xA) fprintf(output, "^");
+        else fprintf(output, "*");
+        if ((k%120 == (119 - outCount)) && (k+1 < length)) fprintf(output, "\n");
+    }
+
+    if (intVals && (length%8 == 0)) {
+        fprintf(output, "\nInteger values =");
+        for (int j=0; j < length; j+=8) {
+            fprintf(output, " %" PRIu64 , *((const uint64_t*)(text+j)));
+        }
+    }
+    fprintf(output, "\n");
+}
+
+void NHttpMsgSection::printMessageTitle(FILE *output, const char *title) const {
+    fprintf(output, "HTTP message %s:\n", title);
+    printInterval(output, "Input", msgText, length);
+}
+
+void NHttpMsgSection::printMessageWrapup(FILE *output) const {
+    fprintf(output, "Infractions: %lx, TCP Close: %s\n", infractions, tcpClose ? "True" : "False");
+    fprintf(output, "Interface to old clients. http_mask = %x.\n", http_mask);
+    for (int i=0; i < HTTP_BUFFER_MAX; i++) {
+        if ((1 << i) & http_mask) printInterval(output, http_buffer_name[i], http_buffer[i].buf, http_buffer[i].length);
+    }
+    fprintf(output, "\n");
+}
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h
new file mode 100644 (file)
index 0000000..7830873
--- /dev/null
@@ -0,0 +1,99 @@
+/****************************************************************************
+ *
+** 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      NHttpMsgSection class declaration
+//
+
+#ifndef NHTTP_MSG_SECTION_H
+#define NHTTP_MSG_SECTION_H
+
+#include "detection/detection_util.h"
+#include "nhttp_scratch_pad.h"
+#include "nhttp_flow_data.h"
+
+//-------------------------------------------------------------------------
+// NHttpMsgSection class
+//-------------------------------------------------------------------------
+
+class NHttpMsgSection {
+public:
+    virtual void loadSection(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_);
+    virtual ~NHttpMsgSection() = default;
+    virtual void initSection() = 0;
+    virtual void analyze() = 0;
+    virtual void printMessage(FILE *output) const = 0;
+    virtual void genEvents() = 0;
+    virtual void updateFlow() const = 0;
+    virtual void legacyClients() const = 0;
+
+protected:
+    // Convenience methods
+    static uint32_t findCrlf(const uint8_t* buffer, int32_t length, bool wrappable);
+    static void printInterval(FILE *output, const char* name, const uint8_t *text, int32_t length, bool intVals = false);
+    void printMessageTitle(FILE *output, const char *title) const;
+    void printMessageWrapup(FILE *output) const;
+
+    // The current strategy is to copy the entire raw message section into this object. Here it is.
+    int32_t length;               // Length of the original message section in octets
+    uint8_t rawBuf[NHttpEnums::MAXOCTETS];    // The original HTTP message section octets
+    // This pointer is the handle for working with the original message data. It makes it simple to later replace rawBuf with some other form of storage
+    // such as the buffer in the packet structure or something dynamic. Const x 2 because this pointer should never change and people working with the
+    // original message should not be changing it. Only loading a completely new message into rawBuf should do that.
+    const uint8_t * const msgText = rawBuf;
+
+    // Working space and storage for all the derived fields. See scratchPad.h for usage instructions.
+    // Allocation size may be complete overkill. Need to revisit this.
+    uint64_t derivedBuf[NHttpEnums::MAXOCTETS/8];
+    NHttpFlowData* sessionData;
+    ScratchPad scratchPad {derivedBuf, NHttpEnums::MAXOCTETS/8};
+
+    // This is where all the derived values, extracted message parts, and normalized values are.
+    // Note that this is all scalars, buffer pointers, and buffer sizes. The actual buffers are in message buffer (raw pieces) or the
+    // scratchPad (normalized pieces).
+    uint64_t infractions;
+    bool tcpClose;
+    NHttpEnums::SourceId sourceId;
+};
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.cc
new file mode 100644 (file)
index 0000000..a1770b3
--- /dev/null
@@ -0,0 +1,123 @@
+/****************************************************************************
+ *
+** 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      NHttpMsgTrailer class analyzes HTTP chunked message trailers.
+//
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "snort.h"
+#include "nhttp_enum.h"
+#include "nhttp_msg_trailer.h"
+
+using namespace NHttpEnums;
+
+void NHttpMsgTrailer::parseWhole() {
+    // The following if-else ladder puts the extremely common normal cases at the beginning and the rare pathological cases at the end
+    // Normal case with no trailer fields
+    if (!tcpClose && (length == 2)) {
+        headers.length = STAT_NOTPRESENT;
+    }
+    // Normal case with trailer fields
+    else if (!tcpClose) {
+        headers.start = msgText;
+        headers.length = length - 4;
+    }
+    // Normal case no trailer fields and TCP connection close
+    else if ((length == 2) && !memcmp(msgText, "\r\n", 2)) {
+        headers.length = STAT_NOTPRESENT;
+    }
+    // Normal case with trailer fields and TCP connection close
+    else if ((length >= 5) && !memcmp(msgText+length-4, "\r\n\r\n", 4)) {
+        headers.start = msgText;
+        headers.length = length - 4;
+    }
+    // Abnormal cases truncated by TCP connection close
+    else {
+        infractions |= INF_TRUNCATED;
+
+        // Lone <CR>
+        if ((length == 1) && (msgText[0] == '\r')) {
+            headers.length = STAT_NOTPRESENT;
+        }
+        // Truncation occurred somewhere in the trailer fields
+        else {
+            headers.start = msgText;
+            headers.length = length;
+            // When present, remove partial <CR><LF><CR><LF> sequence from the very end
+            if ((length > 4) && !memcmp(msgText+length-3, "\r\n\r", 3)) headers.length -= 3;
+            else if ((length > 3) && !memcmp(msgText+length-2, "\r\n", 2)) headers.length -= 2;
+            else if ((length > 2) && (msgText[length-1] == '\r')) headers.length -= 1;
+        }
+    }
+}
+
+void NHttpMsgTrailer::genEvents() {
+    if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
+}
+
+void NHttpMsgTrailer::printMessage(FILE *output) const {
+    NHttpMsgSection::printMessageTitle(output, "trailer");
+    NHttpMsgSharedHead::printMessageHead(output);
+    NHttpMsgSection::printMessageWrapup(output);
+}
+
+
+void NHttpMsgTrailer::updateFlow() const {
+    if (tcpClose) {
+        sessionData->typeExpected[sourceId] = SEC_CLOSED;
+        sessionData->halfReset(sourceId);
+    }
+    else {
+        sessionData->typeExpected[sourceId] = SEC_HEADER;
+        sessionData->halfReset(sourceId);
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.h
new file mode 100644 (file)
index 0000000..282421d
--- /dev/null
@@ -0,0 +1,68 @@
+/****************************************************************************
+ *
+** 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      NHttpMsgTrailer class declaration
+//
+
+#ifndef NHTTP_MSG_TRAILER_H
+#define NHTTP_MSG_TRAILER_H
+
+#include "nhttp_msg_head_shared.h"
+
+//-------------------------------------------------------------------------
+// NHttpMsgTrailer class
+//-------------------------------------------------------------------------
+
+class NHttpMsgTrailer: public NHttpMsgSharedHead {
+public:
+    NHttpMsgTrailer() {};
+    void printMessage(FILE *output) const;
+    void genEvents();
+    void updateFlow() const;
+
+private:
+    void parseWhole();
+};
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_scratch_pad.h b/src/service_inspectors/nhttp_inspect/nhttp_scratch_pad.h
new file mode 100644 (file)
index 0000000..76e3243
--- /dev/null
@@ -0,0 +1,59 @@
+/****************************************************************************
+ *
+** 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      ScratchPad class declaration
+//
+
+#ifndef NHTTP_SCRATCH_PAD_H
+#define NHTTP_SCRATCH_PAD_H
+
+
+//-------------------------------------------------------------------------
+// ScratchPad class
+// Memory management for NHttpMsgHeader class
+//-------------------------------------------------------------------------
+
+// Working space and storage for all the derived fields
+// Return value of request is 64-bit aligned and may be freely cast to uint64_t*
+// 1. request the maximum number of bytes you might need
+// 2. use what you need
+// 3. commit() what you actually used if you want to keep it
+// Anything you do not commit will be reused by the next request.
+
+class ScratchPad {
+public:
+    ScratchPad(uint64_t *buff, uint32_t length) : buffer(buff), capacity(length*8), used(0) {}; // Careful: length must be number of uint64_ts provided, not octets.
+    void reinit() {used = 0;};
+    uint8_t *request(uint32_t needed) const {return (needed <= capacity-used) ? (uint8_t*)(buffer+used) : nullptr;};
+    void commit(uint32_t taken) { used += taken + (8-(taken%8))%8; };  // round up to multiple of 8 to preserve alignment
+
+private:
+    uint64_t *buffer;
+    uint32_t capacity;
+    uint32_t used;
+};
+
+#endif
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_str_to_code.cc b/src/service_inspectors/nhttp_inspect/nhttp_str_to_code.cc
new file mode 100644 (file)
index 0000000..c8c3337
--- /dev/null
@@ -0,0 +1,47 @@
+/****************************************************************************
+ *
+** 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     Converts token strings to enum codes
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include "snort.h"
+#include "nhttp_enum.h"
+#include "nhttp_str_to_code.h"
+
+// Need to replace this simple algorithm for better performance
+int32_t strToCode(const uint8_t *text, int32_t textLen, const StrCode table[]) {
+    if (textLen <= 0) return NHttpEnums::STAT_PROBLEMATIC;
+    for (int32_t k=0; table[k].name != nullptr; k++) {
+        if ((textLen == (int) strlen(table[k].name)) && (memcmp(text, table[k].name, textLen) == 0)) {
+            return table[k].code;
+        }
+    }
+    return NHttpEnums::STAT_OTHER;
+}
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_str_to_code.h b/src/service_inspectors/nhttp_inspect/nhttp_str_to_code.h
new file mode 100644 (file)
index 0000000..c3e278d
--- /dev/null
@@ -0,0 +1,40 @@
+/****************************************************************************
+ *
+** 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      Converts protocol constant string to enum
+//
+
+#ifndef NHTTP_STR_TO_CODE_H
+#define NHTTP_STR_TO_CODE_H
+
+struct StrCode {
+    int32_t code;
+    const char *name;
+};
+
+int32_t strToCode(const uint8_t *text, int32_t textLen, const StrCode table[]);
+
+#endif
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc b/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc
new file mode 100644 (file)
index 0000000..3974450
--- /dev/null
@@ -0,0 +1,159 @@
+/****************************************************************************
+ *
+** 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      HTTP Stream Splitter Class
+//
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include "snort.h"
+#include "nhttp_enum.h"
+#include "nhttp_test_input.h"
+#include "nhttp_stream_splitter.h"
+
+using namespace NHttpEnums;
+
+// Convenience function. All the housekeeping that must be done before we can return PAF_FLUSH to stream.
+void NHttpStreamSplitter::prepareFlush(NHttpFlowData* sessionData, uint32_t* flushOffset, SourceId sourceId, SectionType sectionType, bool tcpClose,
+      uint64_t infractions, uint32_t numOctets) {
+    sessionData->sourceId = sourceId;
+    sessionData->sectionType = sectionType;
+    sessionData->tcpClose = tcpClose;
+    sessionData->infractions = infractions;
+    if (tcpClose) sessionData->typeExpected[sourceId] = SEC_CLOSED;
+    if (!NHttpTestInput::test_mode) *flushOffset = numOctets;
+    else NHttpTestInput::testInput->pafFlush(numOctets);
+    octetsSeen = 0;
+    numCrlf = 0;
+}
+
+PAF_Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* data, uint32_t length, uint32_t flags, uint32_t* flushOffset) {
+    // When the system begins providing TCP connection close information this won't always be false. &&&
+    bool tcpClose = false;
+
+    // This is the session state information we share with HTTP Inspect and store with stream. A session is defined by a TCP connection.
+    // Since PAF is the first to see a new TCP connection the new flow data object is created here.
+    NHttpFlowData* sessionData = (NHttpFlowData*)flow->get_application_data(NHttpFlowData::nhttp_flow_id);
+    if (sessionData == nullptr) flow->set_application_data(sessionData = new NHttpFlowData);
+    assert(sessionData != nullptr);
+
+    SourceId sourceId = (flags & PKT_FROM_CLIENT) ? SRC_CLIENT : SRC_SERVER;
+
+    if (NHttpTestInput::test_mode) {
+        *flushOffset = length;
+        bool needBreak;
+        NHttpTestInput::testInput->toPaf((uint8_t*&)data, length, sourceId, tcpClose, needBreak);
+        if (length == 0) return PAF_FLUSH;
+        if (needBreak) flow->set_application_data(sessionData = new NHttpFlowData);
+    }
+
+    switch (SectionType type = sessionData->typeExpected[sourceId]) {
+      case SEC_HEADER:
+      case SEC_CHUNKHEAD:
+      case SEC_TRAILER:
+        pafMax = 63780;
+        for (uint32_t k = 0; k < length; k++) {
+            octetsSeen++;
+
+            // Count the alternating <CR> and <LF> characters we have seen in a row
+            if (((data[k] == '\r') && (numCrlf%2 == 0)) || ((data[k] == '\n') && (numCrlf%2 == 1))) numCrlf++;
+            else numCrlf = 0;
+
+            // Check header for leading CRLF because some 1.0 implementations put extra blank lines between messages. We tolerate this by quietly ignoring them.
+            // Trailer may also have leading CRLF. That is completely normal and means there is no trailer.
+            if (((type == SEC_HEADER) || (type == SEC_TRAILER)) && (numCrlf == 2) && (octetsSeen == 2)) {
+                prepareFlush(sessionData, flushOffset, sourceId, (type == SEC_HEADER) ? SEC_DISCARD : type, tcpClose && (k == length-1), 0, k+1);
+                return PAF_FLUSH;
+            }
+            // The chunk header section always ends with the first <CRLF>
+            else if ((type == SEC_CHUNKHEAD) && (numCrlf == 2)) {
+                prepareFlush(sessionData, flushOffset, sourceId, type, tcpClose && (k == length-1), 0, k+1);
+                return PAF_FLUSH;
+            }
+            // The header and trailer sections always end with the first double <CRLF>
+            else if (numCrlf == 4) {
+                prepareFlush(sessionData, flushOffset, sourceId, type, tcpClose && (k == length-1), 0, k+1);
+                return PAF_FLUSH;
+            }
+            // We must do this to protect ourself from buffer overrun.
+            else if (octetsSeen >= 63780) {
+                prepareFlush(sessionData, flushOffset, sourceId, type, tcpClose && (k == length-1), INF_HEADTOOLONG, k+1);
+                return PAF_FLUSH;
+            }
+        }
+        // Incomplete headers wait patiently for more data
+        if (!tcpClose) return PAF_SEARCH;
+        // Discard the oddball case where the new "message" starts with <CR><close>
+        else if ((octetsSeen == 1) && (numCrlf == 1)) prepareFlush(sessionData, flushOffset, sourceId, SEC_DISCARD, true, 0, length);
+        // TCP connection close, flush the partial header
+        else prepareFlush(sessionData, flushOffset, sourceId, type, true, INF_TRUNCATED, length);
+        return PAF_FLUSH;
+      case SEC_BODY:
+      case SEC_CHUNKBODY:
+        pafMax = 16384;
+        prepareFlush(sessionData, flushOffset, sourceId, type, tcpClose && (sessionData->octetsExpected[sourceId] >= length), 0, sessionData->octetsExpected[sourceId]);
+        return PAF_FLUSH;
+      case SEC_ABORT:
+        return PAF_ABORT;
+      default:
+        assert(0);
+        return PAF_ABORT;
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.h b/src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.h
new file mode 100644 (file)
index 0000000..ce6fd74
--- /dev/null
@@ -0,0 +1,65 @@
+/****************************************************************************
+ *
+** 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      NHTTP Stream Splitter class
+//
+
+#ifndef NHTTP_STREAM_SPLITTER_H
+#define NHTTP_STREAM_SPLITTER_H
+
+#include "stream/stream_splitter.h"
+#include "nhttp_flow_data.h"
+
+class NHttpStreamSplitter : public StreamSplitter {
+public:
+    NHttpStreamSplitter(bool isClientToServer) : StreamSplitter(isClientToServer) {};
+    PAF_Status scan(Flow* flow, const uint8_t* data, uint32_t length, uint32_t flags, uint32_t* flushOffset);
+    bool is_paf() { return true; };
+    uint32_t max() { return pafMax; };
+private:
+    void prepareFlush(NHttpFlowData* sessionData, uint32_t* flushOffset, NHttpEnums::SourceId sourceId, NHttpEnums::SectionType sectionType, bool tcpClose,
+          uint64_t infractions, uint32_t numOctets);
+
+    int64_t octetsSeen;
+    int numCrlf;
+    uint32_t pafMax = 63780;
+};
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
index 38bb8006e1c52a4cd74fabdeffad79fabcbffac4..7ad6e7727550b6e5385eeaaeb23bcaf4c49e24a6 100644 (file)
 //
 //  @author     Tom Peters <thopeter@cisco.com>
 //
-//  @brief      Static constant tables for converting protocol strings to enum codes. Members of HttpMsgHeader.
+//  @brief      Static constant tables for various conversions and normalizations
+//
+//  Note: protocol correctness is not the decisive criterion for inclusion in the following tables. Just because something is "wrong" per RFC does
+//        not mean that it cannot appear here. The goal is to recognize and inspect things that actually happen or might happen regardless of
+//        whether they should happen.
 //
-
 
 #include <string.h>
 #include <sys/types.h>
 
 #include "snort.h"
-#include "flow/flow.h"
+#include "framework/module.h"
 #include "nhttp_enum.h"
-#include "nhttp_scratchpad.h"
-#include "nhttp_strtocode.h"
-#include "nhttp_headnorm.h"
-#include "nhttp_flowdata.h"
-#include "nhttp_msgheader.h"
+#include "nhttp_str_to_code.h"
+#include "nhttp_head_norm.h"
+#include "nhttp_msg_head.h"
+#include "nhttp_module.h"
 
 using namespace NHttpEnums;
 
@@ -91,7 +93,7 @@ const StrCode NHttpMsgHeader::methodList[] =
     { METH_UPDATEREDIRECTREF,  "UPDATEREDIRECTREF"},
     { 0,                       nullptr} };
 
-const StrCode NHttpMsgHeader::headerList[] =
+const StrCode NHttpMsgSharedHead::headerList[] =
    {{ HEAD_CACHE_CONTROL,        "cache-control"},
     { HEAD_CONNECTION,           "connection"},
     { HEAD_DATE,                 "date"},
@@ -143,7 +145,7 @@ const StrCode NHttpMsgHeader::headerList[] =
     { HEAD_LAST_MODIFIED,        "last-modified"},
     { 0,                         nullptr} };
 
-const StrCode NHttpMsgHeader::transCodeList[] =
+const StrCode NHttpMsgSharedHead::transCodeList[] =
    {{ TRANSCODE_CHUNKED,         "chunked"},
     { TRANSCODE_IDENTITY,        "identity"},
     { TRANSCODE_GZIP,            "gzip"},
@@ -151,14 +153,14 @@ const StrCode NHttpMsgHeader::transCodeList[] =
     { TRANSCODE_DEFLATE,         "deflate"},
     { 0,                         nullptr} };
 
-const HeaderNormalizer NHttpMsgHeader::NORMALIZER_NIL {NORM_NULL, false, false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
-const HeaderNormalizer NHttpMsgHeader::NORMALIZER_BASIC {NORM_FIELD, false, false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
-const HeaderNormalizer NHttpMsgHeader::NORMALIZER_CAT {NORM_FIELD, true, false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
-const HeaderNormalizer NHttpMsgHeader::NORMALIZER_NOREPEAT {NORM_FIELD, false, true, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
-const HeaderNormalizer NHttpMsgHeader::NORMALIZER_DECIMAL {NORM_INTEGER, false, true, normDecimalInteger, nullptr, nullptr, nullptr, nullptr, nullptr};
-const HeaderNormalizer NHttpMsgHeader::NORMALIZER_TRANSCODE {NORM_INTEGER, true, false, normSeqStrCode, NHttpMsgHeader::transCodeList, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgSharedHead::NORMALIZER_NIL {NORM_NULL, false, false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgSharedHead::NORMALIZER_BASIC {NORM_FIELD, false, false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgSharedHead::NORMALIZER_CAT {NORM_FIELD, true, false, normRemoveLws, nullptr, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgSharedHead::NORMALIZER_NOREPEAT {NORM_FIELD, false, true, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgSharedHead::NORMALIZER_DECIMAL {NORM_INT64, false, true, normDecimalInteger, nullptr, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgSharedHead::NORMALIZER_TRANSCODE {NORM_ENUM64, true, false, normRemoveLws, nullptr, norm2Lower, nullptr, normSeqStrCode, NHttpMsgSharedHead::transCodeList};
 
-const HeaderNormalizer* const NHttpMsgHeader::headerNorms[HEAD__MAXVALUE] = { [0] = &NORMALIZER_NIL,
+const HeaderNormalizer* const NHttpMsgSharedHead::headerNorms[HEAD__MAXVALUE] = { [0] = &NORMALIZER_NIL,
     [HEAD__OTHER] = &NORMALIZER_BASIC,
     [HEAD_CACHE_CONTROL] = &NORMALIZER_BASIC,
     [HEAD_CONNECTION] = &NORMALIZER_BASIC,
@@ -173,8 +175,8 @@ const HeaderNormalizer* const NHttpMsgHeader::headerNorms[HEAD__MAXVALUE] = { [0
     [HEAD_WARNING] = &NORMALIZER_BASIC,
     [HEAD_ACCEPT] = &NORMALIZER_BASIC,
     [HEAD_ACCEPT_CHARSET] = &NORMALIZER_BASIC,
-    [HEAD_ACCEPT_ENCODING] = &NORMALIZER_BASIC,
-    [HEAD_ACCEPT_LANGUAGE] = &NORMALIZER_BASIC,
+    [HEAD_ACCEPT_ENCODING] = &NORMALIZER_CAT,
+    [HEAD_ACCEPT_LANGUAGE] = &NORMALIZER_CAT,
     [HEAD_AUTHORIZATION] = &NORMALIZER_BASIC,
     [HEAD_EXPECT] = &NORMALIZER_BASIC,
     [HEAD_FROM] = &NORMALIZER_BASIC,
@@ -211,6 +213,55 @@ const HeaderNormalizer* const NHttpMsgHeader::headerNorms[HEAD__MAXVALUE] = { [0
     [HEAD_LAST_MODIFIED] = &NORMALIZER_BASIC
 };
 
-    const int32_t NHttpMsgHeader::numNorms = HEAD__MAXVALUE-1;
+const int32_t NHttpMsgSharedHead::numNorms = HEAD__MAXVALUE-1;
 
+const RuleMap NHttpModule::nhttpEvents[] =
+{
+    { EVENT_ASCII,                           "(nhttp_inspect) ascii encoding" },
+    { EVENT_DOUBLE_DECODE,                   "(nhttp_inspect) double decoding attack" },
+    { EVENT_U_ENCODE,                        "(nhttp_inspect) u encoding" },
+    { EVENT_BARE_BYTE,                       "(nhttp_inspect) bare byte unicode encoding" },
+    { EVENT_OBSOLETE_1,                      "(nhttp_inspect) obsolete event--should not appear" },
+    { EVENT_UTF_8,                           "(nhttp_inspect) utf-8 encoding" },
+    { EVENT_IIS_UNICODE,                     "(nhttp_inspect) iis unicode codepoint encoding" },
+    { EVENT_MULTI_SLASH,                     "(nhttp_inspect) multi_slash encoding" },
+    { EVENT_IIS_BACKSLASH,                   "(nhttp_inspect) iis backslash evasion" },
+    { EVENT_SELF_DIR_TRAV,                   "(nhttp_inspect) self directory traversal" },
+    { EVENT_DIR_TRAV,                        "(nhttp_inspect) directory traversal" },
+    { EVENT_APACHE_WS,                       "(nhttp_inspect) apache whitespace (tab)" },
+    { EVENT_IIS_DELIMITER,                   "(nhttp_inspect) non-rfc http delimiter" },
+    { EVENT_NON_RFC_CHAR,                    "(nhttp_inspect) non-rfc defined char" },
+    { EVENT_OVERSIZE_DIR,                    "(nhttp_inspect) oversize request-uri directory" },
+    { EVENT_LARGE_CHUNK,                     "(nhttp_inspect) oversize chunk encoding" },
+    { EVENT_PROXY_USE,                       "(nhttp_inspect) unauthorized proxy use detected" },
+    { EVENT_WEBROOT_DIR,                     "(nhttp_inspect) webroot directory traversal" },
+    { EVENT_LONG_HDR,                        "(nhttp_inspect) long header" },
+    { EVENT_MAX_HEADERS,                     "(nhttp_inspect) max header fields" },
+    { EVENT_MULTIPLE_CONTLEN,                "(nhttp_inspect) multiple content length" },
+    { EVENT_CHUNK_SIZE_MISMATCH,             "(nhttp_inspect) chunk size mismatch detected" },
+    { EVENT_INVALID_TRUEIP,                  "(nhttp_inspect) invalid ip in true-client-ip/xff header" },
+    { EVENT_MULTIPLE_HOST_HDRS,              "(nhttp_inspect) multiple host hdrs detected" },
+    { EVENT_LONG_HOSTNAME,                   "(nhttp_inspect) hostname exceeds 255 characters" },
+    { EVENT_EXCEEDS_SPACES,                  "(nhttp_inspect) header parsing space saturation" },
+    { EVENT_CONSECUTIVE_SMALL_CHUNKS,        "(nhttp_inspect) client consecutive small chunk sizes" },
+    { EVENT_UNBOUNDED_POST,                  "(nhttp_inspect) post w/o content-length or chunks" },
+    { EVENT_MULTIPLE_TRUEIP_IN_SESSION,      "(nhttp_inspect) multiple true ips in a session" },
+    { EVENT_BOTH_TRUEIP_XFF_HDRS,            "(nhttp_inspect) both true_client_ip and xff hdrs present" },
+    { EVENT_UNKNOWN_METHOD,                  "(nhttp_inspect) unknown method" },
+    { EVENT_SIMPLE_REQUEST,                  "(nhttp_inspect) simple request" },
+    { EVENT_UNESCAPED_SPACE_URI,             "(nhttp_inspect) unescaped space in http uri" },
+    { EVENT_PIPELINE_MAX,                    "(nhttp_inspect) too many pipelined requests" },
+    { EVENT_ANOM_SERVER,                     "(nhttp_inspect) anomalous http server on undefined http port" },
+    { EVENT_INVALID_STATCODE,                "(nhttp_inspect) invalid status code in http response" },
+    { EVENT_NO_CONTLEN,                      "(nhttp_inspect) no content-length or transfer-encoding in http response" },
+    { EVENT_UTF_NORM_FAIL,                   "(nhttp_inspect) http response has utf charset which failed to normalize" },
+    { EVENT_UTF7,                            "(nhttp_inspect) http response has utf-7 charset" },
+    { EVENT_DECOMPR_FAILED,                  "(nhttp_inspect) http response gzip decompression failed" },
+    { EVENT_CONSECUTIVE_SMALL_CHUNKS_S,      "(nhttp_inspect) server consecutive small chunk sizes" },
+    { EVENT_MSG_SIZE_EXCEPTION,              "(nhttp_inspect) invalid content-length or chunk size" },
+    { EVENT_JS_OBFUSCATION_EXCD,             "(nhttp_inspect) javascript obfuscation levels exceeds 1" },
+    { EVENT_JS_EXCESS_WS,                    "(nhttp_inspect) javascript whitespaces exceeds max allowed" },
+    { EVENT_MIXED_ENCODINGS,                 "(nhttp_inspect) multiple encodings within javascript obfuscated data" },
+    { 0, nullptr }
+};
 
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_test_input.cc b/src/service_inspectors/nhttp_inspect/nhttp_test_input.cc
new file mode 100644 (file)
index 0000000..943b806
--- /dev/null
@@ -0,0 +1,294 @@
+/****************************************************************************
+ *
+** 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      Interface to file of test messages
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdexcept>
+
+#include "nhttp_enum.h"
+#include "nhttp_test_input.h"
+
+using namespace NHttpEnums;
+
+bool NHttpTestInput::test_mode = false;
+NHttpTestInput *NHttpTestInput::testInput = nullptr;
+
+NHttpTestInput::NHttpTestInput(const char *fileName) {
+    if ((testDataFile = fopen(fileName, "r")) == nullptr) throw std::runtime_error("Cannot open test input file");
+}
+
+NHttpTestInput::~NHttpTestInput() {
+    fclose(testDataFile);
+}
+
+// Read from the test data file and present to PAF.
+// In the process we may need to skip comments, execute simple commands, and handle escape sequences.
+// The best way to understand this function is to read the comments at the top of the file of test cases.
+void NHttpTestInput::toPaf(uint8_t*& data, uint32_t &length, SourceId &sourceId, bool &tcpClose, bool &needBreak) {
+    // No new data presented to PAF while the last section is still being flushed.
+    if (flushed) {
+        length = 0;
+        return;
+    }
+
+    sourceId = lastSourceId;
+    tcpClose = false;
+    needBreak = false;
+
+    if (justFlushed) {
+        // PAF just flushed. There may or may not be leftover data in our buffer.
+        justFlushed = false;
+        data = msgBuf;
+        length = endOffset - flushOffset;  // this is the leftover data
+        previousOffset = 0;
+        endOffset = length;
+        if (length > 0) {
+            // Must present unflushed leftovers to PAF again.
+            // If we don't take this opportunity to left justify our data in the buffer we may "walk" to the right until we run out of buffer space
+            memmove(msgBuf, msgBuf+flushOffset, length);
+            tcpClose = tcpAlreadyClosed;
+            return;
+        }
+        // If we reach here then PAF has already flushed all the data we have read so far.
+        tcpAlreadyClosed = false;
+    }
+    else {
+        // The data we gave PAF last time was not flushed
+        length = 0;
+        previousOffset = endOffset;
+        data = msgBuf + previousOffset;
+    }
+
+    // Now we need to move forward by reading more data from the file
+    int newChar;
+    typedef enum { WAITING, COMMENT, COMMAND, SECTION, ESCAPE, HEXVAL, FILLNUM, BRIDGE } State;
+    State state = WAITING;
+    bool ending;
+    int commandLength;
+    const int MaxCommand = 100;
+    char commandValue[MaxCommand];
+    uint8_t hexVal;
+    int numDigits;
+    uint32_t fillLength;
+
+    while ((newChar = getc(testDataFile)) != EOF) {
+        switch (state) {
+          case WAITING:
+            if (newChar == '#') state = COMMENT;
+            else if (newChar == '@') {
+                state = COMMAND;
+                commandLength = 0;
+            }
+            else if (newChar == '\\') {
+                state = ESCAPE;
+                ending = false;
+            }
+            else if (newChar != '\n') {
+                state = SECTION;
+                ending = false;
+                data[length++] = (uint8_t) newChar;
+            }
+            break;
+          case COMMENT:
+            if (newChar == '\n') state = WAITING;
+            break;
+          case COMMAND:
+            if (newChar == '\n') {
+                state = WAITING;
+                if ((commandLength == strlen("request")) && !memcmp(commandValue, "request", strlen("request"))) sourceId = lastSourceId = SRC_CLIENT;
+                else if ((commandLength == strlen("response")) && !memcmp(commandValue, "response", strlen("response"))) sourceId = lastSourceId = SRC_SERVER;
+                else if ((commandLength == strlen("break")) && !memcmp(commandValue, "break", strlen("break"))) needBreak = true;
+                else if ((commandLength == strlen("bodyend")) && !memcmp(commandValue, "bodyend", strlen("bodyend"))) {
+                    termBytes[0] = 'x';
+                    termBytes[1] = 'y';
+                }
+                else if ((commandLength == strlen("chunkend")) && !memcmp(commandValue, "chunkend", strlen("chunkend"))) {
+                    termBytes[0] = '\r';
+                    termBytes[1] = '\n';
+                }
+                else if (commandLength > 0) {
+                    // Look for a test number
+                    bool isNumber = true;
+                    for (int k=0; (k < commandLength) && isNumber; k++) {
+                        isNumber = (commandValue[k] >= '0') && (commandValue[k] <= '9');
+                    }
+                    if (isNumber) {
+                        testNumber = 0;
+                        for (int j=0; j < commandLength; j++) {
+                            testNumber = testNumber * 10 + (commandValue[j] - '0');
+                        }
+                    }
+                }
+            }
+            else {
+                if (commandLength < MaxCommand) commandValue[commandLength++] = newChar;
+                else assert(0);
+            }
+            break;
+          case SECTION:
+            if (newChar == '\\') {
+                state = ESCAPE;
+                ending = false;
+            }
+            else if (newChar == '\n') {
+                if (ending) {
+                    // Found the blank line that ends the section.
+                    endOffset = previousOffset + length;
+                    return;
+                }
+                ending = true;
+            }
+            else {
+                ending = false;
+                data[length++] = (uint8_t) newChar;
+            }
+            break;
+          case ESCAPE:
+            switch (newChar) {
+              case 'n':  state = SECTION; data[length++] = '\n'; break;
+              case 'r':  state = SECTION; data[length++] = '\r'; break;
+              case 't':  state = SECTION; data[length++] = '\t'; break;
+              case 'B':  state = BRIDGE; break;
+              case 'C':  endOffset = previousOffset + length; return;
+              case 'T':  tcpClose = tcpAlreadyClosed = true; endOffset = previousOffset + length; return;
+              case '#':  state = SECTION; data[length++] = '#';  break;
+              case '@':  state = SECTION; data[length++] = '@';  break;
+              case '\\': state = SECTION; data[length++] = '\\'; break;
+              case 'x':
+              case 'X':  state = HEXVAL; hexVal = 0; numDigits = 0; break;
+              case '/':  state = FILLNUM; fillLength = 0; break;
+              default:   assert(0); state = SECTION; break;
+            }
+            break;
+          case BRIDGE:
+            if (newChar != '\n') {
+                state = SECTION;
+                data[length++] = (uint8_t) newChar;
+            }
+            break;
+          case HEXVAL:
+            if ((newChar >= '0') && (newChar <= '9')) hexVal = hexVal * 16 + (newChar - '0');
+            else if ((newChar >= 'a') && (newChar <= 'f')) hexVal = hexVal * 16 + 10 + (newChar - 'a');
+            else if ((newChar >= 'A') && (newChar <= 'F')) hexVal = hexVal * 16 + 10 + (newChar - 'A');
+            else assert(0);
+            if (++numDigits == 2) {
+                data[length++] = hexVal;
+                state = SECTION;
+            }
+            break;
+          case FILLNUM:
+            if (newChar != '/') {
+                assert((newChar >= '0') && (newChar <= '9'));
+                fillLength = fillLength * 10 + (newChar - '0');
+                assert(fillLength <= sizeof(msgBuf));  
+                break;
+            }
+            else {
+                bodyData = true;
+                // Add the specified number of fill characters to the buffer and cut.
+                // Simulates body data at the end of a header segment or the first segment containing body data
+                // Don't allow a buffer overrun.
+                if (previousOffset + length + fillLength > sizeof(msgBuf)) assert(0);
+                for (uint32_t k=0; k < fillLength; k++) {
+                    data[length++] = 'x';
+                }
+                endOffset = previousOffset + length;
+                return;
+            }
+        }
+        // If we have reached the configured maximum segment size automatically cut the data.
+        if (length >= mssLength) {
+            endOffset = previousOffset + length;
+            return;
+        }
+        // Don't allow a buffer overrun.
+        if (previousOffset + length >= sizeof(msgBuf)) assert(0);
+    }
+    // End-of-file. Return everything we have so far.
+    endOffset = previousOffset + length;
+    return;
+}
+
+void NHttpTestInput::pafFlush(uint32_t length) {
+    assert(!flushed);
+    flushed = true;
+    if (bodyData && (previousOffset + length >= endOffset)) {
+        fillOctets = length;
+        bodyData = false;
+        previousOffset = 0;
+        endOffset = 0;
+        flushOffset = 0;
+    }
+    else {
+        flushOffset = previousOffset + length;
+    }
+}
+
+
+uint16_t NHttpTestInput::toEval(uint8_t **buffer, int64_t &testNumber_) {
+    if (!flushed) return 0;
+    testNumber_ = testNumber;
+    *buffer = msgBuf;
+    if (fillOctets > 0) {
+        uint32_t fillOut = (fillOctets <= 16384) ? fillOctets : 16384;
+        for (uint32_t k = 0; k < fillOut; k++) {
+            msgBuf[k] = 'A' + k % 26;
+        }
+        fillOctets -= fillOut;
+        if (fillOctets == 0) {
+            if (fillOut > 1) msgBuf[fillOut-2] = termBytes[0];
+            msgBuf[fillOut-1] = termBytes[1];
+            flushed = false;
+            justFlushed = true;
+        }
+        else if (fillOctets == 1) {
+            msgBuf[fillOut-1] = termBytes[0];
+        }
+        return (uint16_t)fillOut;
+    }
+    flushed = false;
+    justFlushed = true;
+    return (uint16_t)flushOffset;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_test_input.h b/src/service_inspectors/nhttp_inspect/nhttp_test_input.h
new file mode 100644 (file)
index 0000000..d7fe9e5
--- /dev/null
@@ -0,0 +1,61 @@
+/****************************************************************************
+ *
+** 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      Read test file and present test input to PAF
+//
+
+#ifndef NHTTP_TEST_INPUT_H
+#define NHTTP_TEST_INPUT_H
+
+class NHttpTestInput {
+public:
+    NHttpTestInput(const char *fileName);
+    ~NHttpTestInput();
+    void toPaf(uint8_t*& data, uint32_t &length, NHttpEnums::SourceId &sourceId, bool &tcpClose, bool &needBreak);
+    void pafFlush(uint32_t length);
+    uint16_t toEval(uint8_t **buffer, int64_t &testNumber);
+
+    // Hard for NHttpInspect and PAF to share these without making them "global". This is as good a place as any for them to live.
+    static bool test_mode;
+    static NHttpTestInput *testInput;
+private:
+    FILE *testDataFile;
+    uint8_t msgBuf[2 * NHttpEnums::MAXOCTETS];
+    bool flushed = false;   // verifies alternation between PAF section flushing and eval() section processing
+    bool justFlushed = true;   // toPaf() needs to do special post-flush processing before it resumes reading the file
+    bool tcpAlreadyClosed = false;  // so we can keep presenting a TCP close to PAF until all the remaining octets are consumed and flushed
+    uint32_t flushOffset = 0;  // last character in buffer that has been flushed by PAF and must go to eval().
+    uint32_t previousOffset = 0;   // last character in the buffer shown to PAF but not flushed yet
+    uint32_t endOffset = 0;   // last read character in the buffer
+    bool bodyData = false;  // pending output of fill data
+    uint32_t fillOctets = 0;    // remaining fill data
+    int64_t testNumber = 0;   // for numbering test output files
+    uint32_t mssLength = 1460;   // Maximum Segment Size. Needs to be enhanced to be set through a command.
+    NHttpEnums::SourceId lastSourceId = NHttpEnums::SRC_CLIENT;   // current direction of traffic flow. Toggled by commands in file.
+    uint8_t termBytes[2] = { 'x', 'y' };
+};
+
+#endif
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_test_msgs.txt b/src/service_inspectors/nhttp_inspect/nhttp_test_msgs.txt
new file mode 100644 (file)
index 0000000..24cc211
--- /dev/null
@@ -0,0 +1,530 @@
+# Enter HTTP test message text as you want it to be presented to PAF.
+#
+# The easiest way to format is to put a blank line between message sections so that each message section is its own "paragraph".
+# Within a paragraph the placement of new lines does not have any effect. Format a paragraph any way you are comfortable. Extra blank lines
+# between paragraphs also do not have any effect.
+#
+# Lines beginning with # are comments. Lines beginning with @ are commands. This does not apply to lines in the middle of a paragraph.
+#
+# Command lines are left justified, lower case, with no whitespace:
+#   @break resets HTTP Inspect data structures and begins a new test. Use it liberally to prevent unrelated tests from interfering with each other.
+#   @request and @response set the message direction. Applies to subsequent sections until changed.
+#   @bodyend and @chunkend set the final two characters of a body or chunk to "xy" or "\r\n".
+#   @<decimal number> sets the test number and hence the test output file name. Applies to subsequent sections until changed. Don't reuse numbers.
+#
+# Escape sequences begin with '\'. They may be used within a paragraph or to begin a paragraph.
+#   \r - carriage return
+#   \n - linefeed
+#   \t - tab
+#   \\ - backslash
+#   \# - #
+#   \@ - @
+#   \xnn or \Xnn - where nn is a two-digit hexadecimal number. Insert an arbitrary 8-bit number as the next character. a-f and A-F are both acceptable.
+#   \/nnn/ - where nnn is a variable-length decimal number. Insert nnn octets of fill data. The fill characters are 'A' - 'Z' with the final two bytes
+#            "xy" for a normal body and "\r\n" for a chunk.
+#   \B, \C, and \T - bridge, cut, and TCP close. See below.
+
+# The default procedure for separating data into segments for presentation to PAF is 1) whenever a paragraph ends (blank line) and 2) every MSS octets,
+# where MSS is currently a source code constant set to 1460. The \C escape forces an immediate separation point even in the middle of a paragraph. It is
+# useful for testing PAF where message segments often arrive in pieces. \B causes prevents the immediately following blank line(s) from causing a
+# separation point. Data resumes with the next non-newline character as if there was no gap. This allows PAF to be tested with the end of a section and
+# the beginning of the next section or message in the same packet. Commands and comments following \B are not allowed. Data must resume immediately
+# following the blank lines.
+#
+# \T functions as \C but causes the TCP connection closed indication to be set. It only makes sense at the end of a paragraph. \C\T simulates a data
+# packet subsequently followed by a close with no more data.
+
+
+# ***********************************************************************************************
+# Valid response start lines
+@1001
+@break
+@response
+HTTP/1.1 200 OK\r\n\r\n
+
+@1002
+@break
+@response
+HTTP/1.0 315 An example reason phrase with spaces\r\n\r\n
+
+@1003
+@break
+@response
+HTTP/2.0 100 minimum valid status code\r\n\r\n
+
+@1004
+@break
+@response
+HTTP/1.1 599 MAX\r\n\r\n
+
+@1005
+@break
+@response
+HTTP/1.1 502 I'm very long and contain punctuationabcdefghijklmnopqrstuvwxyz!@#$%^&*()-_=+`~![]{}1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghij
+|\\;:'",<.>/?klmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstu vwxyz123 4567890abcdefghijklmnopqrstuvwxyz1234567890abcd
+efghijklmnopqrstuvwxyz1234567890\r\n\r\n
+
+@1006
+@break
+@response
+HTTP/1.0 200 1\r\n\r\n
+
+@1007
+@break
+@response
+HTTP/1.1 301 \r\n\r\n
+
+@1008
+@break
+@response
+HTTP/1.1 560  \r\n\r\n
+
+@1010
+@break
+@response
+HTTP/1.1 111     \r\n\r\n
+
+@1011
+@break
+@response
+HTTP/1.1 234 qwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZ qwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZqwertyuiopasdfghjklzxcvbnmABCDE{}[]
+FGHIJKLMNOPQRSTUVWXYZ   qwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZ qwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZ qwertyuiopasdfghjklzxcv
+bnmABCDEFGHIJKLMNOPQRSTUVWXYZqwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZqwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZqwertyuiopasdfghjklz
+xcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZqwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZqwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZqwertyuiopasdfghj
+klzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZqwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZqwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZqwertyuiopasdf
+ghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZ,.;'<>:"qwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZqwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZqwe
+rtyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZqwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZqwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZ
+\r\n\r\n
+
+
+# ***********************************************************************************************
+# Invalid response start lines
+
+@2001
+@break
+@response
+HTTP/2.0 099 status-code too small\r\n\r\n
+
+@2002
+@break
+@response
+HTTP/1.1 600 status-code too large\r\n\r\n
+
+@2003
+@break
+@response
+HTTP/1.1 879 x\r\n\r\n
+
+@2004
+@break
+@response
+HTTP/1.1 000 x----------------------------------------------------------s\r\n\r\n
+
+@2005
+@break
+@response
+HTTP/1.1 999 \r\n\r\n
+
+@2006
+@break
+@response
+HTTP/0.9 401  aaa\r\n\r\n
+
+@2007
+@break
+@response
+HTTP/1.2 344 \r\n\r\n
+
+@2008
+@break
+@response
+HTTP/2.1 200 400\r\n\r\n
+
+@2009
+@break
+@response
+HTTP/8.0 500 HTTP/1.1\r\n\r\n
+
+@2010
+@break
+@response
+HTOP/1.1 378 :"<>;'m,.\r\n\r\n
+
+@2011
+@break
+@response
+hTTP/1.1 400 OK\r\n\r\n
+
+@2012
+@break
+@response
+HTT5/1.1 200 OK\r\n\r\n
+
+@2013
+@break
+@response
+H TP/1.1 588 OK\r\n\r\n
+
+@2014
+@break
+@response
+HTTP\\1.1 101 OK\r\n\r\n
+
+@2015
+@break
+@response
+HTTP/1.A 700 OK\r\n\r\n
+
+@2016
+@break
+@response
+HTTP/1.a 303 OK   \r\n\r\n
+
+@2017
+@break
+@response
+HTTP/k.1 400 OK\tok\r\n\r\n
+
+@2018
+@break
+@response
+HTTP/1;0 400 O K\r\n\r\n
+
+@2019
+@break
+@response
+HTTP/1.0 444 O\rK\r\n\r\n
+
+@2020
+@break
+@response
+HTTP/1.1 444 O\n K1234567890\r\n\r\n
+
+@2021
+@break
+@response
+HTTP/1.1 46 status code too short\r\n\r\n
+
+@2022
+@break
+@response
+HTTP/1.1 2001 status code too \x31 long\r\n\r\n
+
+@2023
+@break
+@response
+HTTP/1.1 +88 bad character in status code I\r\n\r\n
+
+@2024
+@break
+@response
+HTTP/1.1 5E8 bad character in status code\x20II\r\n\r\n
+
+@2025
+@break
+@response
+HTTP/1.1 56a bad character in status code III\r\n\r\n
+
+@2026
+@break
+@response
+HTTP/1.0 401 illegal nontext char\x00acter in reason phrase null\r\n\r\n
+
+@2027
+@break
+@response
+HTTP/1.0 401 illegal nontext character in reason\xffphrase delete\r\n\r\n
+
+@2028
+@break
+@response
+HTTP/1.0 401 \x08illegal nontext character in reason phrase backspace\r\n\r\n
+
+
+# ***********************************************************************************************
+# Valid request start lines
+@3001
+@break
+@request
+MKREDIRECTREF / HTTP/2.0\r\n\r\n
+
+@3002
+@break
+@request
+BIND 1234567890?abcdef HTTP/1.1\r\n\r\n
+
+@3003
+@break
+@request
+GET /test/hi-there.txt HTTP/1.0\r\n\r\n
+
+
+
+# ***********************************************************************************************
+# Invalid request start lines
+@4001
+@break
+@request
+
+
+# ***********************************************************************************************
+# Valid headers without body
+@5001
+@break
+@request
+GET /test/hi-there.htm HTTP/1.1\r\nAccept: text/*\r\nAccept-Language: en,fr\r\n\r\n
+
+# Base example followed by whitespace variations before and after header field values.
+# Varying whitespace in comma-separated fields.
+@5002
+@break
+@request
+GET / HTTP/1.1\r\nAccept: */*\r\nAccept-Language: en,en-us,de,is\r\nAccept-Encoding: gzip, deflate\r\nUser-Agent: Mozilla/4.0\r\n\t(compatible: MSIE 5.5;
+ Windows NT 5.0)\r\nHost: www.ft.com\r\nConnection: Keep-Alive\r\n\r\n
+
+@5003
+# Whitespace variations before header values
+@break
+@request
+GET / HTTP/1.1\r\nAccept:*/*\r\nAccept-Language: en, en-us,\tde,    is\r\nAccept-Encoding:\tgzip,deflate\r\nUser-Agent:\t\t\t\t\tMozilla/4.0\r\n (compatible: MSIE 5.5;
+ Windows NT 5.0)\r\nHost:\t \t \t   \t\twww.ft.com\r\nConnection:               \t         Keep-Alive\r\n\r\n
+
+@5004
+# Whitespace variations after header values
+@break
+@request
+GET / HTTP/1.1\r\nAccept: */* \r\nAccept-Language: en,\t\ten-us,\t de,\r\n is\t\r\nAccept-Encoding: gzip,\tdeflate         \r\nUser-Agent: Mozilla/4.0\r\n\t \r\n\t(compatible: MSIE 5.5;
+ Windows NT 5.0)\t\t\t \t\t\r\nHost: www.ft.com   \t\r\n\t\t\r\nConnection: Keep-Alive \t \t \t\r\n\r\n
+
+@5005
+@break
+@response
+HTTP/2.0 200 OK\r\nContent-type: text/plain\r\nTransfer-Encoding: gzip,identity,compress,deflate,foo,chunked\r\n\r\n
+
+@5006
+@break
+@response
+HTTP/2.0 200 OK\r\nContent-type: text/plain\r\nTransfer-Encoding: gzip, identity, compress, deflate, foo, chunked\r\n\r\n
+
+@5007
+@break
+@response
+HTTP/2.0 200 OK\r\nContent-type: text/plain\r\nTransfer-Encoding: gzip\r\nTransfer-Encoding: identity\r\nTransfer-Encoding: compress\r\nTransfer-Encoding: deflate
+\r\nTransfer-Encoding: foo\r\nTransfer-Encoding: chunked\r\n\r\n
+
+
+# ***********************************************************************************************
+# Invalid headers without body
+@6001
+@break
+@request
+
+
+# ***********************************************************************************************
+# Valid Content-Length and body
+@7001
+@break
+@bodyend
+@response
+HTTP/1.1 200 OK\r\nContent-type: \ttext/plain\t\r\nContent-LENGTH: 19\r\n\r\n
+\/19/
+
+@7002
+@break
+@bodyend
+@response
+HTTP/1.1 200 OK\r\nContent-type: \ttext/plain\t\r\nContent-LENGTH: 19\r\n\r\n
+\/10/
+
+@7003
+@break
+@bodyend
+@response
+HTTP/1.1 200 OK\r\nContent-type: \ttext/plain\t\r\nContent-LENGTH: 19\r\n\r\n
+
+\/10/
+
+@7004
+@break
+@bodyend
+@response
+HTTP/1.1 200 OK\r\nContent-type: \ttext/plain\t\r\nContent-LENGTH: 19\r\n\r\n
+
+\/19/
+
+@7005
+@break
+@bodyend
+@response
+HTTP/1.1 200 OK\r\n
+Transfer-Encoding: identity\r\n
+CoNtEnT-lEnGtH:16382\r\n
+Content-type: text/plain\r\n
+\r\n
+\/800/
+
+@7006
+@break
+@bodyend
+@request
+POST /body/in/a/request/ HTTP/1.1\r\n
+Content-Length:16383\r\n
+\r\n
+
+\/1300/
+
+@7007
+@break
+@bodyend
+@request
+POST /body/in/a/request/ HTTP/1.1\r\n
+Content-Length:16384\r\n
+\r\n
+\/1/
+
+@7008
+@break
+@bodyend
+@response
+HTTP/1.0 408 barely too big for one section\r\n
+Content-Length:16385\r\n
+\r\n
+
+\/1/
+
+@7009
+@break
+@bodyend
+@response
+HTTP/1.1 408 barely too big for two sections\r\n
+Content-Length:32772\r\n
+\r\n
+\/2/
+
+@7010
+@break
+@bodyend
+@response
+HTTP/1.1 408 more than eight sections\r\n
+Content-Length:133072\r\n
+\r\n
+
+\/2/
+
+# ***********************************************************************************************
+# Invalid Content-Length
+@8001
+@break
+@response
+HTTP/2.0 200 Illegal zero length\r\nContent-type: \ttext/plain\t\r\nContent-LENGTH: 0\r\n\r\n
+
+@8002
+@break
+@response
+HTTP/1.1 200 Silly gigantic length\r\nContent-type: \ttext/plain\t\r\nContent-LENGTH: 12345678901
+23456789\r\n\r\n
+
+
+# ***********************************************************************************************
+# Valid chunks
+# Remember chunk lengths are required to be specified in hex
+@9001
+@break
+@chunkend
+@response
+HTTP/1.1 208 Example with chunks\r\nTransfer-Encoding: chunked\r\n\r\n
+
+64\r\n
+\/100/
+
+8A\r\n
+\/60/
+
+e6\r\n
+
+\/1/
+
+0\r\n
+
+\r\n
+
+@9002
+@break
+@chunkend
+@request
+POST /request/with/chunks HTTP/1.1\r\n
+Content-Type:    text/plain\r\n
+Transfer-Encoding: compress\r\n
+Via: 1.1 proxy3.company.com\r\n
+tRaNsFeR-eNcO\CdInG: cHuNkEd   \r\n
+Accept: *\r\n
+\r\n
+
+1C320\r\n
+\/1400/
+
+1F40; testing-the-extension-feature\r\n
+\/2/
+
+13FFE;  \r\n
+
+\/1460/
+
+4000;\r\n
+\/1460/
+
+3FFF;\r\n
+
+\/2/
+
+3FFE\r\n
+\/1454/
+
+4001;\r\n
+\/1/
+
+00000;chunkextension=3\r\n
+
+date: Sun, 01 Oct 2000 23:25:17 GMT\r\n
+X-madeupdate: Mon, 02 Oct 2000 23:25:17 GMT\r\n
+Accept-Language: en,  de\r\n
+Accept-Language: is\r\n
+\r\n
+
+# ***********************************************************************************************
+# Invalid chunks
+@10001
+@break
+@request
+
+
+
+# ***********************************************************************************************
+# Valid trailers
+@11001
+@break
+@request
+
+
+
+# ***********************************************************************************************
+# Invalid trailers
+@12001
+@break
+@request
+
+
+
+# ***********************************************************************************************
+# Valid request-response pairs
+@13001
+@break
+@request
+
+
+
+
+
+
+
+