From: Tom Peters Date: Wed, 25 Jun 2014 21:05:43 +0000 (-0400) Subject: URI normalization X-Git-Tag: 3.0.0-233~1454^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b04c76f32945e84918062eec76f94133ceb891ff;p=thirdparty%2Fsnort3.git URI normalization --- diff --git a/src/service_inspectors/nhttp_inspect/CMakeLists.txt b/src/service_inspectors/nhttp_inspect/CMakeLists.txt index a196af48c..76742dc3c 100644 --- a/src/service_inspectors/nhttp_inspect/CMakeLists.txt +++ b/src/service_inspectors/nhttp_inspect/CMakeLists.txt @@ -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 diff --git a/src/service_inspectors/nhttp_inspect/Makefile.am b/src/service_inspectors/nhttp_inspect/Makefile.am index 8807f8678..4b428ea9e 100644 --- a/src/service_inspectors/nhttp_inspect/Makefile.am +++ b/src/service_inspectors/nhttp_inspect/Makefile.am @@ -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 \ diff --git a/src/service_inspectors/nhttp_inspect/nhttp_enum.h b/src/service_inspectors/nhttp_inspect/nhttp_enum.h index d17225981..cd5be5189 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_enum.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_enum.h @@ -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; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc index 57d5236f1..bd30faa33 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.cc @@ -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; -} - - - - - diff --git a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.h b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.h index 126fe88a3..6aab0e275 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_head_norm.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_head_norm.h @@ -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 diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc index 77b846a6b..1453190e9 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc @@ -33,6 +33,7 @@ #include "snort.h" #include "nhttp_enum.h" +#include "nhttp_normalizers.h" #include "nhttp_msg_head_shared.h" using namespace NHttpEnums; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc index c55d3b040..ba654997c 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_request.cc @@ -34,11 +34,14 @@ #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 { - diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_request.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_request.h index 063764998..409ce2d19 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_request.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_request.h @@ -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 diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h index 2e44e2f6f..c36c50046 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h +++ b/src/service_inspectors/nhttp_inspect/nhttp_msg_section.h @@ -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 index 000000000..8ea718e5b --- /dev/null +++ b/src/service_inspectors/nhttp_inspect/nhttp_normalizers.cc @@ -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 +// +// @brief Normalizer functions +// + + +#include +#include +#include + +#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 index 000000000..8d062c2c1 --- /dev/null +++ b/src/service_inspectors/nhttp_inspect/nhttp_normalizers.h @@ -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 +// +// @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 + + diff --git a/src/service_inspectors/nhttp_inspect/nhttp_tables.cc b/src/service_inspectors/nhttp_inspect/nhttp_tables.cc index 84014c259..0af32ee4c 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_tables.cc +++ b/src/service_inspectors/nhttp_inspect/nhttp_tables.cc @@ -37,10 +37,12 @@ #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; diff --git a/src/service_inspectors/nhttp_inspect/nhttp_test_msgs.txt b/src/service_inspectors/nhttp_inspect/nhttp_test_msgs.txt index df1e9da3f..857bd1fbd 100644 --- a/src/service_inspectors/nhttp_inspect/nhttp_test_msgs.txt +++ b/src/service_inspectors/nhttp_inspect/nhttp_test_msgs.txt @@ -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 index 000000000..d1dd9da81 --- /dev/null +++ b/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.cc @@ -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 +// +// @brief URI normalization functions +// + + +// &&&#include +#include +#include + +#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 index 000000000..9251feb6a --- /dev/null +++ b/src/service_inspectors/nhttp_inspect/nhttp_uri_norm.h @@ -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 +// +// @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 index 000000000..c147115fd --- /dev/null +++ b/src/service_inspectors/nhttp_inspect/nhttp_uri_tables.cc @@ -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 +// +// @brief Static constant tables for URI scanning and normalization +// +// + +#include +#include + +#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 }; +