]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Initial commit for NHttpInspect.
authorTom Peters <thopeter@Tom-Peters-ThinkStation-D30.(none)>
Fri, 2 May 2014 20:12:56 +0000 (16:12 -0400)
committerTom Peters <thopeter@Tom-Peters-ThinkStation-D30.(none)>
Fri, 2 May 2014 20:12:56 +0000 (16:12 -0400)
25 files changed:
configure.ac
src/Makefile.am
src/service_inspectors/Makefile.am
src/service_inspectors/nhttp_inspect/Makefile.am [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_api.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_api.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_enum.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_flowdata.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_flowdata.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_headnorm.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_headnorm.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_inspect.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_inspect.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_module.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_module.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msgheader.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_msgheader.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_scratchpad.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_strtocode.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_strtocode.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_tables.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_testinput.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_testinput.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttptestmsgs.txt [new file with mode: 0644]
src/service_inspectors/service_inspectors.cc

index 97883fa1438c432c28cece2f2e745a836b531ba1..ba983c9c6994db45a478ab3d68cad040fb9c1b2f 100644 (file)
@@ -893,6 +893,7 @@ src/service_inspectors/Makefile     \
 src/service_inspectors/back_orifice/Makefile \
 src/service_inspectors/ftp_telnet/Makefile \
 src/service_inspectors/http_inspect/Makefile \
+src/service_inspectors/nhttp_inspect/Makefile \
 src/service_inspectors/rpc_decode/Makefile \
 src/protocols/Makefile \
 src/search_engines/Makefile \
index 0ce392a1c5f71c0521895df3353ef9c04c313c1f..aa42adca614676c0ecfa6855e4d73346376024cc 100644 (file)
@@ -12,7 +12,8 @@ network_inspectors/arp_spoof/libarp_spoof.a \
 network_inspectors/port_scan/libport_scan.a \
 service_inspectors/back_orifice/libback_orifice.a \
 service_inspectors/ftp_telnet/libftp_telnet.a \
-service_inspectors/rpc_decode/librpc_decode.a
+service_inspectors/rpc_decode/librpc_decode.a \
+service_inspectors/nhttp_inspect/libnhttp_inspect.a
 endif
 
 # order libs to avoid undefined symbols
index 0fa1b890443f68d509b4da9a3da0ab77c30c80c3..b5ad88cf074c28be7ce0d302bb8c373e822dfbb1 100644 (file)
@@ -18,6 +18,7 @@ SUBDIRS = \
 back_orifice \
 ftp_telnet \
 http_inspect \
+nhttp_inspect \
 rpc_decode
 
 AM_CXXFLAGS = @AM_CXXFLAGS@
diff --git a/src/service_inspectors/nhttp_inspect/Makefile.am b/src/service_inspectors/nhttp_inspect/Makefile.am
new file mode 100644 (file)
index 0000000..db9ffca
--- /dev/null
@@ -0,0 +1,29 @@
+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_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
+
+
+#if STATIC_INSPECTORS
+noinst_LIBRARIES = libnhttp_inspect.a
+libnhttp_inspect_a_SOURCES = $(file_list)
+#else
+# need to fix undefineds first :(
+#shlibdir = $(pkglibdir)/inspectors
+#shlib_LTLIBRARIES = libnhttp_inspect.la
+#libnhttp_inspect_la_CXXFLAGS = $(AM_CXXFLAGS) -DBUILDING_SO
+#libnhttp_inspect_la_LDFLAGS = -export-dynamic -shared
+#libnhttp_inspect_la_SOURCES = $(file_list)
+#endif
+
+AM_CXXFLAGS = @AM_CXXFLAGS@
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_api.cc b/src/service_inspectors/nhttp_inspect/nhttp_api.cc
new file mode 100644 (file)
index 0000000..6c42d73
--- /dev/null
@@ -0,0 +1,145 @@
+/****************************************************************************
+ *
+** 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      API for NHttpInspect
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "snort.h"
+#include "framework/parameter.h"
+#include "framework/module.h"
+#include "framework/inspector.h"
+#include "flow/flow.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"
+
+Module* NHttpApi::nhttp_mod_ctor() { return new NHttpModule; }
+
+void NHttpApi::nhttp_mod_dtor(Module* m) { delete m; }
+
+const char* NHttpApi::nhttp_myName = "nhttp_inspect";
+
+void NHttpApi::nhttp_init()
+{
+    printf("nhttp_init()\n");
+    NHttpFlowData::init();
+}
+
+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;
+}
+
+void NHttpApi::nhttp_pterm()
+{
+    printf("nhttp_pterm()\n");
+    delete NHttpInspect::msgHead;
+}
+
+void NHttpApi::nhttp_purge()
+{
+    printf("nhttp_purge()\n");
+}
+
+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 =
+{
+    {
+        PT_INSPECTOR,
+        NHttpApi::nhttp_myName,
+        INSAPI_PLUGIN_V0,
+        0,
+        NHttpApi::nhttp_mod_ctor,
+        NHttpApi::nhttp_mod_dtor
+    },
+    PRIORITY_APPLICATION,
+    PROTO_BIT__TCP,
+    NHttpApi::nhttp_init,
+    NHttpApi::nhttp_term,
+    NHttpApi::nhttp_ctor,
+    NHttpApi::nhttp_dtor,
+    NHttpApi::nhttp_pinit,
+    NHttpApi::nhttp_pterm,
+    NHttpApi::nhttp_purge,
+    NHttpApi::nhttp_sum,
+    NHttpApi::nhttp_stats,
+    NHttpApi::nhttp_reset
+};
+
+#ifdef BUILDING_SO
+SO_PUBLIC const BaseApi* snort_plugins[] =
+{
+    &NHttpApi::nhttp_api.base,
+    nullptr
+};
+#else
+const BaseApi* sin_nhttp = &NHttpApi::nhttp_api.base;
+#endif
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_api.h b/src/service_inspectors/nhttp_inspect/nhttp_api.h
new file mode 100644 (file)
index 0000000..79f4ec0
--- /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      Noninstantiated class to collect static framework API functions and facilitate friendships.
+//
+
+#ifndef NHTTP_API_H
+#define NHTTP_API_H
+
+class NHttpApi {
+public:
+    static const InspectApi nhttp_api;
+private:
+    NHttpApi() = delete;
+    static Module* nhttp_mod_ctor();
+    static void nhttp_mod_dtor(Module* m);
+    static const char* nhttp_myName;
+    static void nhttp_init();
+    static void nhttp_term();
+    static Inspector* nhttp_ctor(Module* mod);
+    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();
+};
+
+#endif
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_enum.h b/src/service_inspectors/nhttp_inspect/nhttp_enum.h
new file mode 100644 (file)
index 0000000..46355c6
--- /dev/null
@@ -0,0 +1,85 @@
+/****************************************************************************
+ *
+** 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 enumerations
+//
+
+#ifndef NHTTP_ENUM_H
+#define NHTTP_ENUM_H
+
+#define GID_HTTP_CLIENT 119
+#define GID_HTTP_SERVER 120
+
+namespace NHttpEnums {
+
+// 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;
+
+// Type of message section
+typedef enum { SEC__PROBLEMATIC=-2, SEC_HEADER = 2, SEC_BODY, SEC_CHUNK } 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;
+
+// Every request method we have ever heard of
+typedef enum { METH__NOTCOMPUTE=-4, METH__INSUFMEMORY=-3, METH__PROBLEMATIC=-2, METH__OTHER=1, METH_GET, METH_HEAD, METH_POST, METH_PUT, METH_DELETE, METH_TRACE, METH_CONNECT, METH_PROPFIND,
+   METH_PROPPATCH, METH_MKCOL, METH_COPY, METH_MOVE, METH_LOCK, METH_UNLOCK, METH_VERSION_CONTROL, METH_REPORT, METH_CHECKOUT, METH_CHECKIN, METH_UNCHECKOUT,
+   METH_MKWORKSPACE, METH_UPDATE, METH_LABEL, METH_MERGE, METH_BASELINE_CONTROL, METH_MKACTIVITY, METH_ORDERPATCH, METH_ACL, METH_PATCH, METH_SEARCH, METH_BCOPY,
+   METH_BDELETE, METH_BMOVE, METH_BPROPFIND, METH_BPROPPATCH, METH_NOTIFY, METH_POLL, METH_SUBSCRIBE, METH_UNSUBSCRIBE, METH_X_MS_ENUMATTS, METH_BIND, METH_LINK,
+   METH_MKCALENDAR, METH_MKREDIRECTREF, METH_REBIND, METH_UNBIND, METH_UNLINK, METH_UPDATEREDIRECTREF } MethodId;
+
+// Every header we have ever heard of
+typedef enum { HEAD__NOTCOMPUTE=-4, HEAD__INSUFMEMORY=-3, HEAD__PROBLEMATIC=-2, HEAD__NOTPRESENT=-1, HEAD__OTHER=1, HEAD_CACHE_CONTROL, HEAD_CONNECTION, HEAD_DATE,
+   HEAD_PRAGMA, HEAD_TRAILER, HEAD_COOKIE, HEAD_SET_COOKIE,
+   HEAD_TRANSFER_ENCODING, HEAD_UPGRADE, HEAD_VIA, HEAD_WARNING, HEAD_ACCEPT, HEAD_ACCEPT_CHARSET, HEAD_ACCEPT_ENCODING, HEAD_ACCEPT_LANGUAGE, HEAD_AUTHORIZATION,
+   HEAD_EXPECT, HEAD_FROM, HEAD_HOST, HEAD_IF_MATCH, HEAD_IF_MODIFIED_SINCE, HEAD_IF_NONE_MATCH, HEAD_IF_RANGE, HEAD_IF_UNMODIFIED_SINCE, HEAD_MAX_FORWARDS,
+   HEAD_PROXY_AUTHORIZATION, HEAD_RANGE, HEAD_REFERER, HEAD_TE, HEAD_USER_AGENT, HEAD_ACCEPT_RANGES, HEAD_AGE, HEAD_ETAG, HEAD_LOCATION, HEAD_PROXY_AUTHENTICATE,
+   HEAD_RETRY_AFTER, HEAD_SERVER, HEAD_VARY, HEAD_WWW_AUTHENTICATE, HEAD_ALLOW, HEAD_CONTENT_ENCODING, HEAD_CONTENT_LANGUAGE, HEAD_CONTENT_LENGTH,
+   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;
+
+// Formats for output from a header normalization function
+typedef enum { NORM_NULL, NORM_FIELD, NORM_INTEGER, NORM_ENUM, NORM_ENUMLIST } NormFormat;
+
+// Transfer codings
+typedef enum { TRANSCODE__OTHER=1, TRANSCODE_CHUNKED, TRANSCODE_IDENTITY, TRANSCODE_GZIP, TRANSCODE_COMPRESS, TRANSCODE_DEFLATE } Transcoding;
+
+} // end namespace NHttpEnums
+
+// Individual pieces of the message found during parsing
+// Set length to -1 when field has no value
+typedef struct {
+    const uint8_t *start = nullptr;
+    int32_t length = -1;
+} field;
+
+#endif
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_flowdata.cc b/src/service_inspectors/nhttp_inspect/nhttp_flowdata.cc
new file mode 100644 (file)
index 0000000..2ac2a73
--- /dev/null
@@ -0,0 +1,46 @@
+/****************************************************************************
+ *
+** 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 "flow/flow.h"
+#include "nhttp_enum.h"
+#include "nhttp_flowdata.h"
+
+unsigned NHttpFlowData::nhttp_flow_id = 0;
+
+NHttpFlowData::NHttpFlowData() : FlowData(nhttp_flow_id)
+{
+}
+
+NHttpFlowData::~NHttpFlowData()
+{
+}
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_flowdata.h b/src/service_inspectors/nhttp_inspect/nhttp_flowdata.h
new file mode 100644 (file)
index 0000000..9d865f9
--- /dev/null
@@ -0,0 +1,57 @@
+/****************************************************************************
+ *
+** 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_FLOWDATA_H
+#define NHTTP_FLOWDATA_H
+
+class NHttpFlowData : public FlowData
+{
+public:
+    NHttpFlowData();
+    ~NHttpFlowData();
+    static unsigned nhttp_flow_id;
+    static void init() { nhttp_flow_id = FlowData::get_flow_id(); };
+
+    // PAF data
+    NHttpEnums::SourceId sourceId = NHttpEnums::SRC_CLIENT;
+    bool tcpClose = false;
+    uint64_t infractions = 0;
+    
+    // Client message data
+    NHttpEnums::SectionType clientTypeExpected = NHttpEnums::SEC_HEADER;
+    int32_t clientOctetsExpected = NHttpEnums::STAT_NOTPRESENT;
+
+    // Server message data
+    NHttpEnums::SectionType serverTypeExpected = NHttpEnums::SEC_HEADER;
+    int32_t serverOctetsExpected = NHttpEnums::STAT_NOTPRESENT;
+
+    // Session data
+};
+
+#endif
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_headnorm.cc b/src/service_inspectors/nhttp_inspect/nhttp_headnorm.cc
new file mode 100644 (file)
index 0000000..cd5b960
--- /dev/null
@@ -0,0 +1,187 @@
+/****************************************************************************
+ *
+** 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_scratchpad.h"
+#include "nhttp_strtocode.h"
+#include "nhttp_headnorm.h"
+
+using namespace NHttpEnums;
+
+// This derivation removes embedded CRLFs (wrapping), omits leading and trailing 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) const {
+    int32_t outLength = 0;
+    bool lastWhite = true;
+    for (int 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
+    bufferLength += (4-bufferLength%4)%4 + 200;  // &&& 200 is a "way too big" fudge factor to allow for modest expansion of field size during normalization. Needs improvement.
+    uint8_t *scratch;
+    if ((scratch = scratchPad.request(2*bufferLength)) == 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 growth;
+    int32_t dataLength = 0;
+    for (int j=0; j < numMatches; j++) {
+        if (j >= 1) {
+            *working++ = ',';
+            dataLength++;
+            while (headerNameId[++currMatch] != headId);
+        }
+        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 *notUsed) {
+    uint32_t total = 0;
+    int value;
+    for (int32_t k=0; k < inLength; k++) {
+        value = inBuf[k] - '0';
+        if ((value < 0) || (value > 9)) {
+            infractions |= INF_BADHEADERDATA;
+            return STAT_PROBLEMATIC;
+        }
+        total = total*10 + value;
+    }
+    ((uint32_t*)outBuf)[0] = total;
+    return sizeof(uint32_t);
+}
+
+
+int32_t norm2Lower(const uint8_t* inBuf, int32_t inLength, uint8_t* outBuf, uint64_t& infractions, const void *notUsed) {
+    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& infractions, const void *table) {
+    ((uint32_t*)outBuf)[0] = strToCode(inBuf, inLength, (const StrCode*)table);
+    return sizeof(uint32_t);
+}
+
+int32_t normSeqStrCode(const uint8_t* inBuf, int32_t inLength, uint8_t* outBuf, uint64_t& infractions, const void *table) {
+    int32_t numCodes = 0;
+    const uint8_t* start = inBuf;
+    int32_t length = 0;
+    while (true) {
+        start += length;
+        for (length = 0; (start + length < inBuf + inLength) && (start[length] != ','); length++);
+        if (length == 0) ((uint32_t*)outBuf)[numCodes++] = STAT_EMPTYSTRING;
+        else ((uint32_t*)outBuf)[numCodes++] = strToCode(start, length, (const StrCode*)table);
+        if (start + length++ >= inBuf + inLength) break;
+    }
+    return numCodes * sizeof(uint32_t);
+}
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_headnorm.h b/src/service_inspectors/nhttp_inspect/nhttp_headnorm.h
new file mode 100644 (file)
index 0000000..5a31626
--- /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      HeaderNormalizer class declaration
+//
+
+#ifndef NHTTP_HEADNORM_H
+#define NHTTP_HEADNORM_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:
+    int32_t deriveHeaderContent(const uint8_t *value, int32_t length, uint8_t *buffer) const;
+
+    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*);
+
+#endif
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_inspect.cc b/src/service_inspectors/nhttp_inspect/nhttp_inspect.cc
new file mode 100644 (file)
index 0000000..fb6e196
--- /dev/null
@@ -0,0 +1,148 @@
+/****************************************************************************
+ *
+** 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 Inspector class.
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdexcept>
+
+#include "snort.h"
+#include "framework/inspector.h"
+#include "flow/flow.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_api.h"
+#include "nhttp_inspect.h"
+
+const char* NHttpInspect::testInputFile = "nhttptestmsgs.txt";
+const char* NHttpInspect::testOutputPrefix = "nhttpresults/testcase";
+THREAD_LOCAL NHttpMsgHeader* NHttpInspect::msgHead;
+
+NHttpInspect::NHttpInspect(bool _test_mode) : test_mode(_test_mode)
+{
+    printf("NHttpInspect constructor()\n");
+    if (test_mode) {
+        testInput = new NHttpTestInput(testInputFile);
+    }
+}
+
+NHttpInspect::~NHttpInspect ()
+{
+    printf("NHttpInspect destructor()\n");
+    if (test_mode) {
+        delete testInput;
+        if (testOut) fclose(testOut);
+    }
+}
+
+bool NHttpInspect::enabled ()
+{
+    printf("NHttpInspect enabled()\n");
+    return true;
+}
+
+void NHttpInspect::configure (SnortConfig *sc, const char*, char *args)
+{
+    printf("NHttpInspect configure()\n");
+}
+
+int NHttpInspect::verify(SnortConfig* sc)
+{
+    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");
+
+    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);
+    else {
+        uint8_t *testBuffer;
+        int32_t testLength;
+        if ((testLength = testInput->ntiGet(&testBuffer, sessionData, testNumber)) > 0) {
+            msgHead->loadMessage(testBuffer, testLength, sessionData);
+        }
+        else {
+            printf("Out of test data.\n");
+            return;
+        }
+    }
+
+    msgHead->analyze();
+
+    msgHead->genEvents();
+
+    // Interface to the old Snort clients
+    msgHead->oldClients();
+
+    if (!test_mode) msgHead->printMessage(stdout);
+    else {
+        if (testNumber != fileTestNumber) {
+            if (testOut) fclose (testOut);
+            fileTestNumber = testNumber;
+            char fileName[100];
+            sprintf(fileName, "%s%d.txt", testOutputPrefix, testNumber);
+            if ((testOut = fopen(fileName, "w+")) == nullptr) throw std::runtime_error("Cannot open test output file");
+        }
+        msgHead->printMessage(testOut);
+    }
+}
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_inspect.h b/src/service_inspectors/nhttp_inspect/nhttp_inspect.h
new file mode 100644 (file)
index 0000000..4a1d577
--- /dev/null
@@ -0,0 +1,58 @@
+/****************************************************************************
+ *
+** 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.
+ *
+ ****************************************************************************/
+
+#ifndef NHTTP_INSPECT_H
+#define NHTTP_INSPECT_H
+
+//-------------------------------------------------------------------------
+// NHttpInspect class
+//-------------------------------------------------------------------------
+
+class NHttpInspect : public Inspector {
+public:
+    NHttpInspect(bool _test_mode);
+    ~NHttpInspect();
+
+    void configure(SnortConfig*, const char*, char *args);
+    int verify(SnortConfig*);
+    void show(SnortConfig*);
+    void eval(Packet*);
+    bool enabled();
+    void pinit();
+    void pterm();
+
+private:
+    friend NHttpApi;
+    static THREAD_LOCAL NHttpMsgHeader *msgHead;
+
+    // Test mode
+    bool test_mode;
+    static const char *testInputFile;
+    static const char *testOutputPrefix;
+    NHttpTestInput *testInput = nullptr;
+    FILE *testOut = nullptr;
+    int64_t testNumber = 0;
+    int64_t fileTestNumber = -1;
+};
+
+#endif
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_module.cc b/src/service_inspectors/nhttp_inspect/nhttp_module.cc
new file mode 100644 (file)
index 0000000..04ac352
--- /dev/null
@@ -0,0 +1,66 @@
+/****************************************************************************
+ *
+** 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      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"
+
+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;
+    }
+    return false;
+}
+
+unsigned NHttpModule::get_gid() const {
+    printf("NHttpModule get_gid()\n");
+    return GID_HTTP_CLIENT;
+}
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_module.h b/src/service_inspectors/nhttp_inspect/nhttp_module.h
new file mode 100644 (file)
index 0000000..22f1f8f
--- /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      NHttpModule
+//
+
+#ifndef NHTTP_MODULE_H
+#define NHTTP_MODULE_H
+
+class NHttpModule : public Module
+{
+public:
+    NHttpModule() : Module("nhttp_inspect", nhttpParams) { printf("NHttpModule constructor()\n"); };
+    bool begin(const char*, int, SnortConfig*);
+    bool end(const char*, int, SnortConfig*);
+    bool set(const char*, Value&, SnortConfig*);
+    unsigned get_gid() const;
+    bool get_test_mode() const { return test_mode; };
+private:
+    static const Parameter nhttpParams[];
+    bool test_mode = false;
+};
+
+#endif
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msgheader.cc b/src/service_inspectors/nhttp_inspect/nhttp_msgheader.cc
new file mode 100644 (file)
index 0000000..cbaaff2
--- /dev/null
@@ -0,0 +1,412 @@
+/****************************************************************************
+ *
+** 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 messages.
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "snort.h"
+#include "flow/flow.h"
+#include "detection/detection_util.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"
+
+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 NHttpMsgHeader::findCrlf(const uint8_t* buffer, uint32_t length, bool wrappable) {
+    for (uint32_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;
+}
+
+// Reinitialize everything and load a new message
+void NHttpMsgHeader::loadMessage(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_) {
+    length = (bufsize <= MAXOCTETS) ? bufsize : MAXOCTETS;
+    memcpy(rawBuf, buffer, length);
+
+    sessionData = sessionData_;
+    infractions = sessionData->infractions;
+    sourceId = sessionData->sourceId;
+    tcpClose = sessionData->tcpClose;
+
+    scratchPad.reinit();
+
+    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;
+    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 NHttpMsgHeader::analyze() {
+    parseWhole();
+    if (sourceId == SRC_CLIENT) {
+        parseRequestLine();
+        deriveMethodId();
+    }
+    else if (sourceId == SRC_SERVER) {
+        parseStatusLine();
+        deriveStatusCodeNum();
+    }
+    deriveVersionId();
+    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]);
+    }
+}
+
+// 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)));
+    // 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)));
+
+    // 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;
+    assert (startLine.length == version.length + statusCode.length + reasonPhrase.length + 2);
+}
+
+// Divide up the block of header fields into individual header field lines.
+void NHttpMsgHeader::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 NHttpMsgHeader::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 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::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 NHttpMsgHeader::genEvents() {
+    if (infractions != 0) SnortEventqAdd(GID_HTTP_CLIENT, 1); // I'm just an example event (HI_CLIENT_ASCII)
+}
+
+void NHttpMsgHeader::printInterval(FILE *output, const char* name, const uint8_t *text, int32_t length, bool intVals) {
+    if ((length == STAT_NOTPRESENT) || (length == STAT_NOTCOMPUTE)) return;
+    fprintf(output, "%s, length = %d\n", name, length);
+    if (length <= 0) return;
+    if (text == nullptr) {
+        fprintf(output, "nullptr\n");
+        return;
+    }
+    for (int k=0; k < length; k++) {
+        if ((text[k] >= 0x20) && (text[k] <= 0x7E)) fprintf(output, "%c", (char)text[k]);
+        else if (text[k] == 0x0) fprintf(output, "~");
+        else if (text[k] == 0xD) fprintf(output, "`");
+        else if (text[k] == 0xA) fprintf(output, "'");
+        else fprintf(output, "*");
+        if (k%200 == 199) fprintf(output, "\n");
+    }
+
+    if (intVals && (length%4 == 0)) {
+        fprintf(output, "\nInteger values =");
+        for (int j=0; j < length; j+=4) {
+            fprintf(output, " %u", *((const uint32_t*)(text+j)));
+        }
+    }
+    fprintf(output, "\n");
+}
+
+void NHttpMsgHeader::printMessage(FILE *output) {
+    fprintf(output, "Printout of HTTP message structure.\n");
+    printInterval(output, "Raw message", msgText, length);
+    printInterval(output, "Start Line", startLine.start, startLine.length);
+    if (sourceId != SRC__NOTCOMPUTE) fprintf(output, "Source Id: %d\n", sourceId);
+    printInterval(output, "Version", version.start, version.length);
+    if (versionId != VERS__NOTCOMPUTE) fprintf(output, "Version Id: %d\n", versionId);
+    printInterval(output, "Method", method.start, method.length);
+    if (methodId != METH__NOTCOMPUTE) fprintf(output, "Method Id: %d\n", methodId);
+    printInterval(output, "URI", uri.start, uri.length);
+    printInterval(output, "Status Code", statusCode.start, statusCode.length);
+    if (statusCodeNum != STAT_NOTCOMPUTE) fprintf(output, "Status Code Num: %d\n", statusCodeNum);
+    printInterval(output, "Reason Phrase", reasonPhrase.start, reasonPhrase.length);
+    printInterval(output, "Headers", headers.start, headers.length);
+    if (numHeaders != STAT_NOTCOMPUTE) fprintf(output, "Number of headers: %d\n", numHeaders);
+    for (int j=0; j < numHeaders && j < 200; j++) {
+        printInterval(output, "Header Line", headerLine[j].start, headerLine[j].length);
+        printInterval(output, "Header Name", headerName[j].start, headerName[j].length);
+        fprintf(output, "Header name Id: %d\n", headerNameId[j]);
+        printInterval(output, "Header Value", headerValue[j].start, headerValue[j].length);
+    }
+    for (int k=1; k <= numNorms; k++) {
+        if (headerValueNorm[k].length != STAT_NOTPRESENT) fprintf(output, "Header ID = %d\n", k);
+        printInterval(output, "Header Value Normalized", headerValueNorm[k].start, headerValueNorm[k].length, true);
+    }
+    fprintf(output, "Infractions: %lx\n", infractions);
+    fprintf(output, "TCP Close: %s\n", 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);
+    }
+}
+
+// Legacy support function. Puts message fields into the buffers used by old Snort. This should go away.
+void NHttpMsgHeader::oldClients() {
+    ClearHttpBuffers();
+
+    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 (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);
+    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);
+    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);
+}
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msgheader.h b/src/service_inspectors/nhttp_inspect/nhttp_msgheader.h
new file mode 100644 (file)
index 0000000..adc859f
--- /dev/null
@@ -0,0 +1,140 @@
+/****************************************************************************
+ *
+** 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_MSGHEADER_H
+#define NHTTP_MSGHEADER_H
+
+
+//-------------------------------------------------------------------------
+// NHttpMsgHeader class
+//-------------------------------------------------------------------------
+
+class NHttpMsgHeader {
+public:
+    NHttpMsgHeader() {};
+    void loadMessage(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_);
+    void analyze();
+    void printMessage(FILE *output);
+    void genEvents();
+    void oldClients();  // I'm a legacy support method and should go away eventually
+    static const uint32_t MAXOCTETS = 65535;
+
+private:
+    // 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 methodList[];
+    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.
+    void parseWhole();
+    void deriveSourceId();
+    void parseRequestLine();
+    void parseStatusLine();
+    void parseHeaderBlock();
+    void parseHeaderLines();
+    void deriveHeaderNameId(int index);
+    void deriveStatusCodeNum();
+    void deriveVersionId();
+    void deriveMethodId();
+
+    // Convenience methods
+    uint32_t findCrlf(const uint8_t* buffer, uint32_t length, bool wrappable);
+    void printInterval(FILE *output, const char* name, const uint8_t *text, int32_t length, bool intVals = false);
+
+    // The current strategy is to copy the entire raw message headers into this object. Here it is.
+    uint32_t length;              // Length of the original message headers in octets
+    uint8_t rawBuf[MAXOCTETS];    // The original HTTP message header 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.
+    uint32_t derivedBuf[MAXOCTETS/4];
+    NHttpFlowData* sessionData;
+    ScratchPad scratchPad {derivedBuf, MAXOCTETS/4};
+
+    // 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 original message buffer (raw pieces) or the
+    // scratchPad (normalized pieces).
+    uint64_t infractions;
+    bool tcpClose;
+    field startLine;
+    NHttpEnums::SourceId sourceId;
+    field version;
+    NHttpEnums::VersionId versionId;
+    field method;
+    NHttpEnums::MethodId methodId;
+    field uri;
+    field statusCode;
+    int32_t statusCodeNum;
+    field reasonPhrase;
+    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_scratchpad.h b/src/service_inspectors/nhttp_inspect/nhttp_scratchpad.h
new file mode 100644 (file)
index 0000000..fd568f8
--- /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_SCRATCHPAD_H
+#define NHTTP_SCRATCHPAD_H
+
+
+//-------------------------------------------------------------------------
+// ScratchPad class
+// Memory management for NHttpMsgHeader class
+//-------------------------------------------------------------------------
+
+// Working space and storage for all the derived fields
+// Return value of request is 32-bit aligned and may be freely cast to uint32_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(uint32_t *buff, uint32_t length) : buffer(buff), capacity(length*4), used(0) {}; // Careful: length must be number of uint32_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 + (4-(taken%4))%4; };  // round up to multiple of 4 to preserve alignment
+
+private:
+    uint32_t *buffer;
+    uint32_t capacity;
+    uint32_t used;
+};
+
+#endif
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_strtocode.cc b/src/service_inspectors/nhttp_inspect/nhttp_strtocode.cc
new file mode 100644 (file)
index 0000000..5ad95a4
--- /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_strtocode.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_strtocode.h b/src/service_inspectors/nhttp_inspect/nhttp_strtocode.h
new file mode 100644 (file)
index 0000000..5aceece
--- /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_STRTOCODE_H
+#define NHTTP_STRTOCODE_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_tables.cc b/src/service_inspectors/nhttp_inspect/nhttp_tables.cc
new file mode 100644 (file)
index 0000000..38bb800
--- /dev/null
@@ -0,0 +1,216 @@
+/****************************************************************************
+ *
+** 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      Static constant tables for converting protocol strings to enum codes. Members of HttpMsgHeader.
+//
+
+
+#include <string.h>
+#include <sys/types.h>
+
+#include "snort.h"
+#include "flow/flow.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"
+
+using namespace NHttpEnums;
+
+const StrCode NHttpMsgHeader::methodList[] =
+   {{ METH_GET,                "GET"},
+    { METH_HEAD,               "HEAD"},
+    { METH_POST,               "POST"},
+    { METH_PUT,                "PUT"},
+    { METH_DELETE,             "DELETE"},
+    { METH_TRACE,              "TRACE"},
+    { METH_CONNECT,            "CONNECT"},
+    { METH_PROPFIND,           "PROPFIND"},
+    { METH_PROPPATCH,          "PROPPATCH"},
+    { METH_MKCOL,              "MKCOL"},
+    { METH_COPY,               "COPY"},
+    { METH_MOVE,               "MOVE"},
+    { METH_LOCK,               "LOCK"},
+    { METH_UNLOCK,             "UNLOCK"},
+    { METH_VERSION_CONTROL,    "VERSION-CONTROL"},
+    { METH_REPORT,             "REPORT"},
+    { METH_CHECKOUT,           "CHECKOUT"},
+    { METH_CHECKIN,            "CHECKIN"},
+    { METH_UNCHECKOUT,         "UNCHECKOUT"},
+    { METH_MKWORKSPACE,        "MKWORKSPACE"},
+    { METH_UPDATE,             "UPDATE"},
+    { METH_LABEL,              "LABEL"},
+    { METH_MERGE,              "MERGE"},
+    { METH_BASELINE_CONTROL,   "BASELINE-CONTROL"},
+    { METH_MKACTIVITY,         "MKACTIVITY"},
+    { METH_ORDERPATCH,         "ORDERPATCH"},
+    { METH_ACL,                "ACL"},
+    { METH_PATCH,              "PATCH"},
+    { METH_SEARCH,             "SEARCH"},
+    { METH_BCOPY,              "BCOPY"},
+    { METH_BDELETE,            "BDELETE"},
+    { METH_BMOVE,              "BMOVE"},
+    { METH_BPROPFIND,          "BPROPFIND"},
+    { METH_BPROPPATCH,         "BPROPPATCH"},
+    { METH_NOTIFY,             "NOTIFY"},
+    { METH_POLL,               "POLL"},
+    { METH_SUBSCRIBE,          "SUBSCRIBE"},
+    { METH_UNSUBSCRIBE,        "UNSUBSCRIBE"},
+    { METH_X_MS_ENUMATTS,      "X-MS-ENUMATTS"},
+    { METH_BIND,               "BIND"},
+    { METH_LINK,               "LINK"},
+    { METH_MKCALENDAR,         "MKCALENDAR"},
+    { METH_MKREDIRECTREF,      "MKREDIRECTREF"},
+    { METH_REBIND,             "REBIND"},
+    { METH_UNBIND,             "UNBIND"},
+    { METH_UNLINK,             "UNLINK"},
+    { METH_UPDATEREDIRECTREF,  "UPDATEREDIRECTREF"},
+    { 0,                       nullptr} };
+
+const StrCode NHttpMsgHeader::headerList[] =
+   {{ HEAD_CACHE_CONTROL,        "cache-control"},
+    { HEAD_CONNECTION,           "connection"},
+    { HEAD_DATE,                 "date"},
+    { HEAD_PRAGMA,               "pragma"},
+    { HEAD_TRAILER,              "trailer"},
+    { HEAD_COOKIE,               "cookie"},
+    { HEAD_SET_COOKIE,           "set-cookie"},
+    { HEAD_TRANSFER_ENCODING,    "transfer-encoding"},
+    { HEAD_UPGRADE,              "upgrade"},
+    { HEAD_VIA,                  "via"},
+    { HEAD_WARNING,              "warning"},
+    { HEAD_ACCEPT,               "accept"},
+    { HEAD_ACCEPT_CHARSET,       "accept-charset"},
+    { HEAD_ACCEPT_ENCODING,      "accept-encoding"},
+    { HEAD_ACCEPT_LANGUAGE,      "accept-language"},
+    { HEAD_AUTHORIZATION,        "authorization"},
+    { HEAD_EXPECT,               "expect"},
+    { HEAD_FROM,                 "from"},
+    { HEAD_HOST,                 "host"},
+    { HEAD_IF_MATCH,             "if-match"},
+    { HEAD_IF_MODIFIED_SINCE,    "if-modified-since"},
+    { HEAD_IF_NONE_MATCH,        "if-none-match"},
+    { HEAD_IF_RANGE,             "if-range"},
+    { HEAD_IF_UNMODIFIED_SINCE,  "if-unmodified-since"},
+    { HEAD_MAX_FORWARDS,         "max-forwards"},
+    { HEAD_PROXY_AUTHORIZATION,  "proxy-authorization"},
+    { HEAD_RANGE,                "range"},
+    { HEAD_REFERER,              "referer"},
+    { HEAD_TE,                   "te"},
+    { HEAD_USER_AGENT,           "user-agent"},
+    { HEAD_ACCEPT_RANGES,        "accept-ranges"},
+    { HEAD_AGE,                  "age"},
+    { HEAD_ETAG,                 "etag"},
+    { HEAD_LOCATION,             "location"},
+    { HEAD_PROXY_AUTHENTICATE,   "proxy-authenticate"},
+    { HEAD_RETRY_AFTER,          "retry-after"},
+    { HEAD_SERVER,               "server"},
+    { HEAD_VARY,                 "vary"},
+    { HEAD_WWW_AUTHENTICATE,     "www-authenticate"},
+    { HEAD_ALLOW,                "allow"},
+    { HEAD_CONTENT_ENCODING,     "content-encoding"},
+    { HEAD_CONTENT_LANGUAGE,     "content-language"},
+    { HEAD_CONTENT_LENGTH,       "content-length"},
+    { HEAD_CONTENT_LOCATION,     "content-location"},
+    { HEAD_CONTENT_MD5,          "content-md5"},
+    { HEAD_CONTENT_RANGE,        "content-range"},
+    { HEAD_CONTENT_TYPE,         "content-type"},
+    { HEAD_EXPIRES,              "expires"},
+    { HEAD_LAST_MODIFIED,        "last-modified"},
+    { 0,                         nullptr} };
+
+const StrCode NHttpMsgHeader::transCodeList[] =
+   {{ TRANSCODE_CHUNKED,         "chunked"},
+    { TRANSCODE_IDENTITY,        "identity"},
+    { TRANSCODE_GZIP,            "gzip"},
+    { TRANSCODE_COMPRESS,        "compress"},
+    { 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* const NHttpMsgHeader::headerNorms[HEAD__MAXVALUE] = { [0] = &NORMALIZER_NIL,
+    [HEAD__OTHER] = &NORMALIZER_BASIC,
+    [HEAD_CACHE_CONTROL] = &NORMALIZER_BASIC,
+    [HEAD_CONNECTION] = &NORMALIZER_BASIC,
+    [HEAD_DATE] = &NORMALIZER_BASIC,
+    [HEAD_PRAGMA] = &NORMALIZER_BASIC,
+    [HEAD_TRAILER] = &NORMALIZER_BASIC,
+    [HEAD_COOKIE] = &NORMALIZER_BASIC,
+    [HEAD_SET_COOKIE] = &NORMALIZER_BASIC,
+    [HEAD_TRANSFER_ENCODING] = &NORMALIZER_TRANSCODE,
+    [HEAD_UPGRADE] = &NORMALIZER_BASIC,
+    [HEAD_VIA] = &NORMALIZER_BASIC,
+    [HEAD_WARNING] = &NORMALIZER_BASIC,
+    [HEAD_ACCEPT] = &NORMALIZER_BASIC,
+    [HEAD_ACCEPT_CHARSET] = &NORMALIZER_BASIC,
+    [HEAD_ACCEPT_ENCODING] = &NORMALIZER_BASIC,
+    [HEAD_ACCEPT_LANGUAGE] = &NORMALIZER_BASIC,
+    [HEAD_AUTHORIZATION] = &NORMALIZER_BASIC,
+    [HEAD_EXPECT] = &NORMALIZER_BASIC,
+    [HEAD_FROM] = &NORMALIZER_BASIC,
+    [HEAD_HOST] = &NORMALIZER_BASIC,
+    [HEAD_IF_MATCH] = &NORMALIZER_BASIC,
+    [HEAD_IF_MODIFIED_SINCE] = &NORMALIZER_BASIC,
+    [HEAD_IF_NONE_MATCH] = &NORMALIZER_BASIC,
+    [HEAD_IF_RANGE] = &NORMALIZER_BASIC,
+    [HEAD_IF_UNMODIFIED_SINCE] = &NORMALIZER_BASIC,
+    [HEAD_MAX_FORWARDS] = &NORMALIZER_BASIC,
+    [HEAD_PROXY_AUTHORIZATION] = &NORMALIZER_BASIC,
+    [HEAD_RANGE] = &NORMALIZER_BASIC,
+    [HEAD_REFERER] = &NORMALIZER_BASIC,
+    [HEAD_TE] = &NORMALIZER_BASIC,
+    [HEAD_USER_AGENT] = &NORMALIZER_BASIC,
+    [HEAD_ACCEPT_RANGES] = &NORMALIZER_BASIC,
+    [HEAD_AGE] = &NORMALIZER_BASIC,
+    [HEAD_ETAG] = &NORMALIZER_BASIC,
+    [HEAD_LOCATION] = &NORMALIZER_BASIC,
+    [HEAD_PROXY_AUTHENTICATE] = &NORMALIZER_BASIC,
+    [HEAD_RETRY_AFTER] = &NORMALIZER_BASIC,
+    [HEAD_SERVER] = &NORMALIZER_BASIC,
+    [HEAD_VARY] = &NORMALIZER_BASIC,
+    [HEAD_WWW_AUTHENTICATE] = &NORMALIZER_BASIC,
+    [HEAD_ALLOW] = &NORMALIZER_BASIC,
+    [HEAD_CONTENT_ENCODING] = &NORMALIZER_BASIC,
+    [HEAD_CONTENT_LANGUAGE] = &NORMALIZER_BASIC,
+    [HEAD_CONTENT_LENGTH] = &NORMALIZER_DECIMAL,
+    [HEAD_CONTENT_LOCATION] = &NORMALIZER_BASIC,
+    [HEAD_CONTENT_MD5] = &NORMALIZER_BASIC,
+    [HEAD_CONTENT_RANGE] = &NORMALIZER_BASIC,
+    [HEAD_CONTENT_TYPE] = &NORMALIZER_BASIC,
+    [HEAD_EXPIRES] = &NORMALIZER_BASIC,
+    [HEAD_LAST_MODIFIED] = &NORMALIZER_BASIC
+};
+
+    const int32_t NHttpMsgHeader::numNorms = HEAD__MAXVALUE-1;
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_testinput.cc b/src/service_inspectors/nhttp_inspect/nhttp_testinput.cc
new file mode 100644 (file)
index 0000000..e264e7f
--- /dev/null
@@ -0,0 +1,162 @@
+/****************************************************************************
+ *
+** 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 test messages
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdexcept>
+#include "snort.h"
+#include "flow/flow.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"
+
+NHttpTestInput::NHttpTestInput(const char *fileName) {
+    if ((msgFile = fopen(fileName, "r")) == nullptr) throw std::runtime_error("Cannot open test input file");
+}
+
+NHttpTestInput::~NHttpTestInput() {
+    fclose(msgFile);
+}
+
+// Read the next message section from the test data file.
+// In the process we may need to skip comments, execute simple commands, and handle simple escape sequences.
+// The best way to understand this function is to read the comments at the top of the file of test cases.
+int32_t NHttpTestInput::ntiGet(uint8_t **buffer, NHttpFlowData* sessionData, int64_t &testNumber) {
+    int32_t length = 0;
+    *buffer = msgBuf;
+    int newChar;
+    typedef enum { WAITING, COMMENT, COMMAND, SECTION, ESCAPE0, ESCAPE1, ESCAPE2 } State;
+    State state = WAITING;
+    bool ending;
+    char escapeNum[] = { 0, 0, '\0' };
+    int commandLength = 0;
+    const int MaxCommand = 100;
+    char commandValue[MaxCommand];
+
+    sessionData->tcpClose = false;
+    sessionData->infractions = 0;
+
+    while ((newChar = getc(msgFile)) != EOF) {
+        switch (state) {
+          case WAITING:
+            if (newChar == '#') state = COMMENT;
+            else if (newChar == '@') {
+                state = COMMAND;
+                commandLength = 0;
+            }
+            else if (newChar == '\\') {
+                state = ESCAPE0;
+                ending = false;
+            }
+            else if (newChar != '\n') {
+                state = SECTION;
+                ending = false;
+                msgBuf[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"))) sessionData->sourceId = NHttpEnums::SRC_CLIENT;
+                else if ((commandLength == strlen("response")) && !memcmp(commandValue, "response", strlen("response"))) sessionData->sourceId = NHttpEnums::SRC_SERVER;
+                else if ((commandLength == strlen("tcpclose")) && !memcmp(commandValue, "tcpclose", strlen("tcpclose"))) sessionData->tcpClose = true;
+                else if ((commandLength == strlen("break")) && !memcmp(commandValue, "break", strlen("break"))) {
+                    // &&& signifies start of new session so wipe "session data" when we define some. See flowdata.h.
+                }
+                else if (commandLength > 0) {
+                    // Look for a test number
+                    bool isNumber = true;
+                    for (int k=0; isNumber && (k < commandLength); 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;
+            }
+            break;
+          case SECTION:
+            if (newChar == '\\') {
+                state = ESCAPE0;
+            }
+            else if (newChar == '\n') {
+                if (ending) return length;  // Found the blank line that ends the section.
+                ending = true;
+            }
+            else {
+                ending = false;
+                msgBuf[length++] = (uint8_t) newChar;
+            }
+            break;
+          case ESCAPE0:
+            if (newChar == 'n')       { state = SECTION; ending = false; msgBuf[length++] = '\n'; }
+            else if (newChar == 'r')  { state = SECTION; ending = false; msgBuf[length++] = '\r'; }
+            else if (newChar == 't')  { state = SECTION; ending = false; msgBuf[length++] = '\t'; }
+            else if (newChar == '#')  { state = SECTION; ending = false; msgBuf[length++] = '#';  }
+            else if (newChar == '@')  { state = SECTION; ending = false; msgBuf[length++] = '@';  }
+            else if (newChar == '\\') { state = SECTION; ending = false; msgBuf[length++] = '\\'; }
+            else if ((newChar == 'x') || (newChar == 'X')) state = ESCAPE1;
+            else                      { state = SECTION; ending = false; }
+            break;
+          case ESCAPE1:
+            state = ESCAPE2;
+            escapeNum[0] = newChar;
+            break;
+          case ESCAPE2:
+            state = SECTION;
+            ending = false;
+            escapeNum[1] = newChar;
+            if (((escapeNum[0] < '0') || (escapeNum[0] > '9')) && ((escapeNum[0] < 'A') || (escapeNum[0] > 'Z')) && ((escapeNum[0] < 'a') || (escapeNum[0] > 'z'))) break;
+            if (((escapeNum[1] < '0') || (escapeNum[1] > '9')) && ((escapeNum[1] < 'A') || (escapeNum[1] > 'Z')) && ((escapeNum[1] < 'a') || (escapeNum[1] > 'z'))) break;
+            msgBuf[length++] = strtoul(escapeNum, nullptr, 16);
+            break;
+        }
+        // Return because the buffer is full. Not a feature just a safety precaution against bad input.
+        if (length >= sizeof(msgBuf)) return length;
+    }
+    // End-of-file. Return everything we have so far.
+    return length;
+}
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_testinput.h b/src/service_inspectors/nhttp_inspect/nhttp_testinput.h
new file mode 100644 (file)
index 0000000..2d578bd
--- /dev/null
@@ -0,0 +1,45 @@
+/****************************************************************************
+ *
+** 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_TESTINPUT_H
+#define NHTTP_TESTINPUT_H
+
+// ntiGet() returns the same static buffer each time.
+// Do not call it again before you are finished with the previous output.
+class NHttpTestInput {
+public:
+    NHttpTestInput(const char *fileName);
+    ~NHttpTestInput();
+    int32_t ntiGet(uint8_t **buffer, NHttpFlowData* sessionData, int64_t &testNumber);
+private:
+    FILE *msgFile;
+    uint8_t msgBuf[NHttpMsgHeader::MAXOCTETS];
+};
+
+#endif
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttptestmsgs.txt b/src/service_inspectors/nhttp_inspect/nhttptestmsgs.txt
new file mode 100644 (file)
index 0000000..7595d4d
--- /dev/null
@@ -0,0 +1,66 @@
+# A message section is the unit of data passed to the inspector and may by headers, body, a chunk, or trailing headers. 
+# Blank lines separate message sections. Extra blank lines don't have any effect.
+# Lines beginning with '@' are control commands.
+# You cannot put comments or control commands in the middle of a message section.
+# If a message section begins with # or @ (presumably a very rare situation) you must escape it: \# or \@
+# Control commands:
+#    @request or @response sets the message direction. Applies to subsequent sections until changed.
+#    @tcpclose specifies that the section ends with a TCP close. Applies only to the very next section.
+#    @<decimal number> sets the test number and hence the test output file name. Applies to subsequent sections until changed. Don't reuse numbers.
+#    Commands must be all lower case with no white space.
+# The escape character is \ and it is used only within message sections.
+# \# and \@ are only necessary for the very first character of a message section. \r, \n, \t, \x, \X, and \\ may appear anywhere in a section.
+# \x and \X are identical and signify the beginning of an arbitrary two-digit hexadecimal escape value. Examples: 0x3A, 0X27, 0xeE. Case a-f or A-F
+# does not matter.
+
+@1
+@response
+HTTP/1.1 200 OK\r\n\r\n
+
+@2
+@response
+HTTP/1.0 315 Redirection test with spaces\r\n\r\n
+
+@3
+@request
+MKREDIRECTREF / HTTP/2.0\r\n\r\n
+
+@4
+@request
+BIND 1234567890?abcdef HTTP/3.8\r\n\r\n
+
+@5
+@response
+HTTP/2.0 600 Bogus-status-code\r\n\r\n
+
+@6
+@response
+HTTP/1.9 26 Short?status++co\nde\r\n\r\n
+
+@7
+@response
+HTTP/1.F 502 abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghij
+klmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstu
+vwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890\r\n\r\n
+
+@8
+@request
+GET /test/hi-there.txt HTTP/1.0\r\n\r\n
+
+@9
+@request
+GET /test/hi-there.htm HTTP/1.1\r\nAccept: text/*\r\nAccept-Language: en,fr\r\n\r\n
+
+@10
+@request
+GET /test/trigger/hi-there.htm HTTP/1.1\r\nAccept: text/*\r\nAccept-Language: en,fr\r\n\r\n
+
+@11
+@request
+@tcpclose
+GET /test/trigger/hi-there.htm HTTP/1.1\r\nAccept: text/*\r\nAccept-Language: en,fr\r\n\r\n
+
+@12
+@response
+HTTP/2.0 200 OK\r\nContent-type: \ttext/plain\t\r\nContent-LENGTH: 19\r\n\r\n
+
index 8e0bd5652a578edee340e2cc139ccdb1f0d26850..4ef91c57bbfa37349eb028a51f93212cc1e2e2c3 100644 (file)
@@ -34,6 +34,7 @@ extern const BaseApi* sin_ftp_client;
 extern const BaseApi* sin_ftp_server;
 extern const BaseApi* sin_rpc_decode;
 extern const BaseApi* sin_telnet;
+extern const BaseApi* sin_nhttp;
 #endif
 
 const BaseApi* service_inspectors[] =
@@ -46,6 +47,7 @@ const BaseApi* service_inspectors[] =
     sin_ftp_server,
     sin_rpc_decode,
     sin_telnet,
+    sin_nhttp,
 #endif
     nullptr,
 };