]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
checkpoint after dynamic section allocation
authorTom Peters <thopeter@cisco.com>
Wed, 9 Jul 2014 22:09:06 +0000 (18:09 -0400)
committerTom Peters <thopeter@cisco.com>
Wed, 9 Jul 2014 22:09:06 +0000 (18:09 -0400)
39 files changed:
src/service_inspectors/nhttp_inspect/CMakeLists.txt
src/service_inspectors/nhttp_inspect/Makefile.am
src/service_inspectors/nhttp_inspect/nhttp_api.cc
src/service_inspectors/nhttp_inspect/nhttp_api.h
src/service_inspectors/nhttp_inspect/nhttp_enum.h
src/service_inspectors/nhttp_inspect/nhttp_flow_data.cc
src/service_inspectors/nhttp_inspect/nhttp_flow_data.h
src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc
src/service_inspectors/nhttp_inspect/nhttp_head_norm.h
src/service_inspectors/nhttp_inspect/nhttp_inspect.cc
src/service_inspectors/nhttp_inspect/nhttp_inspect.h
src/service_inspectors/nhttp_inspect/nhttp_module.cc
src/service_inspectors/nhttp_inspect/nhttp_module.h
src/service_inspectors/nhttp_inspect/nhttp_msg_body.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_body.h
src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.h
src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.h
src/service_inspectors/nhttp_inspect/nhttp_msg_head.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_head.h
src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.h
src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_request.h
src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_section.h
src/service_inspectors/nhttp_inspect/nhttp_msg_start.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_status.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_status.h
src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.h
src/service_inspectors/nhttp_inspect/nhttp_scratch_pad.h
src/service_inspectors/nhttp_inspect/nhttp_str_to_code.cc
src/service_inspectors/nhttp_inspect/nhttp_tables.cc
src/service_inspectors/nhttp_inspect/nhttp_uri.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_uri.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc
src/service_inspectors/nhttp_inspect/nhttp_uri_norm.h

index 361a076a4334659254df74dbfc5c3fe1e4d25818..7d990447448ebdce38994763f9b1b122f5278c81 100644 (file)
@@ -24,6 +24,8 @@ set (FILE_LIST
     nhttp_msg_trailer.h
     nhttp_head_norm.cc
     nhttp_head_norm.h
+    nhttp_uri.cc
+    nhttp_uri.h
     nhttp_uri_norm.cc
     nhttp_uri_norm.h
     nhttp_normalizers.cc
index d10c5da97160159dcf1fd9f17a031e8be7dc2e1e..c5370aab1dba820d491145114019524ac3d9dc10 100644 (file)
@@ -13,6 +13,7 @@ nhttp_msg_chunk_head.cc nhttp_msg_chunk_head.h \
 nhttp_msg_chunk_body.cc nhttp_msg_chunk_body.h \
 nhttp_msg_trailer.cc nhttp_msg_trailer.h \
 nhttp_head_norm.cc nhttp_head_norm.h \
+nhttp_uri.cc nhttp_uri.h \
 nhttp_uri_norm.cc nhttp_uri_norm.h \
 nhttp_normalizers.cc nhttp_normalizers.h \
 nhttp_str_to_code.cc nhttp_str_to_code.h \
index 68d6cd09267b91cd639b8cb87507c484f90b6856..94bff69771c6f5e50a7e21a7b89636cacc123480 100644 (file)
 #include "snort.h"
 #include "target_based/sftarget_protocol_reference.h"
 #include "nhttp_enum.h"
-#include "nhttp_module.h"
 #include "nhttp_inspect.h"
 #include "nhttp_api.h"
 
 int16_t NHttpApi::appProtocolId;
 
-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()
@@ -52,47 +47,10 @@ void NHttpApi::nhttp_init()
     appProtocolId = AddProtocolReference("nhttp");
 }
 
-void NHttpApi::nhttp_term()
-{
-}
-
 Inspector* NHttpApi::nhttp_ctor(Module* mod)
 {
     const NHttpModule* nhttpMod = (NHttpModule*) mod;
-    return new NHttpInspect(nhttpMod->get_test_input(), nhttpMod->get_test_output());
-}
-
-void NHttpApi::nhttp_dtor(Inspector* p)
-{
-    delete p;
-}
-
-void NHttpApi::nhttp_pinit()
-{
-    NHttpInspect::msgRequest = new NHttpMsgRequest;
-    NHttpInspect::msgStatus = new NHttpMsgStatus;
-    NHttpInspect::msgHead = new NHttpMsgHeader;
-    NHttpInspect::msgBody = new NHttpMsgBody;
-    NHttpInspect::msgChunkHead = new NHttpMsgChunkHead;
-    NHttpInspect::msgChunkBody = new NHttpMsgChunkBody;
-    NHttpInspect::msgTrailer = new NHttpMsgTrailer;
-}
-
-void NHttpApi::nhttp_pterm()
-{
-    delete NHttpInspect::msgHead;
-}
-
-void NHttpApi::nhttp_sum()
-{
-}
-
-void NHttpApi::nhttp_stats()
-{
-}
-
-void NHttpApi::nhttp_reset()
-{
+    return new NHttpInspect(nhttpMod->get_test_input(), nhttpMod->get_test_output(), nhttpMod->get_test_inspect());
 }
 
 static const char* buffers[] =
index 4335e79020d3ae0446d7a2e8fd0f6081849eecf1..4e4b6ee85507db361bd3c3d47aec9cbb85d4c647 100644 (file)
 #include "framework/module.h"
 #include "framework/inspector.h"
 
+#include "nhttp_module.h"
+
 class NHttpApi {
 public:
     static const InspectApi nhttp_api;
     static int16_t appProtocolId;
 private:
     NHttpApi() = delete;
-    static Module* nhttp_mod_ctor();
-    static void nhttp_mod_dtor(Module* m);
+    static Module* nhttp_mod_ctor() { return new NHttpModule; };
+    static void nhttp_mod_dtor(Module* m) { delete m; };
     static const char* nhttp_myName;
     static void nhttp_init();
-    static void nhttp_term();
+    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_sum();
-    static void nhttp_stats();
-    static void nhttp_reset();
+    static void nhttp_dtor(Inspector* p) { delete p; };
+    static void nhttp_pinit() {};
+    static void nhttp_pterm() {};
+    static void nhttp_sum() {};
+    static void nhttp_stats() {};
+    static void nhttp_reset() {};
 };
 
 #endif
index 8da00127f34b40dd9aeac7b370508c9fef12546b..fa94fb34209f9974e690b79f8594cd2c82c65e36 100644 (file)
@@ -38,7 +38,7 @@ namespace NHttpEnums {
 static const uint32_t MAXOCTETS = 63780;
 
 // Field status codes for when no valid value is present in length or integer value. Positive values are actual length or field value.
-typedef enum { STAT_NOTCONFIGURED=-5, STAT_NOTCOMPUTE=-4, STAT_INSUFMEMORY=-3, STAT_PROBLEMATIC=-2, STAT_NOTPRESENT=-1, STAT_EMPTYSTRING=0, STAT_OTHER=1 } StatusCode;
+typedef enum { STAT_NOSOURCE=-6, 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_CLIENT=0, SRC_SERVER=1 } SourceId;
@@ -48,21 +48,21 @@ typedef enum { SEC__NOTCOMPUTE=-4, SEC__NOTPRESENT=-1, SEC_REQUEST = 2, SEC_STAT
    SEC_CLOSED, SEC_ABORT } SectionType;
 
 // List of possible HTTP versions. Version 0.9 omitted because 0.9 predates creation of the HTTP/X.Y token. There would never be a message with "HTTP/0.9"
-typedef enum { VERS__NOTCOMPUTE=-4, VERS__PROBLEMATIC=-2, VERS__NOTPRESENT=-1, VERS__OTHER=1, VERS_1_0, VERS_1_1, VERS_2_0 } VersionId;
+typedef enum { VERS__NOSOURCE=-6, 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__NOTPRESENT=-1, METH__OTHER=1, METH_OPTIONS, METH_GET, METH_HEAD, METH_POST, METH_PUT,
-   METH_DELETE, METH_TRACE, METH_CONNECT, METH_PROPFIND,
+typedef enum { METH__NOSOURCE=-6, METH__NOTCOMPUTE=-4, METH__INSUFMEMORY=-3, METH__PROBLEMATIC=-2, METH__NOTPRESENT=-1, METH__OTHER=1, METH_OPTIONS, 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;
 
 // URI formats
-typedef enum { URI__NOTCOMPUTE=-4, URI__PROBLEMATIC=-2, URI__NOTPRESENT=-1, URI_ASTERISK = 2, URI_AUTHORITY, URI_ABSPATH, URI_ABSOLUTE } UriType;
+typedef enum { URI__NOSOURCE=-6, URI__NOTCOMPUTE=-4, URI__PROBLEMATIC=-2, URI_ASTERISK = 2, URI_AUTHORITY, URI_ABSPATH, URI_ABSOLUTE } UriType;
 
 // URI schemes
-typedef enum { SCH__NOTCOMPUTE=-4, SCH__INSUFMEMORY=-3, SCH__NOTPRESENT=-1, SCH_OTHER = 1, SCH_HTTP, SCH_HTTPS, SCH_FTP, SCH_GOPHER, SCH_FILE } SchemeId;
+typedef enum { SCH__NOSOURCE=-6, SCH__NOTCOMPUTE=-4, SCH__INSUFMEMORY=-3, SCH__NOTPRESENT=-1, SCH_OTHER = 1, SCH_HTTP, SCH_HTTPS, SCH_FTP, SCH_GOPHER, SCH_FILE } SchemeId;
 
 // 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,
@@ -71,7 +71,8 @@ typedef enum { HEAD__NOTCOMPUTE=-4, HEAD__INSUFMEMORY=-3, HEAD__PROBLEMATIC=-2,
    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;
+   HEAD_CONTENT_LOCATION, HEAD_CONTENT_MD5, HEAD_CONTENT_RANGE, HEAD_CONTENT_TYPE, HEAD_EXPIRES, HEAD_LAST_MODIFIED, HEAD_X_FORWARDED_FOR, HEAD_TRUE_CLIENT_IP,
+   HEAD__MAXVALUE } HeaderId;
 
 // All the infractions we might find while parsing and analyzing a message
 typedef enum {
@@ -99,8 +100,8 @@ typedef enum { TRANSCODE__OTHER=1, TRANSCODE_CHUNKED, TRANSCODE_IDENTITY, TRANSC
 // Never use the start pointer without verifying that length > 0.
 struct field {
 public:
-    const uint8_t *start;
-    int32_t length;
+    const uint8_t *start = nullptr;
+    int32_t length = NHttpEnums::STAT_NOTCOMPUTE;
 };
 
 typedef enum 
index 01b0f5a0e5325d053721ee746ec4966cc3bc0071..818c229000c74686d61ca1d32f28b5820238a509 100644 (file)
@@ -51,7 +51,6 @@ void NHttpFlowData::halfReset(SourceId sourceId) {
 
     versionId[sourceId] = VERS__NOTPRESENT;
     methodId[sourceId] = METH__NOTPRESENT;
-    schemeId[sourceId] = SCH__NOTPRESENT;
     statusCodeNum[sourceId] = STAT_NOTPRESENT;
 }
 
index a11ad7713822297579ed73f0b992b51c85b70af9..c35ed00be58ef849f058cb7c53c3e85e74bc3e84 100644 (file)
@@ -78,7 +78,6 @@ private:
     // Some items don't apply in both directions. Have two copies anyway just to simplify code and minimize hard-to-find bugs
     NHttpEnums::VersionId versionId[2] = { NHttpEnums::VERS__NOTPRESENT, NHttpEnums::VERS__NOTPRESENT };
     NHttpEnums::MethodId methodId[2] = { NHttpEnums::METH__NOTPRESENT, NHttpEnums::METH__NOTPRESENT };
-    NHttpEnums::SchemeId schemeId[2] = { NHttpEnums::SCH__NOTPRESENT, NHttpEnums::SCH__NOTPRESENT };
     int32_t statusCodeNum[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };
 
     int64_t dataLength[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };        // length of the data from Content-Length field or chunk header.      
index bd30faa333b1a17b6b84b68e4668a048666c9044..9b3d6adc858f71c97673cb1c23d557bc5825da5c 100644 (file)
@@ -59,12 +59,14 @@ int32_t HeaderNormalizer::deriveHeaderContent(const uint8_t *value, int32_t leng
     return outLength;
 }
 
-void HeaderNormalizer::normalize(ScratchPad &scratchPad, uint64_t &infractions, HeaderId headId, const HeaderId headerNameId[], const field headerValue[], int32_t numHeaders,
+// This method normalizes the header field value for headId.
+int32_t HeaderNormalizer::normalize(HeaderId headId, ScratchPad &scratchPad, uint64_t &infractions, const HeaderId headerNameId[], const field headerValue[], int32_t numHeaders,
         field &resultField) const {
-    // This method normalizes the header field value for headId.
+    // If the raw header is not present length will be STAT_NOSOURCE and normalization is skipped
+    if (resultField.length != STAT_NOTCOMPUTE) return resultField.length;
     if (format == NORM_NULL) {
         resultField.length = STAT_NOTCONFIGURED;
-        return;
+        return resultField.length;
     }
 
     // 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
@@ -85,8 +87,8 @@ void HeaderNormalizer::normalize(ScratchPad &scratchPad, uint64_t &infractions,
         }
     }
     if (numMatches == 0) {
-        resultField.length = STAT_NOTPRESENT;
-        return;
+        resultField.length = STAT_NOSOURCE;
+        return resultField.length;
     }
     if (infractRepeats && (numMatches >= 2)) infractions |= INF_BADHEADERREPS;
 
@@ -102,7 +104,7 @@ void HeaderNormalizer::normalize(ScratchPad &scratchPad, uint64_t &infractions,
     uint8_t * const scratch = scratchPad.request(2*bufferLength);
     if (scratch == nullptr) {
         resultField.length = STAT_INSUFMEMORY;
-        return;
+        return resultField.length;
     }
 
     uint8_t * const frontHalf = scratch;
@@ -127,12 +129,13 @@ void HeaderNormalizer::normalize(ScratchPad &scratchPad, uint64_t &infractions,
         else                         dataLength = normalizer[i](frontHalf, dataLength, backHalf, infractions, normArg[i]);
         if (dataLength <= 0) {
             resultField.length = dataLength;
-            return;
+            return resultField.length;
         }
     }
     resultField.start = scratch;
     resultField.length = dataLength;
     scratchPad.commit(dataLength);
+    return resultField.length;
 }
 
 
index 6aab0e2759bd3e9e294df312c5d7e76cbf52771b..63b0d7e03d40508ac0abd882ac49245f7c93c244 100644 (file)
@@ -52,7 +52,7 @@ public:
           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,
+    int32_t normalize(NHttpEnums::HeaderId headId, ScratchPad &scratchPad, uint64_t &infractions, const NHttpEnums::HeaderId headerNameId[], const field headerName[], int32_t numHeaders,
        field &resultField) const;
     NHttpEnums::NormFormat getFormat() const {return format;};
 
index 9b1bd16f4d82c6c9b5ce122cd0a5d9e0ff7a36fd..4bc2aec154bdf592136af188d9670656735ebf0e 100644 (file)
 
 using namespace NHttpEnums;
 
-THREAD_LOCAL NHttpMsgRequest* NHttpInspect::msgRequest;
-THREAD_LOCAL NHttpMsgStatus* NHttpInspect::msgStatus;
-THREAD_LOCAL NHttpMsgHeader* NHttpInspect::msgHead;
-THREAD_LOCAL NHttpMsgBody* NHttpInspect::msgBody;
-THREAD_LOCAL NHttpMsgChunkHead* NHttpInspect::msgChunkHead;
-THREAD_LOCAL NHttpMsgChunkBody* NHttpInspect::msgChunkBody;
-THREAD_LOCAL NHttpMsgTrailer* NHttpInspect::msgTrailer;
-
-NHttpInspect::NHttpInspect(bool test_input, bool _test_output) : test_output(_test_output)
+NHttpInspect::NHttpInspect(bool test_input, bool _test_output, bool _test_inspect) : test_output(_test_output), test_inspect(_test_inspect)
 {
     NHttpTestInput::test_input = test_input;
     if (NHttpTestInput::test_input) {
@@ -60,6 +52,7 @@ NHttpInspect::NHttpInspect(bool test_input, bool _test_output) : test_output(_te
 
 NHttpInspect::~NHttpInspect ()
 {
+    delete msgSection;
     if (NHttpTestInput::test_input) {
         delete NHttpTestInput::testInput;
         if (testOut) fclose(testOut);
@@ -116,55 +109,57 @@ void NHttpInspect::eval(Packet* p)
 
 void NHttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const flow)
 {
+    delete msgSection;
+    msgSection = nullptr;
+
     NHttpFlowData* sessionData = (NHttpFlowData*)flow->get_application_data(NHttpFlowData::nhttp_flow_id);
     assert(sessionData);
 
-    NHttpMsgSection *msgSect = nullptr;
-
     if (!NHttpTestInput::test_input) {
         switch (sessionData->sectionType) {
-          case SEC_REQUEST: msgSect = msgRequest; break;
-          case SEC_STATUS: msgSect = msgStatus; break;
-          case SEC_HEADER: msgSect = msgHead; break;
-          case SEC_BODY: msgSect = msgBody; break;
-          case SEC_CHUNKHEAD: msgSect = msgChunkHead; break;
-          case SEC_CHUNKBODY: msgSect = msgChunkBody; break;
-          case SEC_TRAILER: msgSect = msgTrailer; break;
+          case SEC_REQUEST: msgSection = new NHttpMsgRequest; break;
+          case SEC_STATUS: msgSection = new NHttpMsgStatus; break;
+          case SEC_HEADER: msgSection = new NHttpMsgHeader; break;
+          case SEC_BODY: msgSection = new NHttpMsgBody; break;
+          case SEC_CHUNKHEAD: msgSection = new NHttpMsgChunkHead; break;
+          case SEC_CHUNKBODY: msgSection = new NHttpMsgChunkBody; break;
+          case SEC_TRAILER: msgSection = new NHttpMsgTrailer; break;
           case SEC_DISCARD: return;
           default: assert(0); return;
         }
-        msgSect->loadSection(data, dsize, sessionData);
+        msgSection->loadSection(data, dsize, sessionData);
     }
     else {
         uint8_t *testBuffer;
         uint16_t testLength;
         if ((testLength = NHttpTestInput::testInput->toEval(&testBuffer, testNumber)) > 0) {
             switch (sessionData->sectionType) {
-              case SEC_REQUEST: msgSect = msgRequest; break;
-              case SEC_STATUS: msgSect = msgStatus; break;
-              case SEC_HEADER: msgSect = msgHead; break;
-              case SEC_BODY: msgSect = msgBody; break;
-              case SEC_CHUNKHEAD: msgSect = msgChunkHead; break;
-              case SEC_CHUNKBODY: msgSect = msgChunkBody; break;
-              case SEC_TRAILER: msgSect = msgTrailer; break;
+              case SEC_REQUEST: msgSection = new NHttpMsgRequest; break;
+              case SEC_STATUS: msgSection = new NHttpMsgStatus; break;
+              case SEC_HEADER: msgSection = new NHttpMsgHeader; break;
+              case SEC_BODY: msgSection = new NHttpMsgBody; break;
+              case SEC_CHUNKHEAD: msgSection = new NHttpMsgChunkHead; break;
+              case SEC_CHUNKBODY: msgSection = new NHttpMsgChunkBody; break;
+              case SEC_TRAILER: msgSection = new NHttpMsgTrailer; break;
               case SEC_DISCARD: return;
               default: assert(0); return;
             }
-            msgSect->loadSection(testBuffer, testLength, sessionData);
+            msgSection->loadSection(testBuffer, testLength, sessionData);
         }
         else {
             printf("Zero length test data.\n");
             return;
         }
     }
-    msgSect->initSection();
-    msgSect->analyze();
-    msgSect->updateFlow();
-    msgSect->genEvents();
-    msgSect->legacyClients();
+    msgSection->initSection();
+    msgSection->analyze();
+    msgSection->updateFlow();
+    msgSection->genEvents();
+    msgSection->legacyClients();
 
+    if (test_inspect) msgSection->analyzeAll();
     if (test_output) {
-        if (!NHttpTestInput::test_input) msgSect->printSection(stdout);
+        if (!NHttpTestInput::test_input) msgSection->printSection(stdout);
         else {
             if (testNumber != fileTestNumber) {
                 if (testOut) fclose (testOut);
@@ -173,7 +168,7 @@ void NHttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* cons
                 snprintf(fileName, sizeof(fileName), "%s%" PRIi64 ".txt", testOutputPrefix, testNumber);
                 if ((testOut = fopen(fileName, "w+")) == nullptr) throw std::runtime_error("Cannot open test output file");
             }
-            msgSect->printSection(testOut);
+            msgSection->printSection(testOut);
         }
     }
 }
index e49fdd92d248abf2962f3e8e8f29edd22e0162bd..e1db545f73fdf5ae17dd7e40ab8fa94b5946bd5d 100644 (file)
@@ -42,7 +42,7 @@ class NHttpApi;
 
 class NHttpInspect : public Inspector {
 public:
-    NHttpInspect(bool test_input, bool _test_output);
+    NHttpInspect(bool test_input, bool _test_output, bool _test_inspect);
     ~NHttpInspect();
 
     bool get_buf(unsigned, Packet*, InspectionBuffer&);
@@ -57,18 +57,13 @@ public:
 
 private:
     friend NHttpApi;
-    static THREAD_LOCAL NHttpMsgRequest *msgRequest;
-    static THREAD_LOCAL NHttpMsgStatus *msgStatus;
-    static THREAD_LOCAL NHttpMsgHeader *msgHead;
-    static THREAD_LOCAL NHttpMsgBody *msgBody;
-    static THREAD_LOCAL NHttpMsgChunkHead *msgChunkHead;
-    static THREAD_LOCAL NHttpMsgChunkBody *msgChunkBody;
-    static THREAD_LOCAL NHttpMsgTrailer *msgTrailer;
+    NHttpMsgSection *msgSection = nullptr;
 
     void process(const uint8_t* data, const uint16_t dsize, Flow* const flow);
 
     // Test mode
     bool test_output;
+    bool test_inspect;
     const char *testInputFile = "nhttp_test_msgs.txt";
     const char *testOutputPrefix = "nhttpresults/testcase";
     FILE *testOut = nullptr;
index 73b5670e1bc43298ecbf23b1c43f5de178842e30..691641f328ea368f46d5bce055e71c3639301c91 100644 (file)
@@ -39,11 +39,13 @@ NHttpModule::NHttpModule() : Module("nhttp_inspect", nhttpParams, nhttpEvents) {
 const Parameter NHttpModule::nhttpParams[] =
     {{ "test_input", Parameter::PT_BOOL, nullptr, "false", "read HTTP messages from text file" },
      { "test_output", Parameter::PT_BOOL, nullptr, "false", "print out HTTP section data" },
+     { "test_inspect", Parameter::PT_BOOL, nullptr, "false", "force all possible inspections and normalizations" },
      { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }};
 
 bool NHttpModule::begin(const char*, int, SnortConfig*) {
     test_input = false;
     test_output = false;
+    test_inspect = false;
     return true;
 }
 
@@ -54,6 +56,7 @@ bool NHttpModule::end(const char*, int, SnortConfig*) {
 bool NHttpModule::set(const char*, Value &val, SnortConfig*) {
     if (val.is("test_input")) test_input = val.get_bool();
     else if (val.is("test_output")) test_output = val.get_bool();
+    else if (val.is("test_inspect")) test_inspect = val.get_bool();
     else return false;
 
     return true;
index e1a9f566db607cab54b5a75b14db8a8ec1e3a24c..6b2b9a9939ea96a3c30ecd795cbbea2a0e1c2ebe 100644 (file)
@@ -41,11 +41,13 @@ public:
     unsigned get_gid() const;
     bool get_test_input() const { return test_input; };
     bool get_test_output() const { return test_output; };
+    bool get_test_inspect() const { return test_inspect; };
 private:
     static const Parameter nhttpParams[];
     static const RuleMap nhttpEvents[];
     bool test_input = false;
     bool test_output = false;
+    bool test_inspect = false;
 };
 
 #endif
index 2f0d9c76e820e458df3235533d6d4a913be06a30..20794737030e3accd6ae2f1d03c6a71bdd5dfbc4 100644 (file)
@@ -72,7 +72,7 @@ void NHttpMsgBody::printSection(FILE *output) const {
     NHttpMsgSection::printMessageWrapup(output);
 }
 
-void NHttpMsgBody::updateFlow() const {
+void NHttpMsgBody::updateFlow() {
     if (tcpClose) {
         sessionData->typeExpected[sourceId] = SEC_CLOSED;
         sessionData->halfReset(sourceId);
@@ -91,7 +91,7 @@ void NHttpMsgBody::updateFlow() const {
 
 
 // Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgBody::legacyClients() const {
+void NHttpMsgBody::legacyClients() {
     ClearHttpBuffers();
     if (data.length > 0) SetHttpBuffer(HTTP_BUFFER_CLIENT_BODY, data.start, (unsigned)data.length);
 }
index 3b6f3cf2be0c372a21fbdcd0c02a7fe075e9691c..97a3c5800473c4710e0c722b9aa0dce53d35e76c 100644 (file)
@@ -43,8 +43,8 @@ public:
     void analyze();
     void printSection(FILE *output) const;
     void genEvents();
-    void updateFlow() const;
-    void legacyClients() const;
+    void updateFlow();
+    void legacyClients();
 
 protected:
     int64_t dataLength;
index 32485707b825be3107b89195dcb112a64af9625e..fddf73cc1ac0883e1ee1e00ea86f2bdfa8c54907 100644 (file)
@@ -77,6 +77,7 @@ void NHttpMsgChunkBody::analyze() {
     if (tcpClose) infractions |= INF_TRUNCATED;
 }
 
+
 void NHttpMsgChunkBody::genEvents() {
     if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
 }
@@ -89,7 +90,7 @@ void NHttpMsgChunkBody::printSection(FILE *output) const {
     NHttpMsgSection::printMessageWrapup(output);
 }
 
-void NHttpMsgChunkBody::updateFlow() const {
+void NHttpMsgChunkBody::updateFlow() {
     if (tcpClose) {
         sessionData->typeExpected[sourceId] = SEC_CLOSED;
         sessionData->halfReset(sourceId);
index 34ffb1d4d78fa8fea2fd971a61dec310cb7b645c..f18e6c2e45b43977a4f84a838ec66ee19f15e16f 100644 (file)
@@ -43,7 +43,7 @@ public:
     void analyze();
     void printSection(FILE *output) const;
     void genEvents();
-    void updateFlow() const;
+    void updateFlow();
 
 private:
     int64_t numChunks;
index 248ec3ea45a1b1f088fb3b8c9f80da655d3fddbc..c15382e8512b0e7f53efa589497903352822c9bb 100644 (file)
@@ -44,7 +44,7 @@ using namespace NHttpEnums;
 // vastly bigger than any legitimate chunk.
 void NHttpMsgChunkHead::deriveChunkLength() {
     if (chunkSize.length <= 0) {
-        dataLength = STAT_PROBLEMATIC;
+        dataLength = STAT_NOSOURCE;
         infractions |= INF_BADCHUNKSIZE;
         return;
     }
@@ -118,7 +118,7 @@ void NHttpMsgChunkHead::printSection(FILE *output) const {
     NHttpMsgSection::printMessageWrapup(output);
 }
 
-void NHttpMsgChunkHead::updateFlow() const {
+void NHttpMsgChunkHead::updateFlow() {
     if (tcpClose) {
         sessionData->typeExpected[sourceId] = SEC_CLOSED;
         sessionData->halfReset(sourceId);
@@ -141,7 +141,7 @@ void NHttpMsgChunkHead::updateFlow() const {
 
 
 // Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgChunkHead::legacyClients() const {
+void NHttpMsgChunkHead::legacyClients() {
     ClearHttpBuffers();
     if (startLine.length > 0) SetHttpBuffer(HTTP_BUFFER_CLIENT_BODY, startLine.start, (unsigned)startLine.length);
 }
index 6c86b08c4de65199777350258aa822fe090e8186..e99ca5e2472cb15d7683b2fb00f5349e81c48fb3 100644 (file)
@@ -43,8 +43,8 @@ public:
     void analyze();
     void printSection(FILE *output) const;
     void genEvents();
-    void updateFlow() const;
-    void legacyClients() const;
+    void updateFlow();
+    void legacyClients();
 
 private:
     void deriveChunkLength();
index 984eda60a40d5716ed722e79f81169a9a97853eb..997464a171f3ffc99fd13f359bb402d41d45cf00 100644 (file)
@@ -48,9 +48,12 @@ void NHttpMsgHeader::printSection(FILE *output) const {
     NHttpMsgSection::printMessageWrapup(output);
 }
 
-void NHttpMsgHeader::updateFlow() const {
+void NHttpMsgHeader::updateFlow() {
     const uint64_t disasterMask = 0;
 
+    ;
+    headerNorms[HEAD_CONTENT_LENGTH]->normalize(HEAD_CONTENT_LENGTH, scratchPad, infractions, headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_CONTENT_LENGTH]);
+
     // The following logic to determine body type is by no means the last word on this topic.
     if (tcpClose) {
         sessionData->typeExpected[sourceId] = SEC_CLOSED;
@@ -66,15 +69,18 @@ void NHttpMsgHeader::updateFlow() const {
         sessionData->halfReset(sourceId);
     }
     // If there is a Transfer-Encoding header, see if the last of the encoded values is "chunked".
-    else if ( (headerValueNorm[HEAD_TRANSFER_ENCODING].length > 0) &&
-         ((*(int64_t *)(headerValueNorm[HEAD_TRANSFER_ENCODING].start + (headerValueNorm[HEAD_TRANSFER_ENCODING].length - 8))) == TRANSCODE_CHUNKED) ) {
+    else if ( (headerNorms[HEAD_TRANSFER_ENCODING]->normalize(HEAD_TRANSFER_ENCODING, scratchPad, infractions,
+                  headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_TRANSFER_ENCODING]) > 0) &&
+            ((*(int64_t *)(headerValueNorm[HEAD_TRANSFER_ENCODING].start + (headerValueNorm[HEAD_TRANSFER_ENCODING].length - 8))) == TRANSCODE_CHUNKED) ) {
         // Chunked body
         sessionData->typeExpected[sourceId] = SEC_CHUNKHEAD;
         sessionData->bodySections[sourceId] = 0;
         sessionData->bodyOctets[sourceId] = 0;
         sessionData->numChunks[sourceId] = 0;
     }
-    else if ((headerValueNorm[HEAD_CONTENT_LENGTH].length > 0) && (*(int64_t*)headerValueNorm[HEAD_CONTENT_LENGTH].start > 0)) {
+    else if ((headerNorms[HEAD_CONTENT_LENGTH]->normalize(HEAD_CONTENT_LENGTH, scratchPad, infractions,
+                 headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_CONTENT_LENGTH]) > 0) &&
+            (*(int64_t*)headerValueNorm[HEAD_CONTENT_LENGTH].start > 0)) {
         // Regular body
         sessionData->typeExpected[sourceId] = SEC_BODY;
         sessionData->octetsExpected[sourceId] = *(int64_t*)headerValueNorm[HEAD_CONTENT_LENGTH].start;
index dbe0eb39cff93ff9ff7d36f5323521cc71dad67e..bb9898c3ed77936bfdeb9a362a0f8e06e2ab8650 100644 (file)
@@ -40,7 +40,7 @@ public:
     NHttpMsgHeader() {};
     void printSection(FILE *output) const;
     void genEvents();
-    void updateFlow() const;
+    void updateFlow();
 };
 
 #endif
index 1453190e947882afd787a02bba74ba8856189c7e..b22a1bdd3a39f27bf08348fe0aae6e9b2a3f993d 100644 (file)
@@ -49,7 +49,7 @@ void NHttpMsgHeadShared::initSection() {
         headerValue[k].length = STAT_NOTCOMPUTE;
     }
     for (int k = 1; k < HEAD__MAXVALUE; k++) {
-        headerValueNorm[k].length = STAT_NOTCOMPUTE;
+        headerValueNorm[k].length = STAT_NOSOURCE;
     }
 }
 
@@ -61,9 +61,14 @@ void NHttpMsgHeadShared::analyze() {
     for (int j=0; j < MAXHEADERS; j++) {
         if (headerName[j].length <= 0) break;
         deriveHeaderNameId(j);
+        // Mark this header field as present and therefore eligible for normalization
+        if (headerNameId[j] > 0) headerValueNorm[headerNameId[j]].length = STAT_NOTCOMPUTE;
     }
+}
+
+void NHttpMsgHeadShared::analyzeAll() {
     for (int k=1; k <= numNorms; k++) {
-        headerNorms[k]->normalize(scratchPad, infractions, (HeaderId)k, headerNameId, headerValue, MAXHEADERS, headerValueNorm[k]);
+        headerNorms[k]->normalize((HeaderId)k, scratchPad, infractions, headerNameId, headerValue, MAXHEADERS, headerValueNorm[k]);
     }
 }
 
@@ -109,18 +114,22 @@ void NHttpMsgHeadShared::parseWhole() {
 
 // Divide up the block of header fields into individual header field lines.
 void NHttpMsgHeadShared::parseHeaderBlock() {
-    if (headers.length <= 0) return;
-    int32_t bytesused = 0;
+    if (headers.length < 0) {
+        numHeaders = STAT_NOSOURCE;
+        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;
+    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) {
+    if (bytesUsed < headers.length) {
         infractions |= INF_TOOMANYHEADERS;
     }
 }
@@ -145,7 +154,6 @@ void NHttpMsgHeadShared::parseHeaderLines() {
 }
 
 void NHttpMsgHeadShared::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) {
@@ -162,7 +170,7 @@ void NHttpMsgHeadShared::genEvents() {
 }
 
 // Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgHeadShared::legacyClients() const {
+void NHttpMsgHeadShared::legacyClients() {
     ClearHttpBuffers();
 
     if (headers.length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_HEADER, headers.start, (unsigned)headers.length);
@@ -175,21 +183,27 @@ void NHttpMsgHeadShared::legacyClients() const {
         }
     }
 
-    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);
+    if (sourceId == SRC_CLIENT) {
+       if (headerNorms[HEAD_COOKIE]->normalize(HEAD_COOKIE, scratchPad, infractions, headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_COOKIE]) > 0) {
+           SetHttpBuffer(HTTP_BUFFER_COOKIE, headerValueNorm[HEAD_COOKIE].start, (unsigned)headerValueNorm[HEAD_COOKIE].length);
+       }
+    }
+    else {
+       if (headerNorms[HEAD_SET_COOKIE]->normalize(HEAD_SET_COOKIE, scratchPad, infractions, headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_SET_COOKIE]) > 0) {
+           SetHttpBuffer(HTTP_BUFFER_COOKIE, headerValueNorm[HEAD_SET_COOKIE].start, (unsigned)headerValueNorm[HEAD_SET_COOKIE].length);
+       }
+    }
 }
 
 void NHttpMsgHeadShared::printHeaders(FILE *output) const {
     char titleBuf[100];
-    if (numHeaders != STAT_NOTCOMPUTE) fprintf(output, "Number of headers: %d\n", numHeaders);
+    if (numHeaders != STAT_NOSOURCE) fprintf(output, "Number of headers: %d\n", numHeaders);
     for (int j=0; j < numHeaders && j < 200; j++) {
         snprintf(titleBuf, sizeof(titleBuf), "Header ID %d", headerNameId[j]);
         printInterval(output, titleBuf, headerValue[j].start, headerValue[j].length);
     }
     for (int k=1; k <= numNorms; k++) {
-        if (headerValueNorm[k].length != STAT_NOTPRESENT) {
+        if (headerValueNorm[k].length != STAT_NOSOURCE) {
             snprintf(titleBuf, sizeof(titleBuf), "Normalized header %d", k);
             printInterval(output, titleBuf, headerValueNorm[k].start, headerValueNorm[k].length, true);
         }
index 67c4f5b8f99444f39ce5284e7bf42be30c81466d..155015e82e91ef41a2e20ee4de7ecff16bf87936 100644 (file)
@@ -41,8 +41,9 @@ class NHttpMsgHeadShared: public NHttpMsgSection {
 public:
     void initSection();
     void analyze();
+    void analyzeAll();
     void genEvents();
-    void legacyClients() const;
+    void legacyClients();
 
 protected:
     // Header normalization. There should be one of these for every different way we can process a header field value.
index dcb8bc482804e6cca822657c569b225ab3b0c9b1..df05d1093b046aed8298421adf005d5a1c653bfd 100644 (file)
 
 using namespace NHttpEnums;
 
-const UriNormalizer NHttpMsgRequest::uriNoPath { false };
-const UriNormalizer NHttpMsgRequest::uriPath { true };
-
 // Reinitialize everything derived in preparation for analyzing a new message
 void NHttpMsgRequest::initSection() {
     NHttpMsgStart::initSection();
     method.length = STAT_NOTCOMPUTE;
-    uri.length = STAT_NOTCOMPUTE;
-    uriLegacyNorm.length = STAT_NOTCOMPUTE;
-    uriType = URI__NOTCOMPUTE;
-    scheme.length = STAT_NOTCOMPUTE;
-    schemeId = SCH__NOTCOMPUTE;
-    host.length = STAT_NOTCOMPUTE;
-    hostInfractions = 0;
-    hostNorm.length = STAT_NOTCOMPUTE;
-    port.length = STAT_NOTCOMPUTE;
-    portValue = STAT_NOTCOMPUTE;
-    path.length = STAT_NOTCOMPUTE;
-    pathInfractions = 0;
-    pathNorm.length = STAT_NOTCOMPUTE;
-    query.length = STAT_NOTCOMPUTE;
-    queryInfractions = 0;
-    queryNorm.length = STAT_NOTCOMPUTE;
-    fragment.length = STAT_NOTCOMPUTE;
-    fragmentInfractions = 0;
-    fragmentNorm.length = STAT_NOTCOMPUTE;
-}
-
-// All the processing that is done for every message (i.e. not just-in-time) is done here.
-void NHttpMsgRequest::analyze() {
-    NHttpMsgStart::analyze();
-    deriveMethodId();
-    parseUri();
-    deriveSchemeId();
-    parseAuthority();
-    derivePortValue();
-    parseAbsPath();
-    uriPath.normalize(path, pathNorm, scratchPad, pathInfractions);
-    uriNoPath.normalize(host, hostNorm, scratchPad, hostInfractions);
-    uriNoPath.normalize(query, queryNorm, scratchPad, queryInfractions);
-    uriNoPath.normalize(fragment, fragmentNorm, scratchPad, fragmentInfractions);
-    makeLegacyNormUri();
+    delete uri;
+    uri = nullptr;
 }
 
 void NHttpMsgRequest::parseStartLine() {
@@ -105,137 +69,19 @@ void NHttpMsgRequest::parseStartLine() {
 
     method.start = startLine.start;
     method.length = space;
-    uri.start = startLine.start + method.length + 1;
-    uri.length = startLine.length - method.length - 10;
+    deriveMethodId();
+    uri = new NHttpUri(startLine.start + method.length + 1, startLine.length - method.length - 10, methodId);
     version.start = startLine.start + (startLine.length - 8);
     version.length = 8;
-    assert (startLine.length == method.length + uri.length + version.length + 2);
+    assert (startLine.length == method.length + uri->getUri().length + version.length + 2);
 }
 
 void NHttpMsgRequest::deriveMethodId() {
-    methodId = (MethodId) strToCode(method.start, method.length, methodList);
-}
-
-void NHttpMsgRequest::parseUri() {
-    if (uriType != URI__NOTCOMPUTE) return;
-    if (uri.length <= 0) {
-        uriType = URI__NOTPRESENT;
+    if (method.length <= 0) {
+        methodId = METH__NOSOURCE;
         return;
     }
-
-    // Four basic types of HTTP URI
-    // "*" means request does not apply to any specific resource
-    if ((uri.length == 1) && (uri.start[0] == '*')) {
-        uriType = URI_ASTERISK;
-        scheme.length = STAT_NOTPRESENT;
-        authority.length = STAT_NOTPRESENT;
-        absPath.length = STAT_NOTPRESENT;
-    }
-    // CONNECT method uses an authority
-    else if (methodId == METH_CONNECT) {
-        uriType = URI_AUTHORITY;
-        scheme.length = STAT_NOTPRESENT;
-        authority.length = uri.length;
-        authority.start = uri.start;
-        absPath.length = STAT_NOTPRESENT;
-    }
-    // Absolute path is a path but no scheme or authority
-    else if (uri.start[0] == '/') {
-        uriType = URI_ABSPATH;
-        scheme.length = STAT_NOTPRESENT;
-        authority.length = STAT_NOTPRESENT;
-        absPath.length = uri.length;
-        absPath.start = uri.start;
-    }
-    // Absolute URI includes scheme, authority, and path
-    else {
-        // Find the "://" and then the "/"
-        int j;
-        int k;
-        for (j = 0; (uri.start[j] != ':') && (j < uri.length); j++);
-        for (k = j+3; (uri.start[k] != '/') && (k < uri.length); k++);
-        if ((k < uri.length) && (uri.start[j+1] == '/') && (uri.start[j+2] == '/')) {
-            uriType = URI_ABSOLUTE;
-            scheme.length = j;
-            scheme.start = uri.start;
-            authority.length = k - j - 3;
-            authority.start = uri.start + j + 3;
-            absPath.length = uri.length - k;
-            absPath.start = uri.start + k;
-        }
-        else {
-            infractions |= INF_BADURI;
-            uriType = URI__PROBLEMATIC;
-            scheme.length = STAT_PROBLEMATIC;
-            authority.length = STAT_PROBLEMATIC;
-            absPath.length = STAT_PROBLEMATIC;
-        }
-    }
-}
-
-void NHttpMsgRequest::deriveSchemeId() {
-    if (schemeId != SCH__NOTCOMPUTE) return;
-    if (scheme.length <= 0) return;
-
-    // Normalize scheme name to lower case for matching purposes
-    uint8_t *lowerScheme;
-    if ((lowerScheme = scratchPad.request(scheme.length)) == nullptr) {
-        infractions |= INF_NOSCRATCH;
-        schemeId = SCH__INSUFMEMORY;
-        return;
-    }
-    norm2Lower(scheme.start, scheme.length, lowerScheme, infractions, nullptr);
-    schemeId = (SchemeId) strToCode(lowerScheme, scheme.length, schemeList);
-}
-
-void NHttpMsgRequest::parseAuthority() {
-    if (host.length != STAT_NOTCOMPUTE) return;
-    if (authority.length <= 0) return;
-    host.start = authority.start;
-    for (host.length = 0; (authority.start[host.length] != ':') && (host.length < authority.length); host.length++);
-    if (host.length < authority.length) {
-        port.length = authority.length - host.length - 1;
-        port.start = authority.start + host.length + 1;
-    }
-    else port.length = STAT_NOTPRESENT;
-}
-
-void NHttpMsgRequest::derivePortValue() {
-    if (portValue != SCH__NOTCOMPUTE) return;
-    if (port.length <= 0) return;
-    portValue = 0;
-    for (int k = 0; k < port.length; k++) {
-        portValue = portValue * 10 + (port.start[k] - '0');
-        if ((port.start[k] < '0') || (port.start[k] > '9') || (portValue > 65535))
-        {
-            infractions |= INF_BADPORT;
-            portValue = STAT_PROBLEMATIC;
-            break;
-        }
-    }
-}
-
-void NHttpMsgRequest::parseAbsPath() {
-    if (path.length != STAT_NOTCOMPUTE) return;
-    if (absPath.length <= 0) return;
-    path.start = absPath.start;
-    for (path.length = 0; (absPath.start[path.length] != '?') && (absPath.start[path.length] != '#') && (path.length < absPath.length); path.length++);
-    if (path.length == absPath.length) {
-        query.length = STAT_NOTPRESENT;
-        fragment.length = STAT_NOTPRESENT;
-        return;
-    }
-    if (absPath.start[path.length] == '?') {
-        query.start = absPath.start + path.length + 1;
-        for (query.length = 0; (query.start[query.length] != '#') && (query.length < absPath.length - path.length - 1); query.length++);
-        fragment.start = query.start + query.length + 1;
-        fragment.length = absPath.length - path.length - 1 - query.length - 1;
-    }
-    else {
-        query.length = STAT_NOTPRESENT;
-        fragment.start = absPath.start + path.length + 1;
-        fragment.length = absPath.length - path.length - 1;
-    }
+    methodId = (MethodId) strToCode(method.start, method.length, methodList);
 }
 
 void NHttpMsgRequest::genEvents() {
@@ -244,30 +90,31 @@ void NHttpMsgRequest::genEvents() {
 
 void NHttpMsgRequest::printSection(FILE *output) const {
     NHttpMsgSection::printMessageTitle(output, "request line");
-    if (versionId != VERS__NOTCOMPUTE) fprintf(output, "Version Id: %d\n", versionId);
-    if (methodId != METH__NOTCOMPUTE) fprintf(output, "Method Id: %d\n", methodId);
-    printInterval(output, "URI", uri.start, uri.length);
-    if (uriType != URI__NOTCOMPUTE) fprintf(output, "URI Type: %d\n", uriType);
-    printInterval(output, "Scheme", scheme.start, scheme.length);
-    if (schemeId != SCH__NOTCOMPUTE) fprintf(output, "Scheme Id: %d\n", schemeId);
-    printInterval(output, "Authority", authority.start, authority.length);
-    printInterval(output, "Host Name", host.start, host.length);
-    printInterval(output, "Normalized Host Name", hostNorm.start, hostNorm.length);
-    printInterval(output, "Port", port.start, port.length);
-    if (portValue != STAT_NOTCOMPUTE) fprintf(output, "Port Value: %d\n", portValue);
-    printInterval(output, "Absolute Path", absPath.start, absPath.length);
-    printInterval(output, "Path", path.start, path.length);
-    printInterval(output, "Normalized Path", pathNorm.start, pathNorm.length);
-    printInterval(output, "Query", query.start, query.length);
-    printInterval(output, "Normalized Query", queryNorm.start, queryNorm.length);
-    printInterval(output, "Fragment", fragment.start, fragment.length);
-    printInterval(output, "Normalized Fragment", fragmentNorm.start, fragmentNorm.length);
-    fprintf(output, "URI infractions: host %" PRIx64 ", path %" PRIx64 ", query %" PRIx64 ", fragment %" PRIx64 "\n",
-       hostInfractions, pathInfractions, queryInfractions, fragmentInfractions);
+    fprintf(output, "Version Id: %d\n", versionId);
+    fprintf(output, "Method Id: %d\n", methodId);
+    printInterval(output, "URI", uri->getUri().start, uri->getUri().length);
+    if ((uri->getUriType() != URI__NOTCOMPUTE) && (uri->getUriType() != URI__NOSOURCE)) fprintf(output, "URI Type: %d\n", uri->getUriType());
+    printInterval(output, "Scheme", uri->getScheme().start, uri->getScheme().length);
+    if ((uri->getSchemeId() != SCH__NOTCOMPUTE) && (uri->getSchemeId() != SCH__NOSOURCE)) fprintf(output, "Scheme Id: %d\n", uri->getSchemeId());
+    printInterval(output, "Authority", uri->getAuthority().start, uri->getAuthority().length);
+    printInterval(output, "Host Name", uri->getHost().start, uri->getHost().length);
+    printInterval(output, "Normalized Host Name", uri->getNormHost().start, uri->getNormHost().length);
+    printInterval(output, "Port", uri->getPort().start, uri->getPort().length);
+    if ((uri->getPortValue() != STAT_NOTCOMPUTE) && (uri->getPortValue() != STAT_NOSOURCE)) fprintf(output, "Port Value: %d\n", uri->getPortValue());
+    printInterval(output, "Absolute Path", uri->getAbsPath().start, uri->getAbsPath().length);
+    printInterval(output, "Path", uri->getPath().start, uri->getPath().length);
+    printInterval(output, "Normalized Path", uri->getNormPath().start, uri->getNormPath().length);
+    printInterval(output, "Query", uri->getQuery().start, uri->getQuery().length);
+    printInterval(output, "Normalized Query", uri->getNormQuery().start, uri->getNormQuery().length);
+    printInterval(output, "Fragment", uri->getFragment().start, uri->getFragment().length);
+    printInterval(output, "Normalized Fragment", uri->getNormFragment().start, uri->getNormFragment().length);
+    fprintf(output, "URI infractions: overall %" PRIx64 ", host %" PRIx64 ", path %" PRIx64 ", query %" PRIx64 ", fragment %" PRIx64 "\n",
+       uri->getUriInfractions(), uri->getHostInfractions(), uri->getPathInfractions(), uri->getQueryInfractions(),
+       uri->getFragmentInfractions());
     NHttpMsgSection::printMessageWrapup(output);
-}
+ }
 
-void NHttpMsgRequest::updateFlow() const {
+void NHttpMsgRequest::updateFlow() {
     const uint64_t disasterMask = INF_BADREQLINE;
 
     // The following logic to determine body type is by no means the last word on this topic.
@@ -283,77 +130,15 @@ void NHttpMsgRequest::updateFlow() const {
         sessionData->typeExpected[sourceId] = SEC_HEADER;
         sessionData->versionId[sourceId] = versionId;
         sessionData->methodId[sourceId] = methodId;
-        sessionData->schemeId[sourceId] = schemeId;
-    }
-}
-
-// Glue normalized URI fields back together 
-void NHttpMsgRequest::makeLegacyNormUri() {
-    if (uriLegacyNorm.length != STAT_NOTCOMPUTE) return;
-
-    // We can reuse the raw URI for the normalized URI unless at least one part of the URI has been normalized
-    if ((hostInfractions == 0) && (pathInfractions == 0) && (queryInfractions == 0) && (fragmentInfractions == 0)) {
-        uriLegacyNorm.start = uri.start;
-        uriLegacyNorm.length = uri.length;
-        return;
-    }
-
-    // Glue normalized path pieces back together
-    const uint32_t totalLength = ((scheme.length >= 0) ? scheme.length + 3 : 0) +
-                                 ((hostNorm.length >= 0) ? hostNorm.length : 0) +
-                                 ((port.length >= 0) ? port.length + 1 : 0) +
-                                 ((pathNorm.length >= 0) ? pathNorm.length : 0) +
-                                 ((queryNorm.length >= 0) ? queryNorm.length + 1 : 0) +
-                                 ((fragmentNorm.length >= 0) ? fragmentNorm.length + 1 : 0);
-    uint8_t* const scratch = scratchPad.request(totalLength);
-    if (scratch != nullptr) {
-        uint8_t *current = scratch;
-        if (scheme.length >= 0) {
-            memcpy(current, scheme.start, scheme.length);
-            current += scheme.length;
-            memcpy(current, "://", 3);
-            current += 3;
-        }
-        if (hostNorm.length >= 0) {
-            memcpy(current, hostNorm.start, hostNorm.length);
-            current += hostNorm.length;
-        }
-        if (port.length >= 0) {
-            memcpy(current, ":", 1);
-            current += 1;
-            memcpy(current, port.start, port.length);
-            current += port.length;
-        }
-        if (pathNorm.length >= 0) {
-            memcpy(current, pathNorm.start, pathNorm.length);
-            current += pathNorm.length;
-        }
-        if (queryNorm.length >= 0) {
-            memcpy(current, "?", 1);
-            current += 1;
-            memcpy(current, queryNorm.start, queryNorm.length);
-            current += queryNorm.length;
-        }
-        if (fragmentNorm.length >= 0) {
-            memcpy(current, "#", 1);
-            current += 1;
-            memcpy(current, fragmentNorm.start, fragmentNorm.length);
-            current += fragmentNorm.length;
-        }
-        assert(totalLength == current - scratch);
-        scratchPad.commit(current - scratch);
-        uriLegacyNorm.start = scratch;
-        uriLegacyNorm.length = current - scratch;
     }
-    else uriLegacyNorm.length = STAT_INSUFMEMORY;
 }
 
 // Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgRequest::legacyClients() const {
+void NHttpMsgRequest::legacyClients() {
     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 (uriLegacyNorm.length > 0) SetHttpBuffer(HTTP_BUFFER_URI, uriLegacyNorm.start, (unsigned)uriLegacyNorm.length);
+    if (uri->getUri().length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_URI, uri->getUri().start, (unsigned)uri->getUri().length);
+    if (uri->getNormLegacy().length > 0) SetHttpBuffer(HTTP_BUFFER_URI, uri->getNormLegacy().start, (unsigned)uri->getNormLegacy().length);
 }
 
 
index 409ce2d19bf2d60760882b964b87451db1d34230..db39686220592a4a0cf8689b09bbf1455fe08449 100644 (file)
@@ -30,6 +30,7 @@
 #define NHTTP_MSG_REQUEST_H
 
 #include "nhttp_str_to_code.h"
+#include "nhttp_uri.h"
 #include "nhttp_uri_norm.h"
 #include "nhttp_msg_start.h"
 
 class NHttpMsgRequest: public NHttpMsgStart {
 public:
     NHttpMsgRequest() {};
+    ~NHttpMsgRequest() { delete uri; };
     void initSection();
-    void analyze();
     void printSection(FILE *output) const;
     void genEvents();
-    void updateFlow() const;
-    void legacyClients() const;
+    void updateFlow();
+    void legacyClients();
 
 private:
     // Code conversion tables are for turning token strings into enums.
     static const StrCode methodList[];
-    static const StrCode schemeList[];
 
-    // URI normalization strategy objects
-    static const UriNormalizer uriNoPath;
-    static const UriNormalizer uriPath;
-
-    // "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.
+    // "Parse" methods cut things into pieces. "Extract" methods find the named item. "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 parseStartLine();
     void deriveMethodId();
-    void parseUri();
-    void deriveSchemeId();
-    void parseAuthority();
-    void derivePortValue();
-    void parseAbsPath();
-    void makeLegacyNormUri();
 
     // This is where all the derived values, extracted message parts, and normalized values are.
     // Note that these are all scalars, buffer pointers, and buffer sizes. The actual buffers are in the message buffer (raw pieces) or the
     // scratchPad (normalized pieces).
     field method;
-
-    // URI stuff
-    field uri;
-    field scheme;
-    field authority;
-    field host;
-    field port;
-    field absPath;
-    field path;
-    field query;
-    field fragment;
-
-    uint64_t hostInfractions;
-    uint64_t pathInfractions;
-    uint64_t queryInfractions;
-    uint64_t fragmentInfractions;
-
-    NHttpEnums::UriType uriType;
-    field hostNorm;
-    int32_t portValue;
-    field pathNorm;
-    field queryNorm;
-    field fragmentNorm;
-    field uriLegacyNorm;
+    NHttpUri* uri = nullptr;
 };
 
 #endif
index 6945c0df6ded1d08497c680d76f3ef3883ea6d1b..7071aa6906e095da4eb8549b26e37a877ab3e2d7 100644 (file)
@@ -60,14 +60,13 @@ void NHttpMsgSection::loadSection(const uint8_t *buffer, const uint16_t bufsize,
     tcpClose = sessionData->tcpClose;
     versionId = sessionData->versionId[sourceId];
     methodId = sessionData->methodId[sourceId];
-    schemeId = sessionData->schemeId[sourceId];
     statusCodeNum = sessionData->statusCodeNum[sourceId];
 
     scratchPad.reinit();
 }
 
 void NHttpMsgSection::printInterval(FILE *output, const char* name, const uint8_t *text, int32_t length, bool intVals) {
-    if ((length == STAT_NOTPRESENT) || (length == STAT_NOTCOMPUTE)) return;
+    if ((length == STAT_NOTPRESENT) || (length == STAT_NOTCOMPUTE) || (length == STAT_NOSOURCE)) return;
     int outCount = fprintf(output, "%s, length = %d, ", name, length);
     if (length <= 0) {
         fprintf(output, "\n");
index c36c5004609bbfc9218aa1e1de606c5212a2e964..19c37825511ad95fbea3719f588bcb6dcd490472 100644 (file)
@@ -42,11 +42,12 @@ public:
     virtual void loadSection(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_);
     virtual ~NHttpMsgSection() = default;
     virtual void initSection() = 0;
-    virtual void analyze() = 0;
+    virtual void analyze() = 0;                           // Minimum necessary processing for every message
+    virtual void analyzeAll() {};                         // Force all just-in-time processing (testing method)
     virtual void printSection(FILE *output) const = 0;
     virtual void genEvents() = 0;
-    virtual void updateFlow() const = 0;
-    virtual void legacyClients() const = 0;
+    virtual void updateFlow() = 0;
+    virtual void legacyClients() = 0;
 
 protected:
     // Convenience methods
@@ -63,10 +64,8 @@ protected:
     // 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.
-    uint64_t derivedBuf[NHttpEnums::MAXOCTETS/4];
     NHttpFlowData* sessionData;
-    ScratchPad scratchPad {derivedBuf, NHttpEnums::MAXOCTETS/4};
+    ScratchPad scratchPad {NHttpEnums::MAXOCTETS*2};
 
     // This is where all the derived values, extracted message parts, and normalized values are.
     // Note that these are all scalars, buffer pointers, and buffer sizes. The actual buffers are in message buffer (raw pieces) or the
@@ -76,7 +75,6 @@ protected:
     NHttpEnums::SourceId sourceId;
     NHttpEnums::VersionId versionId;
     NHttpEnums::MethodId methodId;
-    NHttpEnums::SchemeId schemeId;
     int32_t statusCodeNum;
 };
 
index 2a9f7ca0fd0081ecb03155f72db51e7e7f0c3abf..cdcdf445a804ba92aaf205982f4f8174f76f4cac 100644 (file)
@@ -44,7 +44,7 @@ void NHttpMsgStart::initSection() {
     version.length = STAT_NOTCOMPUTE;
 }
 
-// All the processing that is done for every message (i.e. not just-in-time) is done here.
+// Required message processing that is automatically done instead of being just-in-time
 void NHttpMsgStart::analyze() {
     startLine.start = msgText;
     startLine.length = findCrlf(startLine.start, length, false);
@@ -55,10 +55,16 @@ void NHttpMsgStart::analyze() {
 }
 
 void NHttpMsgStart::deriveVersionId() {
+    if (version.length <= 0) {
+        versionId = VERS__NOSOURCE;
+        return;
+    }
     if (version.length != 8) {
         versionId = VERS__PROBLEMATIC;
+        infractions |= INF_BADVERSION;
         return;
     }
+
     if (memcmp(version.start, "HTTP/", 5) || (version.start[6] != '.')) {
         versionId = VERS__PROBLEMATIC;
         infractions |= INF_BADVERSION;
index d3446458cfba0ec9410259cdd787b6bfaecd83a1..48f3368e08dc360f1c96eb6e7a605785457ad9d6 100644 (file)
@@ -75,10 +75,15 @@ void NHttpMsgStatus::parseStartLine() {
 }
 
 void NHttpMsgStatus::deriveStatusCodeNum() {
+    if (statusCode.length <= 0) {
+        statusCodeNum = STAT_NOSOURCE;
+        return;
+    }
     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;
@@ -97,13 +102,13 @@ void NHttpMsgStatus::genEvents() {
 
 void NHttpMsgStatus::printSection(FILE *output) const {
     NHttpMsgSection::printMessageTitle(output, "status line");
-    if (versionId != VERS__NOTCOMPUTE) fprintf(output, "Version Id: %d\n", versionId);
-    if (statusCodeNum != STAT_NOTCOMPUTE) fprintf(output, "Status Code Num: %d\n", statusCodeNum);
+    fprintf(output, "Version Id: %d\n", versionId);
+    fprintf(output, "Status Code Num: %d\n", statusCodeNum);
     printInterval(output, "Reason Phrase", reasonPhrase.start, reasonPhrase.length);
     NHttpMsgSection::printMessageWrapup(output);
 }
 
-void NHttpMsgStatus::updateFlow() const {
+void NHttpMsgStatus::updateFlow() {
     const uint64_t disasterMask = INF_BADSTATLINE;
 
     // The following logic to determine body type is by no means the last word on this topic.
@@ -123,7 +128,7 @@ void NHttpMsgStatus::updateFlow() const {
 }
 
 // Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgStatus::legacyClients() const {
+void NHttpMsgStatus::legacyClients() {
     ClearHttpBuffers();
     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);
index f2a968f962124c9c5f2501e51ce0a16ead046942..597332d725703aab9cf00f448576e14de59a3d5f 100644 (file)
@@ -42,8 +42,8 @@ public:
     void analyze();
     void printSection(FILE *output) const;
     void genEvents();
-    void updateFlow() const;
-    void legacyClients() const;
+    void updateFlow();
+    void legacyClients();
 
 private:
     // "Parse" methods cut things into pieces. "Derive" methods convert things into a new format such as an integer or enum token. "Normalize" methods convert
index 4ac75f8df66049d6705ed90b4714750073492a0d..049451a64937b6348eb4b81bfa238f6e3121d93b 100644 (file)
@@ -48,7 +48,7 @@ void NHttpMsgTrailer::printSection(FILE *output) const {
 }
 
 
-void NHttpMsgTrailer::updateFlow() const {
+void NHttpMsgTrailer::updateFlow() {
     if (tcpClose) {
         sessionData->typeExpected[sourceId] = SEC_CLOSED;
         sessionData->halfReset(sourceId);
index 3d02059e611c4f8f6d8d44d2e196ae261c13b274..6c5101dd3433c30edef2644fb8bddf9de136e560 100644 (file)
@@ -40,7 +40,7 @@ public:
     NHttpMsgTrailer() {};
     void printSection(FILE *output) const;
     void genEvents();
-    void updateFlow() const;
+    void updateFlow();
 };
 
 #endif
index 76e324321e238680734d6fac209c0b74215073ac..4ad6cb074c4793dccc9d3fa5911accdc512ed045 100644 (file)
 
 class ScratchPad {
 public:
-    ScratchPad(uint64_t *buff, uint32_t length) : buffer(buff), capacity(length*8), used(0) {}; // Careful: length must be number of uint64_ts provided, not octets.
+    ScratchPad(uint32_t _capacity) : capacity(_capacity), buffer(new uint64_t[_capacity/8+1]) {};
+    ~ScratchPad() { delete[] buffer; };
     void reinit() {used = 0;};
     uint8_t *request(uint32_t needed) const {return (needed <= capacity-used) ? (uint8_t*)(buffer+used) : nullptr;};
     void commit(uint32_t taken) { used += taken + (8-(taken%8))%8; };  // round up to multiple of 8 to preserve alignment
 
 private:
-    uint64_t *buffer;
     uint32_t capacity;
-    uint32_t used;
+    uint64_t *buffer;
+    uint32_t used = 0;
 };
 
 #endif
index c8c33373f1102c40a1a6bf300a24a65eecb82852..c654c655c3455aaf880d20c5e73d11359afb0a74 100644 (file)
@@ -36,7 +36,6 @@
 
 // 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;
index 0af32ee4c07c345d7650c5ba387e7243778e2f00..fdad4c2d58f8001044f1725822a8766847600278 100644 (file)
@@ -97,7 +97,7 @@ const StrCode NHttpMsgRequest::methodList[] =
     { METH_UPDATEREDIRECTREF,  "UPDATEREDIRECTREF"},
     { 0,                       nullptr} };
 
-const StrCode NHttpMsgRequest::schemeList[] =
+const StrCode NHttpUri::schemeList[] =
    {{ SCH_HTTP,                "http"},
     { SCH_HTTPS,               "https"},
     { SCH_FTP,                 "ftp"},
@@ -155,6 +155,8 @@ const StrCode NHttpMsgHeadShared::headerList[] =
     { HEAD_CONTENT_TYPE,         "content-type"},
     { HEAD_EXPIRES,              "expires"},
     { HEAD_LAST_MODIFIED,        "last-modified"},
+    { HEAD_X_FORWARDED_FOR,      "x-forwarded-for"},
+    { HEAD_TRUE_CLIENT_IP,       "true-client-ip"},
     { 0,                         nullptr} };
 
 const StrCode NHttpMsgHeadShared::transCodeList[] =
@@ -222,7 +224,9 @@ const HeaderNormalizer* const NHttpMsgHeadShared::headerNorms[HEAD__MAXVALUE] =
     [HEAD_CONTENT_RANGE] = &NORMALIZER_BASIC,
     [HEAD_CONTENT_TYPE] = &NORMALIZER_BASIC,
     [HEAD_EXPIRES] = &NORMALIZER_BASIC,
-    [HEAD_LAST_MODIFIED] = &NORMALIZER_BASIC
+    [HEAD_LAST_MODIFIED] = &NORMALIZER_BASIC,
+    [HEAD_X_FORWARDED_FOR] = &NORMALIZER_CAT,
+    [HEAD_TRUE_CLIENT_IP] = &NORMALIZER_BASIC
 };
 
 const int32_t NHttpMsgHeadShared::numNorms = HEAD__MAXVALUE-1;
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri.cc b/src/service_inspectors/nhttp_inspect/nhttp_uri.cc
new file mode 100644 (file)
index 0000000..a6b260d
--- /dev/null
@@ -0,0 +1,291 @@
+/****************************************************************************
+ *
+** 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      NHttpUri class
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "snort.h"
+#include "nhttp_enum.h"
+#include "nhttp_normalizers.h"
+#include "nhttp_uri.h"
+
+using namespace NHttpEnums;
+
+void NHttpUri::parseUri() {
+    if (uriType != URI__NOTCOMPUTE) return;
+    if (uri.length <= 0) {
+        uriType = URI__NOSOURCE;
+        scheme.length = STAT_NOSOURCE;
+        authority.length = STAT_NOSOURCE;
+        absPath.length = STAT_NOSOURCE;
+        return;
+    }
+
+    // Four basic types of HTTP URI
+    // "*" means request does not apply to any specific resource
+    if ((uri.length == 1) && (uri.start[0] == '*')) {
+        uriType = URI_ASTERISK;
+        scheme.length = STAT_NOTPRESENT;
+        authority.length = STAT_NOTPRESENT;
+        absPath.length = STAT_NOTPRESENT;
+    }
+    // CONNECT method uses an authority
+    else if (methodId == METH_CONNECT) {
+        uriType = URI_AUTHORITY;
+        scheme.length = STAT_NOTPRESENT;
+        authority.length = uri.length;
+        authority.start = uri.start;
+        absPath.length = STAT_NOTPRESENT;
+    }
+    // Absolute path is a path but no scheme or authority
+    else if (uri.start[0] == '/') {
+        uriType = URI_ABSPATH;
+        scheme.length = STAT_NOTPRESENT;
+        authority.length = STAT_NOTPRESENT;
+        absPath.length = uri.length;
+        absPath.start = uri.start;
+    }
+    // Absolute URI includes scheme, authority, and path
+    else {
+        // Find the "://" and then the "/"
+        int j;
+        int k;
+        for (j = 0; (uri.start[j] != ':') && (j < uri.length); j++);
+        for (k = j+3; (uri.start[k] != '/') && (k < uri.length); k++);
+        if ((k < uri.length) && (uri.start[j+1] == '/') && (uri.start[j+2] == '/')) {
+            uriType = URI_ABSOLUTE;
+            scheme.length = j;
+            scheme.start = uri.start;
+            authority.length = k - j - 3;
+            authority.start = uri.start + j + 3;
+            absPath.length = uri.length - k;
+            absPath.start = uri.start + k;
+        }
+        else {
+            uriInfractions |= INF_BADURI;
+            uriType = URI__PROBLEMATIC;
+            scheme.length = STAT_PROBLEMATIC;
+            authority.length = STAT_PROBLEMATIC;
+            absPath.length = STAT_PROBLEMATIC;
+        }
+    }
+}
+
+SchemeId NHttpUri::getSchemeId() {
+    if (schemeId != SCH__NOTCOMPUTE) return schemeId;
+    if (getScheme().length <= 0) {
+        schemeId = SCH__NOSOURCE;
+        return schemeId;
+    }
+
+    // Normalize scheme name to lower case for matching purposes
+    uint8_t *lowerScheme;
+    if ((lowerScheme = scratchPad.request(scheme.length)) == nullptr) {
+        uriInfractions |= INF_NOSCRATCH;
+        schemeId = SCH__INSUFMEMORY;
+        return schemeId;
+    }
+    norm2Lower(scheme.start, scheme.length, lowerScheme, uriInfractions, nullptr);
+    schemeId = (SchemeId) strToCode(lowerScheme, scheme.length, schemeList);
+    return schemeId;
+}
+
+field NHttpUri::getNormHost() {
+    if (hostNorm.length != STAT_NOTCOMPUTE) return hostNorm;
+    if (getHost().length < 0) {
+        hostNorm.length = STAT_NOSOURCE;
+        return hostNorm;
+    }
+    UriNormalizer::normalize(host, hostNorm, false, scratchPad, hostInfractions);
+    return hostNorm;
+}
+
+field NHttpUri::getNormPath() {
+    if (pathNorm.length != STAT_NOTCOMPUTE) return pathNorm;
+    if (getPath().length < 0) {
+        pathNorm.length = STAT_NOSOURCE;
+        return pathNorm;
+    }
+    UriNormalizer::normalize(path, pathNorm, true, scratchPad, pathInfractions);
+    return pathNorm;
+}
+
+field NHttpUri::getNormQuery() {
+    if (queryNorm.length != STAT_NOTCOMPUTE) return queryNorm;
+    if (getQuery().length < 0) {
+        queryNorm.length = STAT_NOSOURCE;
+        return queryNorm;
+    }
+    UriNormalizer::normalize(query, queryNorm, true, scratchPad, queryInfractions);
+    return queryNorm;
+}
+
+field NHttpUri::getNormFragment() {
+    if (fragmentNorm.length != STAT_NOTCOMPUTE) return fragmentNorm;
+    if (getFragment().length < 0) {
+        fragmentNorm.length = STAT_NOSOURCE;
+        return fragmentNorm;
+    }
+    UriNormalizer::normalize(fragment, fragmentNorm, true, scratchPad, fragmentInfractions);
+    return fragmentNorm;
+}
+
+int32_t NHttpUri::getPortValue() {
+    if (portValue != STAT_NOTCOMPUTE) return portValue;
+    if (getPort().length <= 0) {
+        portValue = STAT_NOSOURCE;
+        return portValue;
+    }
+    portValue = 0;
+    for (int k = 0; k < port.length; k++) {
+        portValue = portValue * 10 + (port.start[k] - '0');
+        if ((port.start[k] < '0') || (port.start[k] > '9') || (portValue > 65535))
+        {
+            uriInfractions |= INF_BADPORT;
+            portValue = STAT_PROBLEMATIC;
+            break;
+        }
+    }
+    return portValue;
+}
+
+void NHttpUri::parseAuthority() {
+    if (host.length != STAT_NOTCOMPUTE) return;
+    if (getAuthority().length <= 0) {
+        host.length = STAT_NOSOURCE;
+        port.length = STAT_NOSOURCE;
+        return;
+    }
+    host.start = authority.start;
+    for (host.length = 0; (authority.start[host.length] != ':') && (host.length < authority.length); host.length++);
+    if (host.length < authority.length) {
+        port.length = authority.length - host.length - 1;
+        port.start = authority.start + host.length + 1;
+    }
+    else port.length = STAT_NOTPRESENT;
+}
+
+void NHttpUri::parseAbsPath() {
+    if (path.length != STAT_NOTCOMPUTE) return;
+    if (getAbsPath().length <= 0) {
+        path.length = STAT_NOSOURCE;
+        query.length = STAT_NOSOURCE;
+        fragment.length = STAT_NOSOURCE;
+        return;
+    }
+    path.start = absPath.start;
+    for (path.length = 0; (absPath.start[path.length] != '?') && (absPath.start[path.length] != '#') && (path.length < absPath.length); path.length++);
+    if (path.length == absPath.length) {
+        query.length = STAT_NOTPRESENT;
+        fragment.length = STAT_NOTPRESENT;
+        return;
+    }
+    if (absPath.start[path.length] == '?') {
+        query.start = absPath.start + path.length + 1;
+        for (query.length = 0; (query.start[query.length] != '#') && (query.length < absPath.length - path.length - 1); query.length++);
+        fragment.start = query.start + query.length + 1;
+        fragment.length = absPath.length - path.length - 1 - query.length - 1;
+    }
+    else {
+        query.length = STAT_NOTPRESENT;
+        fragment.start = absPath.start + path.length + 1;
+        fragment.length = absPath.length - path.length - 1;
+    }
+}
+
+// Glue normalized URI fields back together 
+field NHttpUri::getNormLegacy() {
+    if (legacyNorm.length != STAT_NOTCOMPUTE) return legacyNorm;
+
+    if (getPath().length >= 0) UriNormalizer::normalize(path, pathNorm, true, scratchPad, pathInfractions);
+    if (getHost().length >= 0) UriNormalizer::normalize(host, hostNorm, false, scratchPad, hostInfractions);
+    if (getQuery().length >= 0) UriNormalizer::normalize(query, queryNorm, false, scratchPad, queryInfractions);
+    if (getFragment().length >= 0) UriNormalizer::normalize(fragment, fragmentNorm, false, scratchPad, fragmentInfractions);
+
+    // We can reuse the raw URI for the normalized URI unless at least one part of the URI has been normalized
+    if ((hostInfractions == 0) && (pathInfractions == 0) && (queryInfractions == 0) && (fragmentInfractions == 0)) {
+        legacyNorm.start = uri.start;
+        legacyNorm.length = uri.length;
+        return legacyNorm;
+    }
+
+    // Glue normalized URI pieces back together
+    const uint32_t totalLength = ((scheme.length >= 0) ? scheme.length + 3 : 0) +
+                                 ((hostNorm.length >= 0) ? hostNorm.length : 0) +
+                                 ((port.length >= 0) ? port.length + 1 : 0) +
+                                 ((pathNorm.length >= 0) ? pathNorm.length : 0) +
+                                 ((queryNorm.length >= 0) ? queryNorm.length + 1 : 0) +
+                                 ((fragmentNorm.length >= 0) ? fragmentNorm.length + 1 : 0);
+    uint8_t* const scratch = scratchPad.request(totalLength);
+    if (scratch != nullptr) {
+        uint8_t *current = scratch;
+        if (scheme.length >= 0) {
+            memcpy(current, scheme.start, scheme.length);
+            current += scheme.length;
+            memcpy(current, "://", 3);
+            current += 3;
+        }
+        if (hostNorm.length >= 0) {
+            memcpy(current, hostNorm.start, hostNorm.length);
+            current += hostNorm.length;
+        }
+        if (port.length >= 0) {
+            memcpy(current, ":", 1);
+            current += 1;
+            memcpy(current, port.start, port.length);
+            current += port.length;
+        }
+        if (pathNorm.length >= 0) {
+            memcpy(current, pathNorm.start, pathNorm.length);
+            current += pathNorm.length;
+        }
+        if (queryNorm.length >= 0) {
+            memcpy(current, "?", 1);
+            current += 1;
+            memcpy(current, queryNorm.start, queryNorm.length);
+            current += queryNorm.length;
+        }
+        if (fragmentNorm.length >= 0) {
+            memcpy(current, "#", 1);
+            current += 1;
+            memcpy(current, fragmentNorm.start, fragmentNorm.length);
+            current += fragmentNorm.length;
+        }
+        assert(totalLength == current - scratch);
+        scratchPad.commit(current - scratch);
+        legacyNorm.start = scratch;
+        legacyNorm.length = current - scratch;
+    }
+    else legacyNorm.length = STAT_INSUFMEMORY;
+    return legacyNorm;
+}
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri.h b/src/service_inspectors/nhttp_inspect/nhttp_uri.h
new file mode 100644 (file)
index 0000000..181ccd2
--- /dev/null
@@ -0,0 +1,112 @@
+/****************************************************************************
+ *
+** 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      NHttpUri class declaration
+//
+
+#ifndef NHTTP_URI_H
+#define NHTTP_URI_H
+
+#include "nhttp_scratch_pad.h"
+#include "nhttp_str_to_code.h"
+#include "nhttp_uri_norm.h"
+
+//-------------------------------------------------------------------------
+// NHttpUri class
+//-------------------------------------------------------------------------
+
+class NHttpUri {
+public:
+    NHttpUri(const uint8_t* start, int32_t length, NHttpEnums::MethodId method) : methodId(method) {
+       uri.length = length; uri.start = start; };
+
+    field getUri() const { return uri; };
+    NHttpEnums::UriType getUriType() { parseUri(); return uriType; };
+    field getScheme() { parseUri(); return scheme; };
+    field getAuthority() { parseUri(); return authority; };
+    field getHost() { parseAuthority(); return host; };
+    field getPort() { parseAuthority(); return port; };
+    field getAbsPath() { parseUri(); return absPath; };
+    field getPath() { parseAbsPath(); return path; };
+    field getQuery() { parseAbsPath(); return query; };
+    field getFragment() { parseAbsPath(); return fragment; };
+
+    uint64_t getUriInfractions() const { return uriInfractions; };
+    uint64_t getHostInfractions() const { return hostInfractions; };
+    uint64_t getPathInfractions() const { return pathInfractions; };
+    uint64_t getQueryInfractions() const { return queryInfractions; };
+    uint64_t getFragmentInfractions() const { return fragmentInfractions; };
+
+    NHttpEnums::SchemeId getSchemeId();
+    field getNormHost();
+    int32_t getPortValue();
+    field getNormPath();
+    field getNormQuery();
+    field getNormFragment();
+    field getNormLegacy();
+
+private:
+    static const StrCode schemeList[];
+
+    field uri;
+    const NHttpEnums::MethodId methodId;
+
+    field scheme;
+    field authority;
+    field host;
+    field port;
+    field absPath;
+    field path;
+    field query;
+    field fragment;
+
+    uint64_t uriInfractions = 0;
+    uint64_t hostInfractions = 0;
+    uint64_t pathInfractions = 0;
+    uint64_t queryInfractions = 0;
+    uint64_t fragmentInfractions = 0;
+
+    NHttpEnums::UriType uriType = NHttpEnums::URI__NOTCOMPUTE;
+    NHttpEnums::SchemeId schemeId = NHttpEnums::SCH__NOTCOMPUTE;
+    field hostNorm;
+    int32_t portValue = NHttpEnums::STAT_NOTCOMPUTE;
+    field pathNorm;
+    field queryNorm;
+    field fragmentNorm;
+    field legacyNorm;
+
+    void parseUri();
+    void parseAuthority();
+    void parseAbsPath();
+
+    ScratchPad scratchPad {NHttpEnums::MAXOCTETS*2};
+};
+
+#endif
+
+
+
+
+
index 3e46622bcb2e155e50a8e51693bccd9f501f3d65..f94cd08bcd696b4b68c3e2a7845fc0da3be67a83 100644 (file)
 //
 //  @author     Tom Peters <thopeter@cisco.com>
 //
-//  @brief      URI normalization functions
+//  @brief      URI normalization class
 //
 
 
-// &&&#include <string.h>
 #include <assert.h>
 #include <sys/types.h>
 
 
 using namespace NHttpEnums;
 
-void UriNormalizer::normalize(const field &input, field &result, ScratchPad &scratchPad, uint64_t &infractions) const {
+void UriNormalizer::normalize(const field &input, field &result, bool doPath, ScratchPad &scratchPad, uint64_t &infractions) {
     if (result.length != STAT_NOTCOMPUTE) return;
-    if (input.length < 0) {
-        result.length = STAT_NOTPRESENT;
-        return;
-    }
+    assert (input.length >= 0);
 
     // Almost all HTTP requests are honest and rarely need expensive normalization processing. We do a quick scan for
     // red flags and only perform normalization if something comes up. Otherwise we set the normalized field to point
@@ -78,7 +74,7 @@ void UriNormalizer::normalize(const field &input, field &result, ScratchPad &scr
     result.length = dataLength;
 }
 
-bool UriNormalizer::noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) const {
+bool UriNormalizer::noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) {
     for (int32_t k = 0; k < inLength; k++) {
         if ((uriChar[inBuf[k]] == CHAR_NORMAL) || (uriChar[inBuf[k]] == CHAR_PATH)) continue;
         infractions |= INF_URINEEDNORM;
@@ -87,7 +83,7 @@ bool UriNormalizer::noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t
     return true;
 }
 
-bool UriNormalizer::pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) const {
+bool UriNormalizer::pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) {
     for (int32_t k = 0; k < inLength; k++) {
         if (uriChar[inBuf[k]] == CHAR_NORMAL) continue;
         if ((inBuf[k] == '/') && ((k == 0) || (inBuf[k-1] != '/'))) continue;
@@ -97,7 +93,7 @@ bool UriNormalizer::pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t&
     return true;
 }
 
-int32_t UriNormalizer::normCharClean(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) const {
+int32_t UriNormalizer::normCharClean(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) {
     int32_t length = 0;
     for (int32_t k = 0; k < inLength; k++) {
         switch (uriChar[inBuf[k]]) {
@@ -154,7 +150,7 @@ int32_t UriNormalizer::normCharClean(const uint8_t* inBuf, int32_t inLength, uin
 }
 
 // Convert URI backslashes to slashes
-int32_t UriNormalizer::normBackSlash(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) const {
+int32_t UriNormalizer::normBackSlash(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) {
     for (int32_t k = 0; k < inLength; k++) {
         if (inBuf[k] != '\\') outBuf[k] = inBuf[k];
         else {
@@ -166,7 +162,7 @@ int32_t UriNormalizer::normBackSlash(const uint8_t* inBuf, int32_t inLength, uin
 }
 
 // Caution: worst case output length is one greater than input length
-int32_t UriNormalizer::normPathClean(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) const {
+int32_t UriNormalizer::normPathClean(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) {
     int32_t length = 0;
     // It simplifies the code that handles /./ and /../ to pretend there is an extra '/' after the buffer.
     // Avoids making a special case of URIs that end in . or ..
index 860ce218d669f8cf40fddc76d36e775476a37965..595cd7ec5a7925996055b6dd26589c6f7398c3fa 100644 (file)
 
 class UriNormalizer {
 public:
-    UriNormalizer(bool doPath_) : doPath(doPath_) {};
-    void normalize(const field &input, field &result, ScratchPad &scratchPad, uint64_t &infractions) const;
+    static void normalize(const field &input, field &result, bool doPath, ScratchPad &scratchPad, uint64_t &infractions);
 
 private:
     static const NHttpEnums::CharAction uriChar[256];
     static const int8_t asHex[256];
     static const bool goodPercent[256];
 
-    bool noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) const;
-    bool pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) const;
+    static bool noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions);
+    static bool pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions);
 
-    int32_t normCharClean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed) const;
-    int32_t normBackSlash(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed) const;
-    int32_t normPathClean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed) const;
-
-    bool doPath;
+    static int32_t normCharClean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
+    static int32_t normBackSlash(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
+    static int32_t normPathClean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
 };
 
 #endif