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_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 \
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;
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;
-}
-
-
-
-
-
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
#include "snort.h"
#include "nhttp_enum.h"
+#include "nhttp_normalizers.h"
#include "nhttp_msg_head_shared.h"
using namespace NHttpEnums;
#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();
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;
}
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() {
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;
}
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);
}
}
}
+// 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);
-
#define NHTTP_MSG_REQUEST_H
#include "nhttp_str_to_code.h"
+#include "nhttp_uri_norm.h"
#include "nhttp_msg_start.h"
//-------------------------------------------------------------------------
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();
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
// 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
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
--- /dev/null
+/****************************************************************************
+ *
+** 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;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+/****************************************************************************
+ *
+** 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
+
+
#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;
@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
@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
# ***********************************************************************************************
--- /dev/null
+/****************************************************************************
+ *
+** 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;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+/****************************************************************************
+ *
+** 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
+
+
--- /dev/null
+/****************************************************************************
+ *
+** 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 };
+