]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
URI normalization
authorTom Peters <thopeter@cisco.com>
Wed, 25 Jun 2014 21:05:43 +0000 (17:05 -0400)
committerTom Peters <thopeter@cisco.com>
Wed, 25 Jun 2014 21:05:43 +0000 (17:05 -0400)
16 files changed:
src/service_inspectors/nhttp_inspect/CMakeLists.txt
src/service_inspectors/nhttp_inspect/Makefile.am
src/service_inspectors/nhttp_inspect/nhttp_enum.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_msg_head_shared.cc
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.h
src/service_inspectors/nhttp_inspect/nhttp_normalizers.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_normalizers.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_tables.cc
src/service_inspectors/nhttp_inspect/nhttp_test_msgs.txt
src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_uri_norm.h [new file with mode: 0644]
src/service_inspectors/nhttp_inspect/nhttp_uri_tables.cc [new file with mode: 0644]

index a196af48c9f367d4c5d6b3fdfc6c1e45ca7e2165..76742dc3c0513137f23bfd53cdc5cf39a700c738 100644 (file)
@@ -24,10 +24,15 @@ set (FILE_LIST
     nhttp_msg_trailer.h
     nhttp_head_norm.cc
     nhttp_head_norm.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
     nhttp_api.cc nhttp_api.h
     nhttp_tables.cc
+    nhttp_uri_tables.cc
     nhttp_module.cc
     nhttp_module.h
     nhttp_test_input.cc
index 8807f86789ae75f679ffa1f2b0491296f3e70e9c..4b428ea9e27dccc50afc6c68f007f1605fcdf67b 100644 (file)
@@ -13,9 +13,12 @@ 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_norm.cc nhttp_uri_norm.h \
+nhttp_normalizers.cc nhttp_normalizers.h \
 nhttp_str_to_code.cc nhttp_str_to_code.h \
 nhttp_api.cc nhttp_api.h \
 nhttp_tables.cc \
+nhttp_uri_tables.cc \
 nhttp_module.cc nhttp_module.h \
 nhttp_test_input.cc nhttp_test_input.h \
 nhttp_flow_data.cc nhttp_flow_data.h \
index d17225981b5e41888f25faa7f6fa4d3a2c7f733b..cd5be5189e70643444241a0e0dbd5e7f9ecc8158 100644 (file)
@@ -74,13 +74,21 @@ typedef enum { HEAD__NOTCOMPUTE=-4, HEAD__INSUFMEMORY=-3, HEAD__PROBLEMATIC=-2,
    HEAD_CONTENT_LOCATION, HEAD_CONTENT_MD5, HEAD_CONTENT_RANGE, HEAD_CONTENT_TYPE, HEAD_EXPIRES, HEAD_LAST_MODIFIED, HEAD__MAXVALUE } HeaderId;
 
 // All the infractions we might find while parsing and analyzing a message
-typedef enum { INF_TRUNCATED=0x1, INF_HEADTOOLONG=0x2, /*INF_STARTTOOSHORT=0x4,*/ INF_BADREQLINE=0x8, INF_BADSTATLINE=0x10, INF_TOOMANYHEADERS=0x20,
-   INF_BADHEADER=0x40, INF_BADSTATCODE=0x80, INF_UNKNOWNVERSION=0x100, INF_BADVERSION=0x200, INF_NOSCRATCH=0x400, INF_BADHEADERREPS=0x800, INF_BADHEADERDATA=0x1000,
-   INF_BROKENCHUNK=0x2000, INF_BADCHUNKSIZE=0x4000, INF_BADPHRASE=0x8000, INF_BADURI=0x10000, INF_BADPORT=0x20000  } Infraction;
+typedef enum {
+   INF_TRUNCATED=0x1, INF_HEADTOOLONG=0x2, INF_BADREQLINE=0x4, INF_BADSTATLINE=0x8, INF_TOOMANYHEADERS=0x10,
+   INF_BADHEADER=0x20, INF_BADSTATCODE=0x40, INF_UNKNOWNVERSION=0x80, INF_BADVERSION=0x100, INF_NOSCRATCH=0x200,
+   INF_BADHEADERREPS=0x400, INF_BADHEADERDATA=0x800, INF_BROKENCHUNK=0x1000, INF_BADCHUNKSIZE=0x2000,
+   INF_BADPHRASE=0x4000, INF_BADURI=0x8000, INF_BADPORT=0x10000, INF_URINEEDNORM=0x20000, INF_URIPERCENTNORMAL=0x40000,
+   INF_URIPERCENTASCII=0x80000, INF_URIPERCENTUTF8=0x100000, INF_URIPERCENTUCODE=0x200000, INF_URIPERCENTOTHER=0x400000,
+   INF_URIBADCHAR=0x800000, INF_URI8BITCHAR=0x1000000, INF_URIMULTISLASH=0x2000000, INF_URIBACKSLASH=0x4000000,
+   INF_URISLASHDOT=0x8000000, INF_URISLASHDOTDOT=0x10000000, INF_URIROOTTRAV=0x20000000 } Infraction;
 
 // Formats for output from a header normalization function
 typedef enum { NORM_NULL, NORM_FIELD, NORM_INT64, NORM_ENUM64, NORM_ENUM64LIST } NormFormat;
 
+// Types of character for URI scanning
+typedef enum { CHAR_NORMAL=2, CHAR_PERCENT, CHAR_SLASH, CHAR_BACKSLASH, CHAR_PERIOD, CHAR_INVALID, CHAR_EIGHTBIT } CharAction;
+
 // Transfer codings
 typedef enum { TRANSCODE__OTHER=1, TRANSCODE_CHUNKED, TRANSCODE_IDENTITY, TRANSCODE_GZIP, TRANSCODE_COMPRESS, TRANSCODE_DEFLATE } Transcoding;
 
index 57d5236f13d321ecda38c69da32c65ed1e8391f5..bd30faa333b1a17b6b84b68e4668a048666c9044 100644 (file)
@@ -135,69 +135,4 @@ void HeaderNormalizer::normalize(ScratchPad &scratchPad, uint64_t &infractions,
     scratchPad.commit(dataLength);
 }
 
-// Collection of stock normalization functions. This will probably grow throughout the life of the software. New functions must follow the standard signature.
-// The void* at the end is for any special configuration data the function requires.
-
-int32_t normDecimalInteger(const uint8_t* inBuf, int32_t inLength, uint8_t* outBuf, uint64_t& infractions, const void *) {
-    // Limited to 18 decimal digits, not including leading zeros, to fit comfortably into int64_t
-    int64_t total = 0;
-    int nonLeadingZeros = 0;
-    for (int32_t k=0; k < inLength; k++) {
-        int value = inBuf[k] - '0';
-        if (nonLeadingZeros || (value != 0)) nonLeadingZeros++;
-        if (nonLeadingZeros > 18) {
-            infractions |= INF_BADHEADERDATA;
-            return STAT_PROBLEMATIC;
-        }
-        if ((value < 0) || (value > 9)) {
-            infractions |= INF_BADHEADERDATA;
-            return STAT_PROBLEMATIC;
-        }
-        total = total*10 + value;
-    }
-    ((int64_t*)outBuf)[0] = total;
-    return sizeof(int64_t);
-}
-
-
-int32_t norm2Lower(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t&, const void *) {
-    for (int32_t k=0; k < inLength; k++) {
-        outBuf[k] = ((inBuf[k] < 'A') || (inBuf[k] > 'Z')) ? inBuf[k] : inBuf[k] - ('A' - 'a');
-    }
-    return inLength;
-}
-
-
-int32_t normStrCode(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t&, const void *table) {
-    ((int64_t*)outBuf)[0] = strToCode(inBuf, inLength, (const StrCode*)table);
-    return sizeof(int64_t);
-}
-
-int32_t normSeqStrCode(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t&, const void *table) {
-    int32_t numCodes = 0;
-    const uint8_t* start = inBuf;
-    while (true) {
-        int32_t length;
-        for (length = 0; (start + length < inBuf + inLength) && (start[length] != ','); length++);
-        if (length == 0) ((uint32_t*)outBuf)[numCodes++] = STAT_EMPTYSTRING;
-        else ((int64_t*)outBuf)[numCodes++] = strToCode(start, length, (const StrCode*)table);
-        if (start + length >= inBuf + inLength) break;
-        start += length + 1;
-    }
-    return numCodes * sizeof(int64_t);
-}
-
-// Remove all space and tab characters (known as LWS or linear white space in the RFC)
-int32_t normRemoveLws(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t&, const void *) {
-    int32_t length = 0;
-    for (int32_t k = 0; k < inLength; k++) {
-        if ((inBuf[k] != ' ') && (inBuf[k] != '\t')) outBuf[length++] = inBuf[k];
-    }
-    return length;
-}
-
-
-
-
-
 
index 126fe88a38fd0d3fd35af66c9e9161f311e13e1c..6aab0e2759bd3e9e294df312c5d7e76cbf52771b 100644 (file)
@@ -67,14 +67,6 @@ private:
     const int numNormalizers;
 };
 
-// Normalizer functions
-
-int32_t normDecimalInteger(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
-int32_t norm2Lower(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
-int32_t normStrCode(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*);
-int32_t normSeqStrCode(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*);
-int32_t normRemoveLws(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
-
 #endif
 
 
index 77b846a6bc27b19638c6d01133ec9bf38a69b071..1453190e947882afd787a02bba74ba8856189c7e 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "snort.h"
 #include "nhttp_enum.h"
+#include "nhttp_normalizers.h"
 #include "nhttp_msg_head_shared.h"
 
 using namespace NHttpEnums;
index c55d3b040f448eaf99b0e0b819abcd69e0a3b9e4..ba654997cefbd9c04276ffb2cff206af8047a47f 100644 (file)
 
 #include "snort.h"
 #include "nhttp_enum.h"
-#include "nhttp_head_norm.h"
+#include "nhttp_normalizers.h"
 #include "nhttp_msg_request.h"
 
 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();
@@ -49,14 +52,18 @@ void NHttpMsgRequest::initSection() {
     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;
 }
 
@@ -69,6 +76,11 @@ void NHttpMsgRequest::analyze() {
     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();
 }
 
 void NHttpMsgRequest::parseStartLine() {
@@ -196,7 +208,7 @@ void NHttpMsgRequest::derivePortValue() {
         portValue = portValue * 10 + (port.start[k] - '0');
         if ((port.start[k] < '0') || (port.start[k] > '9') || (portValue > 65535))
         {
-            infractions |= INF_BADURI;
+            infractions |= INF_BADPORT;
             portValue = STAT_PROBLEMATIC;
             break;
         }
@@ -250,6 +262,8 @@ void NHttpMsgRequest::printSection(FILE *output) const {
     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);
     NHttpMsgSection::printMessageWrapup(output);
 }
 
@@ -273,6 +287,67 @@ void NHttpMsgRequest::updateFlow() const {
     }
 }
 
+// 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 {
     if (method.length > 0) SetHttpBuffer(HTTP_BUFFER_METHOD, method.start, (unsigned)method.length);
@@ -283,4 +358,3 @@ void NHttpMsgRequest::legacyClients() const {
 
 
 
-
index 06376499861d7b2cc5e3f5cf329d28197d5fc814..409ce2d19bf2d60760882b964b87451db1d34230 100644 (file)
@@ -30,6 +30,7 @@
 #define NHTTP_MSG_REQUEST_H
 
 #include "nhttp_str_to_code.h"
+#include "nhttp_uri_norm.h"
 #include "nhttp_msg_start.h"
 
 //-------------------------------------------------------------------------
@@ -51,6 +52,10 @@ private:
     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.
     void parseStartLine();
@@ -60,6 +65,7 @@ private:
     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
@@ -68,21 +74,27 @@ private:
 
     // URI stuff
     field uri;
-    field uriLegacyNorm;
-    NHttpEnums::UriType uriType;
     field scheme;
     field authority;
     field host;
-    field hostNorm;
     field port;
-    int32_t portValue;
     field absPath;
     field path;
-    field pathNorm;
     field query;
-    field queryNorm;
     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;
 };
 
 #endif
index 2e44e2f6f97f4e222b1eaa24fe795953455fbe13..c36c5004609bbfc9218aa1e1de606c5212a2e964 100644 (file)
@@ -64,10 +64,9 @@ protected:
     const uint8_t * const msgText = rawBuf;
 
     // Working space and storage for all the derived fields. See scratchPad.h for usage instructions.
-    // Allocation size may be complete overkill. Need to revisit this.
-    uint64_t derivedBuf[NHttpEnums::MAXOCTETS/8];
+    uint64_t derivedBuf[NHttpEnums::MAXOCTETS/4];
     NHttpFlowData* sessionData;
-    ScratchPad scratchPad {derivedBuf, NHttpEnums::MAXOCTETS/8};
+    ScratchPad scratchPad {derivedBuf, NHttpEnums::MAXOCTETS/4};
 
     // 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
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_normalizers.cc b/src/service_inspectors/nhttp_inspect/nhttp_normalizers.cc
new file mode 100644 (file)
index 0000000..8ea718e
--- /dev/null
@@ -0,0 +1,143 @@
+/****************************************************************************
+ *
+** 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      Normalizer functions
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "nhttp_enum.h"
+#include "nhttp_str_to_code.h"
+#include "nhttp_normalizers.h"
+
+using namespace NHttpEnums;
+
+// Collection of stock normalization functions. This will probably grow throughout the life of the software. New functions must follow the standard signature.
+// The void* at the end is for any special configuration data the function requires.
+
+int32_t normDecimalInteger(const uint8_t* inBuf, int32_t inLength, uint8_t* outBuf, uint64_t& infractions, const void *) {
+    // Limited to 18 decimal digits, not including leading zeros, to fit comfortably into int64_t
+    int64_t total = 0;
+    int nonLeadingZeros = 0;
+    for (int32_t k=0; k < inLength; k++) {
+        int value = inBuf[k] - '0';
+        if (nonLeadingZeros || (value != 0)) nonLeadingZeros++;
+        if (nonLeadingZeros > 18) {
+            infractions |= INF_BADHEADERDATA;
+            return STAT_PROBLEMATIC;
+        }
+        if ((value < 0) || (value > 9)) {
+            infractions |= INF_BADHEADERDATA;
+            return STAT_PROBLEMATIC;
+        }
+        total = total*10 + value;
+    }
+    ((int64_t*)outBuf)[0] = total;
+    return sizeof(int64_t);
+}
+
+
+int32_t norm2Lower(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t&, const void *) {
+    for (int32_t k=0; k < inLength; k++) {
+        outBuf[k] = ((inBuf[k] < 'A') || (inBuf[k] > 'Z')) ? inBuf[k] : inBuf[k] - ('A' - 'a');
+    }
+    return inLength;
+}
+
+
+int32_t normStrCode(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t&, const void *table) {
+    ((int64_t*)outBuf)[0] = strToCode(inBuf, inLength, (const StrCode*)table);
+    return sizeof(int64_t);
+}
+
+int32_t normSeqStrCode(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t&, const void *table) {
+    int32_t numCodes = 0;
+    const uint8_t* start = inBuf;
+    while (true) {
+        int32_t length;
+        for (length = 0; (start + length < inBuf + inLength) && (start[length] != ','); length++);
+        if (length == 0) ((uint32_t*)outBuf)[numCodes++] = STAT_EMPTYSTRING;
+        else ((int64_t*)outBuf)[numCodes++] = strToCode(start, length, (const StrCode*)table);
+        if (start + length >= inBuf + inLength) break;
+        start += length + 1;
+    }
+    return numCodes * sizeof(int64_t);
+}
+
+// Remove all space and tab characters (known as LWS or linear white space in the RFC)
+int32_t normRemoveLws(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t&, const void *) {
+    int32_t length = 0;
+    for (int32_t k = 0; k < inLength; k++) {
+        if ((inBuf[k] != ' ') && (inBuf[k] != '\t')) outBuf[length++] = inBuf[k];
+    }
+    return length;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_normalizers.h b/src/service_inspectors/nhttp_inspect/nhttp_normalizers.h
new file mode 100644 (file)
index 0000000..8d062c2
--- /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      Normalizer function declarations
+//
+
+#ifndef NHTTP_NORMALIZERS_H
+#define NHTTP_NORMALIZERS_H
+
+int32_t normDecimalInteger(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
+int32_t norm2Lower(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
+int32_t normStrCode(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*);
+int32_t normSeqStrCode(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*);
+int32_t normRemoveLws(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
+
+#endif
+
+
index 84014c25910f5457253644e9cf6cb4560db7b1de..0af32ee4c07c345d7650c5ba387e7243778e2f00 100644 (file)
 #include "framework/module.h"
 #include "nhttp_enum.h"
 #include "nhttp_str_to_code.h"
+#include "nhttp_normalizers.h"
 #include "nhttp_head_norm.h"
 #include "nhttp_msg_request.h"
 #include "nhttp_msg_head.h"
 #include "nhttp_module.h"
+#include "nhttp_uri_norm.h"
 
 using namespace NHttpEnums;
 
index df1e9da3f97e94a284b6b3c255841834e5ad0f6d..857bd1fbd137c4806fdc4f437c4141b96a9c3b1c 100644 (file)
@@ -233,7 +233,7 @@ HTTP/1.0 401 illegal nontext char\x00acter in reason phrase null\r\n\r\n
 @2027
 @break
 @response
-HTTP/1.0 401 illegal nontext character in reason\xffphrase delete\r\n\r\n
+HTTP/1.0 401 illegal nontext character in reason\xFFphrase delete\r\n\r\n
 
 @2028
 @break
@@ -348,6 +348,82 @@ GET HtTpS://1.2.3.4.5.a:6/abcdef/ghijklmnop/qrstuvwxyz/?thequery?fieldcontinues?
 @4001
 @break
 @request
+GET http://sam\xC6plehost.%3d.com:65535/abcdef//ghijkl\x7F?%21%23%24%25%26%27%28%29%2a%2b%2c%2f%3a%3b%3d%3f%40%5b%5dallgoodescapeshere#1234567890%abaaa HTTP/1.1\r\n\r\n
+
+@4002
+@break
+@request
+GET http://hostname.com:0/abcde/fghijklmn/opqrstuvwxyz?%5D%5d%5B%5b%40%3F%3f%3D%3d%3B%3b%3A%3a%2F%2f%2C%2c%2B%2b%2A%2a%29%28%27%26%25%24%23%21#
+1%5D2%5d3%5B4%5b5%406%3F7%3f8%3D9%3da%3Bb%3bc%3Ad%3ae%2Ff%2fg%2CA%2cB%2BC%2bD%2AE%2aF%29%28%27%26%25%24%23%21 HTTP/1.1\r\n\r\n
+
+@4003
+@break
+@request
+GET HTTP://longLONGlonglonglonglonglonglonglonglonglonglong.host.name.com:1/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789/
+-_.~.._./?# HTTP/1.1\r\n\r\n
+
+@4004
+@break
+@request
+GET HTTPS://host%6Eamewith\xD3impr oper%25.characters.com:123/..?# HTTP/1.1\r\n\r\n
+
+@4005
+@break
+@request
+GET Ftp://%48%6F%53%74%4E%61%4d%2e%63%6F%6D:45678/./123/./456/./7890././.abcdef/ghijklmn///.//./.?# HTTP/1.1\r\n\r\n
+
+@4006
+@break
+@request
+GET a://\xe8hostname.com%25%22\x08:65535/./././././//././/?# HTTP/1.1\r\n\r\n
+
+@4007
+@break
+@request
+GET http\x00://hostname.com:65536/%2e%2f/%2E%2F%2E%2F%2E%2F%2f%2f%2fUVWXYZ__~_/%2E?# HTTP/1.1\r\n\r\n
+
+@4008
+@break
+@request
+GET \xFF://hostname.com:1000000000/..?# HTTP/1.1\r\n\r\n
+
+@4009
+@break
+@request
+GET ht tp://hostname.com/../?# HTTP/1.1\r\n\r\n
+
+@4010
+@break
+@request
+GET http://hostname.com/../../../../%2e%2e/.%2e%2f%2e./../bogus_directory///bogus-subdirectory%2F../../real_directory//
+%54%61%72%67%03%65%74/./?# HTTP/1.1\r\n\r\n
+
+@4011
+@break
+@request
+GET http://hostname.com/1/2/../3//4/..///5/6/..//////////7/8abcdefghijklmnopqrstuvwxyz%25abcdefghijklmnopqrstuvwxyz%3D/
+fa%6be/..?# HTTP/1.1\r\n\r\n
+
+@4012
+@break
+@request
+GET http://hostname.com/1\\2/../3/\\4/..\\//5/6/..///\\\\\\////7/8abcdefghijklmnopqrstuvwxyz%25abcdefghijklmnopqrstuvwxyz%3D\\
+fa%6be\\..?# HTTP/1.1\r\n\r\n
+
+@4013
+@break
+@request
+GET http://hostname.com/?# HTTP/1.1\r\n\r\n
+
+@4014
+@break
+@request
+GET http://hostname.com/?# HTTP/1.1\r\n\r\n
+
+@4015
+@break
+@request
+GET http://hostname.com/?# HTTP/1.1\r\n\r\n
 
 
 # ***********************************************************************************************
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc b/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc
new file mode 100644 (file)
index 0000000..d1dd9da
--- /dev/null
@@ -0,0 +1,236 @@
+/****************************************************************************
+ *
+** 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      URI normalization functions
+//
+
+
+// &&&#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+
+#include "nhttp_enum.h"
+#include "nhttp_uri_norm.h"
+
+using namespace NHttpEnums;
+
+void UriNormalizer::normalize(const field &input, field &result, ScratchPad &scratchPad, uint64_t &infractions) const {
+    if (result.length != STAT_NOTCOMPUTE) return;
+    if (input.length < 0) {
+        result.length = STAT_NOTPRESENT;
+        return;
+    }
+
+    // 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
+    // at the raw value.
+    if ( ( doPath && pathCheck(input.start, input.length, infractions))      ||
+         (!doPath && noPathCheck(input.start, input.length, infractions)))
+    {
+        result.start = input.start;
+        result.length = input.length;
+        return;
+    }
+
+    // Add an extra byte because normalization on rare occasions adds an extra character
+    // We need working space for two copies to do multiple passes.
+    // Round up to multiple of eight so that both copies are 64-bit aligned.
+    const int32_t bufferLength = input.length + 1 + (8-(input.length+1)%8)%8;
+    uint8_t * const scratch = scratchPad.request(2 * bufferLength);
+    if (scratch == nullptr) {
+        result.length = STAT_INSUFMEMORY;
+        return;
+    }
+    uint8_t* const frontHalf = scratch;
+    uint8_t* const backHalf = scratch + bufferLength;
+
+    int32_t dataLength;
+    dataLength = normCharClean(input.start, input.length, frontHalf, infractions, nullptr);
+    if (doPath) {
+        dataLength = normBackSlash(frontHalf, dataLength, backHalf, infractions, nullptr);
+        dataLength = normPathClean(backHalf, dataLength, frontHalf, infractions, nullptr);
+    }
+
+    scratchPad.commit(dataLength);
+    result.start = frontHalf;
+    result.length = dataLength;
+}
+
+bool UriNormalizer::noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) const {
+    for (int32_t k = 0; k < inLength; k++) {
+        if (nonPathChar[inBuf[k]] == CHAR_NORMAL) continue;
+        infractions |= INF_URINEEDNORM;
+        return false;
+    }
+    return true;
+}
+
+bool UriNormalizer::pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) const {
+    for (int32_t k = 0; k < inLength; k++) {
+        if (pathChar[inBuf[k]] == CHAR_NORMAL) continue;
+        if ((inBuf[k] == '/') && ((k == 0) || (inBuf[k-1] != '/'))) continue;
+        infractions |= INF_URINEEDNORM;
+        return false;
+    }
+    return true;
+}
+
+int32_t UriNormalizer::normCharClean(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) const {
+    int32_t length = 0;
+    for (int32_t k = 0; k < inLength; k++) {
+        switch (nonPathChar[inBuf[k]]) {
+          case CHAR_NORMAL:
+            outBuf[length++] = inBuf[k];
+            break;
+          case CHAR_INVALID:
+            infractions |= INF_URIBADCHAR;
+            outBuf[length++] = inBuf[k];
+            break;
+          case CHAR_EIGHTBIT:
+            infractions |= INF_URI8BITCHAR;
+            outBuf[length++] = inBuf[k];
+            break;
+          case CHAR_PERCENT:
+            if ((k+2 < inLength) && (asHex[inBuf[k+1]] != -1) && (asHex[inBuf[k+2]] != -1)) {
+                if (asHex[inBuf[k+1]] <= 7) {
+                    uint8_t value = asHex[inBuf[k+1]] * 16 + asHex[inBuf[k+2]];
+                    if (goodPercent[value]) {
+                        // Normal % escape of an ASCII special character that is supposed to be escaped
+                        infractions |= INF_URIPERCENTNORMAL;
+                        outBuf[length++] = '%';
+                    }
+                    else {
+                        // Suspicious % escape of an ASCII character that does not need to be escaped
+                        infractions |= INF_URIPERCENTASCII;
+                        if (nonPathChar[value] == CHAR_INVALID) infractions |= INF_URIBADCHAR;
+                        outBuf[length++] = value;
+                        k += 2;
+                    }
+                }
+                else {
+                    // UTF-8 decoding not implemented yet
+                    infractions |= INF_URIPERCENTUTF8;
+                    outBuf[length++] = '%';
+                }
+            }
+            else if ((k+5 < inLength) && (inBuf[k+1] == 'u') && (asHex[inBuf[k+2]] != -1) && (asHex[inBuf[k+3]] != -1)
+               && (asHex[inBuf[k+4]] != -1) && (asHex[inBuf[k+5]] != -1)) {
+                // 'u' UTF-16 decoding not implemented yet
+                infractions |= INF_URIPERCENTUCODE;
+                outBuf[length++] = '%';
+            }
+            else {
+                // Don't recognize it
+                infractions |= INF_URIPERCENTOTHER;
+                outBuf[length++] = '%';
+            }
+            break;
+          default:
+            assert(0);
+            break;
+        }
+    }
+    return length;
+}
+
+// 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 {
+    for (int32_t k = 0; k < inLength; k++) {
+        if (inBuf[k] != '\\') outBuf[k] = inBuf[k];
+        else {
+            outBuf[k] = '/';
+            infractions |= INF_URIBACKSLASH;
+        }
+    }
+    return inLength;
+}
+
+// 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 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 ..
+    // That is why the loop steps off the end of the input buffer by saying <= instead of <.
+    for (int32_t k = 0; k <= inLength; k++) {
+        // Pass through all non-slash characters and also the leading slash
+        if (((k < inLength) && (inBuf[k] != '/')) || (k == 0)) {
+            outBuf[length++] = inBuf[k];
+        }
+        // Ignore this slash if it directly follows another slash
+        else if ((k < inLength) && (length >= 1) && (outBuf[length-1] == '/')) {
+            infractions |= INF_URIMULTISLASH;
+        }
+        // This slash is the end of a /./ pattern, ignore this slash and remove the period from the output
+        else if ((length >= 2) && (outBuf[length-1] == '.') && (outBuf[length-2] == '/')) {
+            infractions |= INF_URISLASHDOT;
+            length -= 1;
+        }
+        // This slash is the end of a /../ pattern, normalization depends on whether there is a previous directory that
+        // we can remove
+        else if ((length >= 3) && (outBuf[length-1] == '.') && (outBuf[length-2] == '.') && (outBuf[length-3] == '/')) {
+            infractions |= INF_URISLASHDOTDOT;
+            // Traversing above the root of the absolute path. A path of the form /../../../foo/bar/whatever cannot be
+            // further normalized. Instead of taking away a directory we leave the .. and write out the new slash.
+            // This code can write out the pretend slash after the end of the buffer. That is intentional so that the
+            // normal form of "/../../../.." is "/../../../../"
+            if ( (length == 3) ||
+                ((length >= 6) && (outBuf[length-4] == '.') && (outBuf[length-5] == '.') && (outBuf[length-6] == '/')))
+            {
+                infractions |= INF_URIROOTTRAV;
+                outBuf[length++] = '/';
+            }
+            // Remove the previous directory from the output. "/foo/bar/../" becomes "/foo/"
+            else {
+                for (length -= 3; outBuf[length-1] != '/'; length--);
+            }
+        }
+        // Pass through an ordinary slash
+        else if (k < inLength) outBuf[length++] = '/';
+    }
+    return length;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.h b/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.h
new file mode 100644 (file)
index 0000000..9251feb
--- /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      URI Normalizer class
+//
+
+#ifndef NHTTP_URI_NORM_H
+#define NHTTP_URI_NORM_H
+
+#include "nhttp_scratch_pad.h"
+
+class UriNormalizer {
+public:
+    UriNormalizer(bool doPath_) : doPath(doPath_) {};
+    void normalize(const field &input, field &result, ScratchPad &scratchPad, uint64_t &infractions) const;
+
+private:
+    static const NHttpEnums::CharAction pathChar[256];
+    static const NHttpEnums::CharAction nonPathChar[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;
+
+    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;
+};
+
+#endif
+
+
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_uri_tables.cc b/src/service_inspectors/nhttp_inspect/nhttp_uri_tables.cc
new file mode 100644 (file)
index 0000000..c147115
--- /dev/null
@@ -0,0 +1,169 @@
+/****************************************************************************
+ *
+** 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 URI scanning and normalization
+//
+//
+
+#include <string.h>
+#include <sys/types.h>
+
+#include "nhttp_enum.h"
+#include "nhttp_uri_norm.h"
+
+using namespace NHttpEnums;
+
+const CharAction UriNormalizer::pathChar[256] = {
+   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,
+   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,
+   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,
+   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,
+
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_PERCENT,   CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_PERIOD,    CHAR_SLASH,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_BACKSLASH, CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_INVALID,
+
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT };
+
+const CharAction UriNormalizer::nonPathChar[256] = {
+   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,
+   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,
+   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,
+   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,   CHAR_INVALID,
+
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_PERCENT,   CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,
+   CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_NORMAL,    CHAR_INVALID,
+
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,
+   CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT,  CHAR_EIGHTBIT };
+
+const int8_t UriNormalizer::asHex[256] = {
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
+
+   -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+
+   -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1  };
+
+const bool UriNormalizer::goodPercent[256] = {
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+   false, false, false, false, false, false, false, false, false, false, false,  true, false,  true, false, false, 
+
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+   false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false };
+