nhttp_msg_trailer.h
nhttp_head_norm.cc
nhttp_head_norm.h
+ nhttp_uri.cc
+ nhttp_uri.h
nhttp_uri_norm.cc
nhttp_uri_norm.h
nhttp_normalizers.cc
nhttp_msg_chunk_body.cc nhttp_msg_chunk_body.h \
nhttp_msg_trailer.cc nhttp_msg_trailer.h \
nhttp_head_norm.cc nhttp_head_norm.h \
+nhttp_uri.cc nhttp_uri.h \
nhttp_uri_norm.cc nhttp_uri_norm.h \
nhttp_normalizers.cc nhttp_normalizers.h \
nhttp_str_to_code.cc nhttp_str_to_code.h \
#include "snort.h"
#include "target_based/sftarget_protocol_reference.h"
#include "nhttp_enum.h"
-#include "nhttp_module.h"
#include "nhttp_inspect.h"
#include "nhttp_api.h"
int16_t NHttpApi::appProtocolId;
-Module* NHttpApi::nhttp_mod_ctor() { return new NHttpModule; }
-
-void NHttpApi::nhttp_mod_dtor(Module* m) { delete m; }
-
const char* NHttpApi::nhttp_myName = "nhttp_inspect";
void NHttpApi::nhttp_init()
appProtocolId = AddProtocolReference("nhttp");
}
-void NHttpApi::nhttp_term()
-{
-}
-
Inspector* NHttpApi::nhttp_ctor(Module* mod)
{
const NHttpModule* nhttpMod = (NHttpModule*) mod;
- return new NHttpInspect(nhttpMod->get_test_input(), nhttpMod->get_test_output());
-}
-
-void NHttpApi::nhttp_dtor(Inspector* p)
-{
- delete p;
-}
-
-void NHttpApi::nhttp_pinit()
-{
- NHttpInspect::msgRequest = new NHttpMsgRequest;
- NHttpInspect::msgStatus = new NHttpMsgStatus;
- NHttpInspect::msgHead = new NHttpMsgHeader;
- NHttpInspect::msgBody = new NHttpMsgBody;
- NHttpInspect::msgChunkHead = new NHttpMsgChunkHead;
- NHttpInspect::msgChunkBody = new NHttpMsgChunkBody;
- NHttpInspect::msgTrailer = new NHttpMsgTrailer;
-}
-
-void NHttpApi::nhttp_pterm()
-{
- delete NHttpInspect::msgHead;
-}
-
-void NHttpApi::nhttp_sum()
-{
-}
-
-void NHttpApi::nhttp_stats()
-{
-}
-
-void NHttpApi::nhttp_reset()
-{
+ return new NHttpInspect(nhttpMod->get_test_input(), nhttpMod->get_test_output(), nhttpMod->get_test_inspect());
}
static const char* buffers[] =
#include "framework/module.h"
#include "framework/inspector.h"
+#include "nhttp_module.h"
+
class NHttpApi {
public:
static const InspectApi nhttp_api;
static int16_t appProtocolId;
private:
NHttpApi() = delete;
- static Module* nhttp_mod_ctor();
- static void nhttp_mod_dtor(Module* m);
+ static Module* nhttp_mod_ctor() { return new NHttpModule; };
+ static void nhttp_mod_dtor(Module* m) { delete m; };
static const char* nhttp_myName;
static void nhttp_init();
- static void nhttp_term();
+ static void nhttp_term() {};
static Inspector* nhttp_ctor(Module* mod);
- static void nhttp_dtor(Inspector* p);
- static void nhttp_pinit();
- static void nhttp_pterm();
- static void nhttp_sum();
- static void nhttp_stats();
- static void nhttp_reset();
+ static void nhttp_dtor(Inspector* p) { delete p; };
+ static void nhttp_pinit() {};
+ static void nhttp_pterm() {};
+ static void nhttp_sum() {};
+ static void nhttp_stats() {};
+ static void nhttp_reset() {};
};
#endif
static const uint32_t MAXOCTETS = 63780;
// Field status codes for when no valid value is present in length or integer value. Positive values are actual length or field value.
-typedef enum { STAT_NOTCONFIGURED=-5, STAT_NOTCOMPUTE=-4, STAT_INSUFMEMORY=-3, STAT_PROBLEMATIC=-2, STAT_NOTPRESENT=-1, STAT_EMPTYSTRING=0, STAT_OTHER=1 } StatusCode;
+typedef enum { STAT_NOSOURCE=-6, STAT_NOTCONFIGURED=-5, STAT_NOTCOMPUTE=-4, STAT_INSUFMEMORY=-3, STAT_PROBLEMATIC=-2, STAT_NOTPRESENT=-1, STAT_EMPTYSTRING=0, STAT_OTHER=1 } StatusCode;
// Message originator--client or server
typedef enum { SRC__NOTCOMPUTE=-4, SRC_CLIENT=0, SRC_SERVER=1 } SourceId;
SEC_CLOSED, SEC_ABORT } SectionType;
// List of possible HTTP versions. Version 0.9 omitted because 0.9 predates creation of the HTTP/X.Y token. There would never be a message with "HTTP/0.9"
-typedef enum { VERS__NOTCOMPUTE=-4, VERS__PROBLEMATIC=-2, VERS__NOTPRESENT=-1, VERS__OTHER=1, VERS_1_0, VERS_1_1, VERS_2_0 } VersionId;
+typedef enum { VERS__NOSOURCE=-6, VERS__NOTCOMPUTE=-4, VERS__PROBLEMATIC=-2, VERS__NOTPRESENT=-1, VERS__OTHER=1, VERS_1_0, VERS_1_1, VERS_2_0 } VersionId;
// Every request method we have ever heard of
-typedef enum { METH__NOTCOMPUTE=-4, METH__INSUFMEMORY=-3, METH__PROBLEMATIC=-2, METH__NOTPRESENT=-1, METH__OTHER=1, METH_OPTIONS, METH_GET, METH_HEAD, METH_POST, METH_PUT,
- METH_DELETE, METH_TRACE, METH_CONNECT, METH_PROPFIND,
+typedef enum { METH__NOSOURCE=-6, METH__NOTCOMPUTE=-4, METH__INSUFMEMORY=-3, METH__PROBLEMATIC=-2, METH__NOTPRESENT=-1, METH__OTHER=1, METH_OPTIONS, METH_GET, METH_HEAD,
+ METH_POST, METH_PUT, METH_DELETE, METH_TRACE, METH_CONNECT, METH_PROPFIND,
METH_PROPPATCH, METH_MKCOL, METH_COPY, METH_MOVE, METH_LOCK, METH_UNLOCK, METH_VERSION_CONTROL, METH_REPORT, METH_CHECKOUT, METH_CHECKIN, METH_UNCHECKOUT,
METH_MKWORKSPACE, METH_UPDATE, METH_LABEL, METH_MERGE, METH_BASELINE_CONTROL, METH_MKACTIVITY, METH_ORDERPATCH, METH_ACL, METH_PATCH, METH_SEARCH, METH_BCOPY,
METH_BDELETE, METH_BMOVE, METH_BPROPFIND, METH_BPROPPATCH, METH_NOTIFY, METH_POLL, METH_SUBSCRIBE, METH_UNSUBSCRIBE, METH_X_MS_ENUMATTS, METH_BIND, METH_LINK,
METH_MKCALENDAR, METH_MKREDIRECTREF, METH_REBIND, METH_UNBIND, METH_UNLINK, METH_UPDATEREDIRECTREF } MethodId;
// URI formats
-typedef enum { URI__NOTCOMPUTE=-4, URI__PROBLEMATIC=-2, URI__NOTPRESENT=-1, URI_ASTERISK = 2, URI_AUTHORITY, URI_ABSPATH, URI_ABSOLUTE } UriType;
+typedef enum { URI__NOSOURCE=-6, URI__NOTCOMPUTE=-4, URI__PROBLEMATIC=-2, URI_ASTERISK = 2, URI_AUTHORITY, URI_ABSPATH, URI_ABSOLUTE } UriType;
// URI schemes
-typedef enum { SCH__NOTCOMPUTE=-4, SCH__INSUFMEMORY=-3, SCH__NOTPRESENT=-1, SCH_OTHER = 1, SCH_HTTP, SCH_HTTPS, SCH_FTP, SCH_GOPHER, SCH_FILE } SchemeId;
+typedef enum { SCH__NOSOURCE=-6, SCH__NOTCOMPUTE=-4, SCH__INSUFMEMORY=-3, SCH__NOTPRESENT=-1, SCH_OTHER = 1, SCH_HTTP, SCH_HTTPS, SCH_FTP, SCH_GOPHER, SCH_FILE } SchemeId;
// Every header we have ever heard of
typedef enum { HEAD__NOTCOMPUTE=-4, HEAD__INSUFMEMORY=-3, HEAD__PROBLEMATIC=-2, HEAD__NOTPRESENT=-1, HEAD__OTHER=1, HEAD_CACHE_CONTROL, HEAD_CONNECTION, HEAD_DATE,
HEAD_EXPECT, HEAD_FROM, HEAD_HOST, HEAD_IF_MATCH, HEAD_IF_MODIFIED_SINCE, HEAD_IF_NONE_MATCH, HEAD_IF_RANGE, HEAD_IF_UNMODIFIED_SINCE, HEAD_MAX_FORWARDS,
HEAD_PROXY_AUTHORIZATION, HEAD_RANGE, HEAD_REFERER, HEAD_TE, HEAD_USER_AGENT, HEAD_ACCEPT_RANGES, HEAD_AGE, HEAD_ETAG, HEAD_LOCATION, HEAD_PROXY_AUTHENTICATE,
HEAD_RETRY_AFTER, HEAD_SERVER, HEAD_VARY, HEAD_WWW_AUTHENTICATE, HEAD_ALLOW, HEAD_CONTENT_ENCODING, HEAD_CONTENT_LANGUAGE, HEAD_CONTENT_LENGTH,
- HEAD_CONTENT_LOCATION, HEAD_CONTENT_MD5, HEAD_CONTENT_RANGE, HEAD_CONTENT_TYPE, HEAD_EXPIRES, HEAD_LAST_MODIFIED, HEAD__MAXVALUE } HeaderId;
+ HEAD_CONTENT_LOCATION, HEAD_CONTENT_MD5, HEAD_CONTENT_RANGE, HEAD_CONTENT_TYPE, HEAD_EXPIRES, HEAD_LAST_MODIFIED, HEAD_X_FORWARDED_FOR, HEAD_TRUE_CLIENT_IP,
+ HEAD__MAXVALUE } HeaderId;
// All the infractions we might find while parsing and analyzing a message
typedef enum {
// Never use the start pointer without verifying that length > 0.
struct field {
public:
- const uint8_t *start;
- int32_t length;
+ const uint8_t *start = nullptr;
+ int32_t length = NHttpEnums::STAT_NOTCOMPUTE;
};
typedef enum
versionId[sourceId] = VERS__NOTPRESENT;
methodId[sourceId] = METH__NOTPRESENT;
- schemeId[sourceId] = SCH__NOTPRESENT;
statusCodeNum[sourceId] = STAT_NOTPRESENT;
}
// Some items don't apply in both directions. Have two copies anyway just to simplify code and minimize hard-to-find bugs
NHttpEnums::VersionId versionId[2] = { NHttpEnums::VERS__NOTPRESENT, NHttpEnums::VERS__NOTPRESENT };
NHttpEnums::MethodId methodId[2] = { NHttpEnums::METH__NOTPRESENT, NHttpEnums::METH__NOTPRESENT };
- NHttpEnums::SchemeId schemeId[2] = { NHttpEnums::SCH__NOTPRESENT, NHttpEnums::SCH__NOTPRESENT };
int32_t statusCodeNum[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };
int64_t dataLength[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT }; // length of the data from Content-Length field or chunk header.
return outLength;
}
-void HeaderNormalizer::normalize(ScratchPad &scratchPad, uint64_t &infractions, HeaderId headId, const HeaderId headerNameId[], const field headerValue[], int32_t numHeaders,
+// This method normalizes the header field value for headId.
+int32_t HeaderNormalizer::normalize(HeaderId headId, ScratchPad &scratchPad, uint64_t &infractions, const HeaderId headerNameId[], const field headerValue[], int32_t numHeaders,
field &resultField) const {
- // This method normalizes the header field value for headId.
+ // If the raw header is not present length will be STAT_NOSOURCE and normalization is skipped
+ if (resultField.length != STAT_NOTCOMPUTE) return resultField.length;
if (format == NORM_NULL) {
resultField.length = STAT_NOTCONFIGURED;
- return;
+ return resultField.length;
}
// Search Header IDs from all the headers in this message. A critical issue is whether the header can be present more than once in a message. concatenateRepeats means the
}
}
if (numMatches == 0) {
- resultField.length = STAT_NOTPRESENT;
- return;
+ resultField.length = STAT_NOSOURCE;
+ return resultField.length;
}
if (infractRepeats && (numMatches >= 2)) infractions |= INF_BADHEADERREPS;
uint8_t * const scratch = scratchPad.request(2*bufferLength);
if (scratch == nullptr) {
resultField.length = STAT_INSUFMEMORY;
- return;
+ return resultField.length;
}
uint8_t * const frontHalf = scratch;
else dataLength = normalizer[i](frontHalf, dataLength, backHalf, infractions, normArg[i]);
if (dataLength <= 0) {
resultField.length = dataLength;
- return;
+ return resultField.length;
}
}
resultField.start = scratch;
resultField.length = dataLength;
scratchPad.commit(dataLength);
+ return resultField.length;
}
normalizer { f1, f2, f3 },
normArg { f1Arg, f2Arg, f3Arg },
numNormalizers((f1 != nullptr) + (f1 != nullptr)*(f2 != nullptr) + (f1 != nullptr)*(f2 != nullptr)*(f3 != nullptr)) {};
- void normalize(ScratchPad &scratchPad, uint64_t &infractions, NHttpEnums::HeaderId headId, const NHttpEnums::HeaderId headerNameId[], const field headerName[], int32_t numHeaders,
+ int32_t normalize(NHttpEnums::HeaderId headId, ScratchPad &scratchPad, uint64_t &infractions, const NHttpEnums::HeaderId headerNameId[], const field headerName[], int32_t numHeaders,
field &resultField) const;
NHttpEnums::NormFormat getFormat() const {return format;};
using namespace NHttpEnums;
-THREAD_LOCAL NHttpMsgRequest* NHttpInspect::msgRequest;
-THREAD_LOCAL NHttpMsgStatus* NHttpInspect::msgStatus;
-THREAD_LOCAL NHttpMsgHeader* NHttpInspect::msgHead;
-THREAD_LOCAL NHttpMsgBody* NHttpInspect::msgBody;
-THREAD_LOCAL NHttpMsgChunkHead* NHttpInspect::msgChunkHead;
-THREAD_LOCAL NHttpMsgChunkBody* NHttpInspect::msgChunkBody;
-THREAD_LOCAL NHttpMsgTrailer* NHttpInspect::msgTrailer;
-
-NHttpInspect::NHttpInspect(bool test_input, bool _test_output) : test_output(_test_output)
+NHttpInspect::NHttpInspect(bool test_input, bool _test_output, bool _test_inspect) : test_output(_test_output), test_inspect(_test_inspect)
{
NHttpTestInput::test_input = test_input;
if (NHttpTestInput::test_input) {
NHttpInspect::~NHttpInspect ()
{
+ delete msgSection;
if (NHttpTestInput::test_input) {
delete NHttpTestInput::testInput;
if (testOut) fclose(testOut);
void NHttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const flow)
{
+ delete msgSection;
+ msgSection = nullptr;
+
NHttpFlowData* sessionData = (NHttpFlowData*)flow->get_application_data(NHttpFlowData::nhttp_flow_id);
assert(sessionData);
- NHttpMsgSection *msgSect = nullptr;
-
if (!NHttpTestInput::test_input) {
switch (sessionData->sectionType) {
- case SEC_REQUEST: msgSect = msgRequest; break;
- case SEC_STATUS: msgSect = msgStatus; break;
- case SEC_HEADER: msgSect = msgHead; break;
- case SEC_BODY: msgSect = msgBody; break;
- case SEC_CHUNKHEAD: msgSect = msgChunkHead; break;
- case SEC_CHUNKBODY: msgSect = msgChunkBody; break;
- case SEC_TRAILER: msgSect = msgTrailer; break;
+ case SEC_REQUEST: msgSection = new NHttpMsgRequest; break;
+ case SEC_STATUS: msgSection = new NHttpMsgStatus; break;
+ case SEC_HEADER: msgSection = new NHttpMsgHeader; break;
+ case SEC_BODY: msgSection = new NHttpMsgBody; break;
+ case SEC_CHUNKHEAD: msgSection = new NHttpMsgChunkHead; break;
+ case SEC_CHUNKBODY: msgSection = new NHttpMsgChunkBody; break;
+ case SEC_TRAILER: msgSection = new NHttpMsgTrailer; break;
case SEC_DISCARD: return;
default: assert(0); return;
}
- msgSect->loadSection(data, dsize, sessionData);
+ msgSection->loadSection(data, dsize, sessionData);
}
else {
uint8_t *testBuffer;
uint16_t testLength;
if ((testLength = NHttpTestInput::testInput->toEval(&testBuffer, testNumber)) > 0) {
switch (sessionData->sectionType) {
- case SEC_REQUEST: msgSect = msgRequest; break;
- case SEC_STATUS: msgSect = msgStatus; break;
- case SEC_HEADER: msgSect = msgHead; break;
- case SEC_BODY: msgSect = msgBody; break;
- case SEC_CHUNKHEAD: msgSect = msgChunkHead; break;
- case SEC_CHUNKBODY: msgSect = msgChunkBody; break;
- case SEC_TRAILER: msgSect = msgTrailer; break;
+ case SEC_REQUEST: msgSection = new NHttpMsgRequest; break;
+ case SEC_STATUS: msgSection = new NHttpMsgStatus; break;
+ case SEC_HEADER: msgSection = new NHttpMsgHeader; break;
+ case SEC_BODY: msgSection = new NHttpMsgBody; break;
+ case SEC_CHUNKHEAD: msgSection = new NHttpMsgChunkHead; break;
+ case SEC_CHUNKBODY: msgSection = new NHttpMsgChunkBody; break;
+ case SEC_TRAILER: msgSection = new NHttpMsgTrailer; break;
case SEC_DISCARD: return;
default: assert(0); return;
}
- msgSect->loadSection(testBuffer, testLength, sessionData);
+ msgSection->loadSection(testBuffer, testLength, sessionData);
}
else {
printf("Zero length test data.\n");
return;
}
}
- msgSect->initSection();
- msgSect->analyze();
- msgSect->updateFlow();
- msgSect->genEvents();
- msgSect->legacyClients();
+ msgSection->initSection();
+ msgSection->analyze();
+ msgSection->updateFlow();
+ msgSection->genEvents();
+ msgSection->legacyClients();
+ if (test_inspect) msgSection->analyzeAll();
if (test_output) {
- if (!NHttpTestInput::test_input) msgSect->printSection(stdout);
+ if (!NHttpTestInput::test_input) msgSection->printSection(stdout);
else {
if (testNumber != fileTestNumber) {
if (testOut) fclose (testOut);
snprintf(fileName, sizeof(fileName), "%s%" PRIi64 ".txt", testOutputPrefix, testNumber);
if ((testOut = fopen(fileName, "w+")) == nullptr) throw std::runtime_error("Cannot open test output file");
}
- msgSect->printSection(testOut);
+ msgSection->printSection(testOut);
}
}
}
class NHttpInspect : public Inspector {
public:
- NHttpInspect(bool test_input, bool _test_output);
+ NHttpInspect(bool test_input, bool _test_output, bool _test_inspect);
~NHttpInspect();
bool get_buf(unsigned, Packet*, InspectionBuffer&);
private:
friend NHttpApi;
- static THREAD_LOCAL NHttpMsgRequest *msgRequest;
- static THREAD_LOCAL NHttpMsgStatus *msgStatus;
- static THREAD_LOCAL NHttpMsgHeader *msgHead;
- static THREAD_LOCAL NHttpMsgBody *msgBody;
- static THREAD_LOCAL NHttpMsgChunkHead *msgChunkHead;
- static THREAD_LOCAL NHttpMsgChunkBody *msgChunkBody;
- static THREAD_LOCAL NHttpMsgTrailer *msgTrailer;
+ NHttpMsgSection *msgSection = nullptr;
void process(const uint8_t* data, const uint16_t dsize, Flow* const flow);
// Test mode
bool test_output;
+ bool test_inspect;
const char *testInputFile = "nhttp_test_msgs.txt";
const char *testOutputPrefix = "nhttpresults/testcase";
FILE *testOut = nullptr;
const Parameter NHttpModule::nhttpParams[] =
{{ "test_input", Parameter::PT_BOOL, nullptr, "false", "read HTTP messages from text file" },
{ "test_output", Parameter::PT_BOOL, nullptr, "false", "print out HTTP section data" },
+ { "test_inspect", Parameter::PT_BOOL, nullptr, "false", "force all possible inspections and normalizations" },
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }};
bool NHttpModule::begin(const char*, int, SnortConfig*) {
test_input = false;
test_output = false;
+ test_inspect = false;
return true;
}
bool NHttpModule::set(const char*, Value &val, SnortConfig*) {
if (val.is("test_input")) test_input = val.get_bool();
else if (val.is("test_output")) test_output = val.get_bool();
+ else if (val.is("test_inspect")) test_inspect = val.get_bool();
else return false;
return true;
unsigned get_gid() const;
bool get_test_input() const { return test_input; };
bool get_test_output() const { return test_output; };
+ bool get_test_inspect() const { return test_inspect; };
private:
static const Parameter nhttpParams[];
static const RuleMap nhttpEvents[];
bool test_input = false;
bool test_output = false;
+ bool test_inspect = false;
};
#endif
NHttpMsgSection::printMessageWrapup(output);
}
-void NHttpMsgBody::updateFlow() const {
+void NHttpMsgBody::updateFlow() {
if (tcpClose) {
sessionData->typeExpected[sourceId] = SEC_CLOSED;
sessionData->halfReset(sourceId);
// Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgBody::legacyClients() const {
+void NHttpMsgBody::legacyClients() {
ClearHttpBuffers();
if (data.length > 0) SetHttpBuffer(HTTP_BUFFER_CLIENT_BODY, data.start, (unsigned)data.length);
}
void analyze();
void printSection(FILE *output) const;
void genEvents();
- void updateFlow() const;
- void legacyClients() const;
+ void updateFlow();
+ void legacyClients();
protected:
int64_t dataLength;
if (tcpClose) infractions |= INF_TRUNCATED;
}
+
void NHttpMsgChunkBody::genEvents() {
if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
}
NHttpMsgSection::printMessageWrapup(output);
}
-void NHttpMsgChunkBody::updateFlow() const {
+void NHttpMsgChunkBody::updateFlow() {
if (tcpClose) {
sessionData->typeExpected[sourceId] = SEC_CLOSED;
sessionData->halfReset(sourceId);
void analyze();
void printSection(FILE *output) const;
void genEvents();
- void updateFlow() const;
+ void updateFlow();
private:
int64_t numChunks;
// vastly bigger than any legitimate chunk.
void NHttpMsgChunkHead::deriveChunkLength() {
if (chunkSize.length <= 0) {
- dataLength = STAT_PROBLEMATIC;
+ dataLength = STAT_NOSOURCE;
infractions |= INF_BADCHUNKSIZE;
return;
}
NHttpMsgSection::printMessageWrapup(output);
}
-void NHttpMsgChunkHead::updateFlow() const {
+void NHttpMsgChunkHead::updateFlow() {
if (tcpClose) {
sessionData->typeExpected[sourceId] = SEC_CLOSED;
sessionData->halfReset(sourceId);
// Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgChunkHead::legacyClients() const {
+void NHttpMsgChunkHead::legacyClients() {
ClearHttpBuffers();
if (startLine.length > 0) SetHttpBuffer(HTTP_BUFFER_CLIENT_BODY, startLine.start, (unsigned)startLine.length);
}
void analyze();
void printSection(FILE *output) const;
void genEvents();
- void updateFlow() const;
- void legacyClients() const;
+ void updateFlow();
+ void legacyClients();
private:
void deriveChunkLength();
NHttpMsgSection::printMessageWrapup(output);
}
-void NHttpMsgHeader::updateFlow() const {
+void NHttpMsgHeader::updateFlow() {
const uint64_t disasterMask = 0;
+ ;
+ headerNorms[HEAD_CONTENT_LENGTH]->normalize(HEAD_CONTENT_LENGTH, scratchPad, infractions, headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_CONTENT_LENGTH]);
+
// The following logic to determine body type is by no means the last word on this topic.
if (tcpClose) {
sessionData->typeExpected[sourceId] = SEC_CLOSED;
sessionData->halfReset(sourceId);
}
// If there is a Transfer-Encoding header, see if the last of the encoded values is "chunked".
- else if ( (headerValueNorm[HEAD_TRANSFER_ENCODING].length > 0) &&
- ((*(int64_t *)(headerValueNorm[HEAD_TRANSFER_ENCODING].start + (headerValueNorm[HEAD_TRANSFER_ENCODING].length - 8))) == TRANSCODE_CHUNKED) ) {
+ else if ( (headerNorms[HEAD_TRANSFER_ENCODING]->normalize(HEAD_TRANSFER_ENCODING, scratchPad, infractions,
+ headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_TRANSFER_ENCODING]) > 0) &&
+ ((*(int64_t *)(headerValueNorm[HEAD_TRANSFER_ENCODING].start + (headerValueNorm[HEAD_TRANSFER_ENCODING].length - 8))) == TRANSCODE_CHUNKED) ) {
// Chunked body
sessionData->typeExpected[sourceId] = SEC_CHUNKHEAD;
sessionData->bodySections[sourceId] = 0;
sessionData->bodyOctets[sourceId] = 0;
sessionData->numChunks[sourceId] = 0;
}
- else if ((headerValueNorm[HEAD_CONTENT_LENGTH].length > 0) && (*(int64_t*)headerValueNorm[HEAD_CONTENT_LENGTH].start > 0)) {
+ else if ((headerNorms[HEAD_CONTENT_LENGTH]->normalize(HEAD_CONTENT_LENGTH, scratchPad, infractions,
+ headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_CONTENT_LENGTH]) > 0) &&
+ (*(int64_t*)headerValueNorm[HEAD_CONTENT_LENGTH].start > 0)) {
// Regular body
sessionData->typeExpected[sourceId] = SEC_BODY;
sessionData->octetsExpected[sourceId] = *(int64_t*)headerValueNorm[HEAD_CONTENT_LENGTH].start;
NHttpMsgHeader() {};
void printSection(FILE *output) const;
void genEvents();
- void updateFlow() const;
+ void updateFlow();
};
#endif
headerValue[k].length = STAT_NOTCOMPUTE;
}
for (int k = 1; k < HEAD__MAXVALUE; k++) {
- headerValueNorm[k].length = STAT_NOTCOMPUTE;
+ headerValueNorm[k].length = STAT_NOSOURCE;
}
}
for (int j=0; j < MAXHEADERS; j++) {
if (headerName[j].length <= 0) break;
deriveHeaderNameId(j);
+ // Mark this header field as present and therefore eligible for normalization
+ if (headerNameId[j] > 0) headerValueNorm[headerNameId[j]].length = STAT_NOTCOMPUTE;
}
+}
+
+void NHttpMsgHeadShared::analyzeAll() {
for (int k=1; k <= numNorms; k++) {
- headerNorms[k]->normalize(scratchPad, infractions, (HeaderId)k, headerNameId, headerValue, MAXHEADERS, headerValueNorm[k]);
+ headerNorms[k]->normalize((HeaderId)k, scratchPad, infractions, headerNameId, headerValue, MAXHEADERS, headerValueNorm[k]);
}
}
// Divide up the block of header fields into individual header field lines.
void NHttpMsgHeadShared::parseHeaderBlock() {
- if (headers.length <= 0) return;
- int32_t bytesused = 0;
+ if (headers.length < 0) {
+ numHeaders = STAT_NOSOURCE;
+ return;
+ }
+
+ int32_t bytesUsed = 0;
numHeaders = 0;
- while (bytesused < headers.length) {
- headerLine[numHeaders].start = headers.start + bytesused;
- headerLine[numHeaders].length = findCrlf(headerLine[numHeaders].start, headers.length - bytesused, true);
- bytesused += headerLine[numHeaders++].length + 2;
+ while (bytesUsed < headers.length) {
+ headerLine[numHeaders].start = headers.start + bytesUsed;
+ headerLine[numHeaders].length = findCrlf(headerLine[numHeaders].start, headers.length - bytesUsed, true);
+ bytesUsed += headerLine[numHeaders++].length + 2;
if (numHeaders >= MAXHEADERS) {
break;
}
}
- if (bytesused < headers.length) {
+ if (bytesUsed < headers.length) {
infractions |= INF_TOOMANYHEADERS;
}
}
}
void NHttpMsgHeadShared::deriveHeaderNameId(int index) {
- if (headerName[index].length <= 0) return;
// Normalize header field name to lower case for matching purposes
uint8_t *lowerName;
if ((lowerName = scratchPad.request(headerName[index].length)) == nullptr) {
}
// Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgHeadShared::legacyClients() const {
+void NHttpMsgHeadShared::legacyClients() {
ClearHttpBuffers();
if (headers.length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_HEADER, headers.start, (unsigned)headers.length);
}
}
- if ((sourceId == SRC_CLIENT) && (headerValueNorm[HEAD_COOKIE].length > 0))
- SetHttpBuffer(HTTP_BUFFER_COOKIE, headerValueNorm[HEAD_COOKIE].start, (unsigned)headerValueNorm[HEAD_COOKIE].length);
- else if ((sourceId == SRC_SERVER) && (headerValueNorm[HEAD_SET_COOKIE].length > 0))
- SetHttpBuffer(HTTP_BUFFER_COOKIE, headerValueNorm[HEAD_SET_COOKIE].start, (unsigned)headerValueNorm[HEAD_SET_COOKIE].length);
+ if (sourceId == SRC_CLIENT) {
+ if (headerNorms[HEAD_COOKIE]->normalize(HEAD_COOKIE, scratchPad, infractions, headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_COOKIE]) > 0) {
+ SetHttpBuffer(HTTP_BUFFER_COOKIE, headerValueNorm[HEAD_COOKIE].start, (unsigned)headerValueNorm[HEAD_COOKIE].length);
+ }
+ }
+ else {
+ if (headerNorms[HEAD_SET_COOKIE]->normalize(HEAD_SET_COOKIE, scratchPad, infractions, headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_SET_COOKIE]) > 0) {
+ SetHttpBuffer(HTTP_BUFFER_COOKIE, headerValueNorm[HEAD_SET_COOKIE].start, (unsigned)headerValueNorm[HEAD_SET_COOKIE].length);
+ }
+ }
}
void NHttpMsgHeadShared::printHeaders(FILE *output) const {
char titleBuf[100];
- if (numHeaders != STAT_NOTCOMPUTE) fprintf(output, "Number of headers: %d\n", numHeaders);
+ if (numHeaders != STAT_NOSOURCE) fprintf(output, "Number of headers: %d\n", numHeaders);
for (int j=0; j < numHeaders && j < 200; j++) {
snprintf(titleBuf, sizeof(titleBuf), "Header ID %d", headerNameId[j]);
printInterval(output, titleBuf, headerValue[j].start, headerValue[j].length);
}
for (int k=1; k <= numNorms; k++) {
- if (headerValueNorm[k].length != STAT_NOTPRESENT) {
+ if (headerValueNorm[k].length != STAT_NOSOURCE) {
snprintf(titleBuf, sizeof(titleBuf), "Normalized header %d", k);
printInterval(output, titleBuf, headerValueNorm[k].start, headerValueNorm[k].length, true);
}
public:
void initSection();
void analyze();
+ void analyzeAll();
void genEvents();
- void legacyClients() const;
+ void legacyClients();
protected:
// Header normalization. There should be one of these for every different way we can process a header field value.
using namespace NHttpEnums;
-const UriNormalizer NHttpMsgRequest::uriNoPath { false };
-const UriNormalizer NHttpMsgRequest::uriPath { true };
-
// Reinitialize everything derived in preparation for analyzing a new message
void NHttpMsgRequest::initSection() {
NHttpMsgStart::initSection();
method.length = STAT_NOTCOMPUTE;
- uri.length = STAT_NOTCOMPUTE;
- uriLegacyNorm.length = STAT_NOTCOMPUTE;
- uriType = URI__NOTCOMPUTE;
- scheme.length = STAT_NOTCOMPUTE;
- schemeId = SCH__NOTCOMPUTE;
- host.length = STAT_NOTCOMPUTE;
- hostInfractions = 0;
- hostNorm.length = STAT_NOTCOMPUTE;
- port.length = STAT_NOTCOMPUTE;
- portValue = STAT_NOTCOMPUTE;
- path.length = STAT_NOTCOMPUTE;
- pathInfractions = 0;
- pathNorm.length = STAT_NOTCOMPUTE;
- query.length = STAT_NOTCOMPUTE;
- queryInfractions = 0;
- queryNorm.length = STAT_NOTCOMPUTE;
- fragment.length = STAT_NOTCOMPUTE;
- fragmentInfractions = 0;
- fragmentNorm.length = STAT_NOTCOMPUTE;
-}
-
-// All the processing that is done for every message (i.e. not just-in-time) is done here.
-void NHttpMsgRequest::analyze() {
- NHttpMsgStart::analyze();
- deriveMethodId();
- parseUri();
- deriveSchemeId();
- parseAuthority();
- derivePortValue();
- parseAbsPath();
- uriPath.normalize(path, pathNorm, scratchPad, pathInfractions);
- uriNoPath.normalize(host, hostNorm, scratchPad, hostInfractions);
- uriNoPath.normalize(query, queryNorm, scratchPad, queryInfractions);
- uriNoPath.normalize(fragment, fragmentNorm, scratchPad, fragmentInfractions);
- makeLegacyNormUri();
+ delete uri;
+ uri = nullptr;
}
void NHttpMsgRequest::parseStartLine() {
method.start = startLine.start;
method.length = space;
- uri.start = startLine.start + method.length + 1;
- uri.length = startLine.length - method.length - 10;
+ deriveMethodId();
+ uri = new NHttpUri(startLine.start + method.length + 1, startLine.length - method.length - 10, methodId);
version.start = startLine.start + (startLine.length - 8);
version.length = 8;
- assert (startLine.length == method.length + uri.length + version.length + 2);
+ assert (startLine.length == method.length + uri->getUri().length + version.length + 2);
}
void NHttpMsgRequest::deriveMethodId() {
- methodId = (MethodId) strToCode(method.start, method.length, methodList);
-}
-
-void NHttpMsgRequest::parseUri() {
- if (uriType != URI__NOTCOMPUTE) return;
- if (uri.length <= 0) {
- uriType = URI__NOTPRESENT;
+ if (method.length <= 0) {
+ methodId = METH__NOSOURCE;
return;
}
-
- // Four basic types of HTTP URI
- // "*" means request does not apply to any specific resource
- if ((uri.length == 1) && (uri.start[0] == '*')) {
- uriType = URI_ASTERISK;
- scheme.length = STAT_NOTPRESENT;
- authority.length = STAT_NOTPRESENT;
- absPath.length = STAT_NOTPRESENT;
- }
- // CONNECT method uses an authority
- else if (methodId == METH_CONNECT) {
- uriType = URI_AUTHORITY;
- scheme.length = STAT_NOTPRESENT;
- authority.length = uri.length;
- authority.start = uri.start;
- absPath.length = STAT_NOTPRESENT;
- }
- // Absolute path is a path but no scheme or authority
- else if (uri.start[0] == '/') {
- uriType = URI_ABSPATH;
- scheme.length = STAT_NOTPRESENT;
- authority.length = STAT_NOTPRESENT;
- absPath.length = uri.length;
- absPath.start = uri.start;
- }
- // Absolute URI includes scheme, authority, and path
- else {
- // Find the "://" and then the "/"
- int j;
- int k;
- for (j = 0; (uri.start[j] != ':') && (j < uri.length); j++);
- for (k = j+3; (uri.start[k] != '/') && (k < uri.length); k++);
- if ((k < uri.length) && (uri.start[j+1] == '/') && (uri.start[j+2] == '/')) {
- uriType = URI_ABSOLUTE;
- scheme.length = j;
- scheme.start = uri.start;
- authority.length = k - j - 3;
- authority.start = uri.start + j + 3;
- absPath.length = uri.length - k;
- absPath.start = uri.start + k;
- }
- else {
- infractions |= INF_BADURI;
- uriType = URI__PROBLEMATIC;
- scheme.length = STAT_PROBLEMATIC;
- authority.length = STAT_PROBLEMATIC;
- absPath.length = STAT_PROBLEMATIC;
- }
- }
-}
-
-void NHttpMsgRequest::deriveSchemeId() {
- if (schemeId != SCH__NOTCOMPUTE) return;
- if (scheme.length <= 0) return;
-
- // Normalize scheme name to lower case for matching purposes
- uint8_t *lowerScheme;
- if ((lowerScheme = scratchPad.request(scheme.length)) == nullptr) {
- infractions |= INF_NOSCRATCH;
- schemeId = SCH__INSUFMEMORY;
- return;
- }
- norm2Lower(scheme.start, scheme.length, lowerScheme, infractions, nullptr);
- schemeId = (SchemeId) strToCode(lowerScheme, scheme.length, schemeList);
-}
-
-void NHttpMsgRequest::parseAuthority() {
- if (host.length != STAT_NOTCOMPUTE) return;
- if (authority.length <= 0) return;
- host.start = authority.start;
- for (host.length = 0; (authority.start[host.length] != ':') && (host.length < authority.length); host.length++);
- if (host.length < authority.length) {
- port.length = authority.length - host.length - 1;
- port.start = authority.start + host.length + 1;
- }
- else port.length = STAT_NOTPRESENT;
-}
-
-void NHttpMsgRequest::derivePortValue() {
- if (portValue != SCH__NOTCOMPUTE) return;
- if (port.length <= 0) return;
- portValue = 0;
- for (int k = 0; k < port.length; k++) {
- portValue = portValue * 10 + (port.start[k] - '0');
- if ((port.start[k] < '0') || (port.start[k] > '9') || (portValue > 65535))
- {
- infractions |= INF_BADPORT;
- portValue = STAT_PROBLEMATIC;
- break;
- }
- }
-}
-
-void NHttpMsgRequest::parseAbsPath() {
- if (path.length != STAT_NOTCOMPUTE) return;
- if (absPath.length <= 0) return;
- path.start = absPath.start;
- for (path.length = 0; (absPath.start[path.length] != '?') && (absPath.start[path.length] != '#') && (path.length < absPath.length); path.length++);
- if (path.length == absPath.length) {
- query.length = STAT_NOTPRESENT;
- fragment.length = STAT_NOTPRESENT;
- return;
- }
- if (absPath.start[path.length] == '?') {
- query.start = absPath.start + path.length + 1;
- for (query.length = 0; (query.start[query.length] != '#') && (query.length < absPath.length - path.length - 1); query.length++);
- fragment.start = query.start + query.length + 1;
- fragment.length = absPath.length - path.length - 1 - query.length - 1;
- }
- else {
- query.length = STAT_NOTPRESENT;
- fragment.start = absPath.start + path.length + 1;
- fragment.length = absPath.length - path.length - 1;
- }
+ methodId = (MethodId) strToCode(method.start, method.length, methodList);
}
void NHttpMsgRequest::genEvents() {
void NHttpMsgRequest::printSection(FILE *output) const {
NHttpMsgSection::printMessageTitle(output, "request line");
- if (versionId != VERS__NOTCOMPUTE) fprintf(output, "Version Id: %d\n", versionId);
- if (methodId != METH__NOTCOMPUTE) fprintf(output, "Method Id: %d\n", methodId);
- printInterval(output, "URI", uri.start, uri.length);
- if (uriType != URI__NOTCOMPUTE) fprintf(output, "URI Type: %d\n", uriType);
- printInterval(output, "Scheme", scheme.start, scheme.length);
- if (schemeId != SCH__NOTCOMPUTE) fprintf(output, "Scheme Id: %d\n", schemeId);
- printInterval(output, "Authority", authority.start, authority.length);
- printInterval(output, "Host Name", host.start, host.length);
- printInterval(output, "Normalized Host Name", hostNorm.start, hostNorm.length);
- printInterval(output, "Port", port.start, port.length);
- if (portValue != STAT_NOTCOMPUTE) fprintf(output, "Port Value: %d\n", portValue);
- printInterval(output, "Absolute Path", absPath.start, absPath.length);
- printInterval(output, "Path", path.start, path.length);
- printInterval(output, "Normalized Path", pathNorm.start, pathNorm.length);
- printInterval(output, "Query", query.start, query.length);
- printInterval(output, "Normalized Query", queryNorm.start, queryNorm.length);
- printInterval(output, "Fragment", fragment.start, fragment.length);
- printInterval(output, "Normalized Fragment", fragmentNorm.start, fragmentNorm.length);
- fprintf(output, "URI infractions: host %" PRIx64 ", path %" PRIx64 ", query %" PRIx64 ", fragment %" PRIx64 "\n",
- hostInfractions, pathInfractions, queryInfractions, fragmentInfractions);
+ fprintf(output, "Version Id: %d\n", versionId);
+ fprintf(output, "Method Id: %d\n", methodId);
+ printInterval(output, "URI", uri->getUri().start, uri->getUri().length);
+ if ((uri->getUriType() != URI__NOTCOMPUTE) && (uri->getUriType() != URI__NOSOURCE)) fprintf(output, "URI Type: %d\n", uri->getUriType());
+ printInterval(output, "Scheme", uri->getScheme().start, uri->getScheme().length);
+ if ((uri->getSchemeId() != SCH__NOTCOMPUTE) && (uri->getSchemeId() != SCH__NOSOURCE)) fprintf(output, "Scheme Id: %d\n", uri->getSchemeId());
+ printInterval(output, "Authority", uri->getAuthority().start, uri->getAuthority().length);
+ printInterval(output, "Host Name", uri->getHost().start, uri->getHost().length);
+ printInterval(output, "Normalized Host Name", uri->getNormHost().start, uri->getNormHost().length);
+ printInterval(output, "Port", uri->getPort().start, uri->getPort().length);
+ if ((uri->getPortValue() != STAT_NOTCOMPUTE) && (uri->getPortValue() != STAT_NOSOURCE)) fprintf(output, "Port Value: %d\n", uri->getPortValue());
+ printInterval(output, "Absolute Path", uri->getAbsPath().start, uri->getAbsPath().length);
+ printInterval(output, "Path", uri->getPath().start, uri->getPath().length);
+ printInterval(output, "Normalized Path", uri->getNormPath().start, uri->getNormPath().length);
+ printInterval(output, "Query", uri->getQuery().start, uri->getQuery().length);
+ printInterval(output, "Normalized Query", uri->getNormQuery().start, uri->getNormQuery().length);
+ printInterval(output, "Fragment", uri->getFragment().start, uri->getFragment().length);
+ printInterval(output, "Normalized Fragment", uri->getNormFragment().start, uri->getNormFragment().length);
+ fprintf(output, "URI infractions: overall %" PRIx64 ", host %" PRIx64 ", path %" PRIx64 ", query %" PRIx64 ", fragment %" PRIx64 "\n",
+ uri->getUriInfractions(), uri->getHostInfractions(), uri->getPathInfractions(), uri->getQueryInfractions(),
+ uri->getFragmentInfractions());
NHttpMsgSection::printMessageWrapup(output);
-}
+ }
-void NHttpMsgRequest::updateFlow() const {
+void NHttpMsgRequest::updateFlow() {
const uint64_t disasterMask = INF_BADREQLINE;
// The following logic to determine body type is by no means the last word on this topic.
sessionData->typeExpected[sourceId] = SEC_HEADER;
sessionData->versionId[sourceId] = versionId;
sessionData->methodId[sourceId] = methodId;
- sessionData->schemeId[sourceId] = schemeId;
- }
-}
-
-// Glue normalized URI fields back together
-void NHttpMsgRequest::makeLegacyNormUri() {
- if (uriLegacyNorm.length != STAT_NOTCOMPUTE) return;
-
- // We can reuse the raw URI for the normalized URI unless at least one part of the URI has been normalized
- if ((hostInfractions == 0) && (pathInfractions == 0) && (queryInfractions == 0) && (fragmentInfractions == 0)) {
- uriLegacyNorm.start = uri.start;
- uriLegacyNorm.length = uri.length;
- return;
- }
-
- // Glue normalized path pieces back together
- const uint32_t totalLength = ((scheme.length >= 0) ? scheme.length + 3 : 0) +
- ((hostNorm.length >= 0) ? hostNorm.length : 0) +
- ((port.length >= 0) ? port.length + 1 : 0) +
- ((pathNorm.length >= 0) ? pathNorm.length : 0) +
- ((queryNorm.length >= 0) ? queryNorm.length + 1 : 0) +
- ((fragmentNorm.length >= 0) ? fragmentNorm.length + 1 : 0);
- uint8_t* const scratch = scratchPad.request(totalLength);
- if (scratch != nullptr) {
- uint8_t *current = scratch;
- if (scheme.length >= 0) {
- memcpy(current, scheme.start, scheme.length);
- current += scheme.length;
- memcpy(current, "://", 3);
- current += 3;
- }
- if (hostNorm.length >= 0) {
- memcpy(current, hostNorm.start, hostNorm.length);
- current += hostNorm.length;
- }
- if (port.length >= 0) {
- memcpy(current, ":", 1);
- current += 1;
- memcpy(current, port.start, port.length);
- current += port.length;
- }
- if (pathNorm.length >= 0) {
- memcpy(current, pathNorm.start, pathNorm.length);
- current += pathNorm.length;
- }
- if (queryNorm.length >= 0) {
- memcpy(current, "?", 1);
- current += 1;
- memcpy(current, queryNorm.start, queryNorm.length);
- current += queryNorm.length;
- }
- if (fragmentNorm.length >= 0) {
- memcpy(current, "#", 1);
- current += 1;
- memcpy(current, fragmentNorm.start, fragmentNorm.length);
- current += fragmentNorm.length;
- }
- assert(totalLength == current - scratch);
- scratchPad.commit(current - scratch);
- uriLegacyNorm.start = scratch;
- uriLegacyNorm.length = current - scratch;
}
- else uriLegacyNorm.length = STAT_INSUFMEMORY;
}
// Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgRequest::legacyClients() const {
+void NHttpMsgRequest::legacyClients() {
ClearHttpBuffers();
if (method.length > 0) SetHttpBuffer(HTTP_BUFFER_METHOD, method.start, (unsigned)method.length);
- if (uri.length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_URI, uri.start, (unsigned)uri.length);
- if (uriLegacyNorm.length > 0) SetHttpBuffer(HTTP_BUFFER_URI, uriLegacyNorm.start, (unsigned)uriLegacyNorm.length);
+ if (uri->getUri().length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_URI, uri->getUri().start, (unsigned)uri->getUri().length);
+ if (uri->getNormLegacy().length > 0) SetHttpBuffer(HTTP_BUFFER_URI, uri->getNormLegacy().start, (unsigned)uri->getNormLegacy().length);
}
#define NHTTP_MSG_REQUEST_H
#include "nhttp_str_to_code.h"
+#include "nhttp_uri.h"
#include "nhttp_uri_norm.h"
#include "nhttp_msg_start.h"
class NHttpMsgRequest: public NHttpMsgStart {
public:
NHttpMsgRequest() {};
+ ~NHttpMsgRequest() { delete uri; };
void initSection();
- void analyze();
void printSection(FILE *output) const;
void genEvents();
- void updateFlow() const;
- void legacyClients() const;
+ void updateFlow();
+ void legacyClients();
private:
// Code conversion tables are for turning token strings into enums.
static const StrCode methodList[];
- static const StrCode schemeList[];
- // URI normalization strategy objects
- static const UriNormalizer uriNoPath;
- static const UriNormalizer uriPath;
-
- // "Parse" methods cut things into pieces. "Derive" methods convert things into a new format such as an integer or enum token. "Normalize" methods convert
- // things into a standard form without changing the underlying format.
+ // "Parse" methods cut things into pieces. "Extract" methods find the named item. "Derive" methods convert things into a new format
+ // such as an integer or enum token. "Normalize" methods convert things into a standard form without changing the underlying format.
void parseStartLine();
void deriveMethodId();
- void parseUri();
- void deriveSchemeId();
- void parseAuthority();
- void derivePortValue();
- void parseAbsPath();
- void makeLegacyNormUri();
// This is where all the derived values, extracted message parts, and normalized values are.
// Note that these are all scalars, buffer pointers, and buffer sizes. The actual buffers are in the message buffer (raw pieces) or the
// scratchPad (normalized pieces).
field method;
-
- // URI stuff
- field uri;
- field scheme;
- field authority;
- field host;
- field port;
- field absPath;
- field path;
- field query;
- field fragment;
-
- uint64_t hostInfractions;
- uint64_t pathInfractions;
- uint64_t queryInfractions;
- uint64_t fragmentInfractions;
-
- NHttpEnums::UriType uriType;
- field hostNorm;
- int32_t portValue;
- field pathNorm;
- field queryNorm;
- field fragmentNorm;
- field uriLegacyNorm;
+ NHttpUri* uri = nullptr;
};
#endif
tcpClose = sessionData->tcpClose;
versionId = sessionData->versionId[sourceId];
methodId = sessionData->methodId[sourceId];
- schemeId = sessionData->schemeId[sourceId];
statusCodeNum = sessionData->statusCodeNum[sourceId];
scratchPad.reinit();
}
void NHttpMsgSection::printInterval(FILE *output, const char* name, const uint8_t *text, int32_t length, bool intVals) {
- if ((length == STAT_NOTPRESENT) || (length == STAT_NOTCOMPUTE)) return;
+ if ((length == STAT_NOTPRESENT) || (length == STAT_NOTCOMPUTE) || (length == STAT_NOSOURCE)) return;
int outCount = fprintf(output, "%s, length = %d, ", name, length);
if (length <= 0) {
fprintf(output, "\n");
virtual void loadSection(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_);
virtual ~NHttpMsgSection() = default;
virtual void initSection() = 0;
- virtual void analyze() = 0;
+ virtual void analyze() = 0; // Minimum necessary processing for every message
+ virtual void analyzeAll() {}; // Force all just-in-time processing (testing method)
virtual void printSection(FILE *output) const = 0;
virtual void genEvents() = 0;
- virtual void updateFlow() const = 0;
- virtual void legacyClients() const = 0;
+ virtual void updateFlow() = 0;
+ virtual void legacyClients() = 0;
protected:
// Convenience methods
// original message should not be changing it. Only loading a completely new message into rawBuf should do that.
const uint8_t * const msgText = rawBuf;
- // Working space and storage for all the derived fields. See scratchPad.h for usage instructions.
- uint64_t derivedBuf[NHttpEnums::MAXOCTETS/4];
NHttpFlowData* sessionData;
- ScratchPad scratchPad {derivedBuf, NHttpEnums::MAXOCTETS/4};
+ ScratchPad scratchPad {NHttpEnums::MAXOCTETS*2};
// This is where all the derived values, extracted message parts, and normalized values are.
// Note that these are all scalars, buffer pointers, and buffer sizes. The actual buffers are in message buffer (raw pieces) or the
NHttpEnums::SourceId sourceId;
NHttpEnums::VersionId versionId;
NHttpEnums::MethodId methodId;
- NHttpEnums::SchemeId schemeId;
int32_t statusCodeNum;
};
version.length = STAT_NOTCOMPUTE;
}
-// All the processing that is done for every message (i.e. not just-in-time) is done here.
+// Required message processing that is automatically done instead of being just-in-time
void NHttpMsgStart::analyze() {
startLine.start = msgText;
startLine.length = findCrlf(startLine.start, length, false);
}
void NHttpMsgStart::deriveVersionId() {
+ if (version.length <= 0) {
+ versionId = VERS__NOSOURCE;
+ return;
+ }
if (version.length != 8) {
versionId = VERS__PROBLEMATIC;
+ infractions |= INF_BADVERSION;
return;
}
+
if (memcmp(version.start, "HTTP/", 5) || (version.start[6] != '.')) {
versionId = VERS__PROBLEMATIC;
infractions |= INF_BADVERSION;
}
void NHttpMsgStatus::deriveStatusCodeNum() {
+ if (statusCode.length <= 0) {
+ statusCodeNum = STAT_NOSOURCE;
+ return;
+ }
if (statusCode.length != 3) {
statusCodeNum = STAT_PROBLEMATIC;
return;
}
+
if ((statusCode.start[0] < '0') || (statusCode.start[0] > '9') || (statusCode.start[1] < '0') || (statusCode.start[1] > '9') ||
(statusCode.start[2] < '0') || (statusCode.start[2] > '9')) {
infractions |= INF_BADSTATCODE;
void NHttpMsgStatus::printSection(FILE *output) const {
NHttpMsgSection::printMessageTitle(output, "status line");
- if (versionId != VERS__NOTCOMPUTE) fprintf(output, "Version Id: %d\n", versionId);
- if (statusCodeNum != STAT_NOTCOMPUTE) fprintf(output, "Status Code Num: %d\n", statusCodeNum);
+ fprintf(output, "Version Id: %d\n", versionId);
+ fprintf(output, "Status Code Num: %d\n", statusCodeNum);
printInterval(output, "Reason Phrase", reasonPhrase.start, reasonPhrase.length);
NHttpMsgSection::printMessageWrapup(output);
}
-void NHttpMsgStatus::updateFlow() const {
+void NHttpMsgStatus::updateFlow() {
const uint64_t disasterMask = INF_BADSTATLINE;
// The following logic to determine body type is by no means the last word on this topic.
}
// Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgStatus::legacyClients() const {
+void NHttpMsgStatus::legacyClients() {
ClearHttpBuffers();
if (statusCode.length > 0) SetHttpBuffer(HTTP_BUFFER_STAT_CODE, statusCode.start, (unsigned)statusCode.length);
if (reasonPhrase.length > 0) SetHttpBuffer(HTTP_BUFFER_STAT_MSG, reasonPhrase.start, (unsigned)reasonPhrase.length);
void analyze();
void printSection(FILE *output) const;
void genEvents();
- void updateFlow() const;
- void legacyClients() const;
+ void updateFlow();
+ void legacyClients();
private:
// "Parse" methods cut things into pieces. "Derive" methods convert things into a new format such as an integer or enum token. "Normalize" methods convert
}
-void NHttpMsgTrailer::updateFlow() const {
+void NHttpMsgTrailer::updateFlow() {
if (tcpClose) {
sessionData->typeExpected[sourceId] = SEC_CLOSED;
sessionData->halfReset(sourceId);
NHttpMsgTrailer() {};
void printSection(FILE *output) const;
void genEvents();
- void updateFlow() const;
+ void updateFlow();
};
#endif
class ScratchPad {
public:
- ScratchPad(uint64_t *buff, uint32_t length) : buffer(buff), capacity(length*8), used(0) {}; // Careful: length must be number of uint64_ts provided, not octets.
+ ScratchPad(uint32_t _capacity) : capacity(_capacity), buffer(new uint64_t[_capacity/8+1]) {};
+ ~ScratchPad() { delete[] buffer; };
void reinit() {used = 0;};
uint8_t *request(uint32_t needed) const {return (needed <= capacity-used) ? (uint8_t*)(buffer+used) : nullptr;};
void commit(uint32_t taken) { used += taken + (8-(taken%8))%8; }; // round up to multiple of 8 to preserve alignment
private:
- uint64_t *buffer;
uint32_t capacity;
- uint32_t used;
+ uint64_t *buffer;
+ uint32_t used = 0;
};
#endif
// Need to replace this simple algorithm for better performance
int32_t strToCode(const uint8_t *text, int32_t textLen, const StrCode table[]) {
- if (textLen <= 0) return NHttpEnums::STAT_PROBLEMATIC;
for (int32_t k=0; table[k].name != nullptr; k++) {
if ((textLen == (int) strlen(table[k].name)) && (memcmp(text, table[k].name, textLen) == 0)) {
return table[k].code;
{ METH_UPDATEREDIRECTREF, "UPDATEREDIRECTREF"},
{ 0, nullptr} };
-const StrCode NHttpMsgRequest::schemeList[] =
+const StrCode NHttpUri::schemeList[] =
{{ SCH_HTTP, "http"},
{ SCH_HTTPS, "https"},
{ SCH_FTP, "ftp"},
{ HEAD_CONTENT_TYPE, "content-type"},
{ HEAD_EXPIRES, "expires"},
{ HEAD_LAST_MODIFIED, "last-modified"},
+ { HEAD_X_FORWARDED_FOR, "x-forwarded-for"},
+ { HEAD_TRUE_CLIENT_IP, "true-client-ip"},
{ 0, nullptr} };
const StrCode NHttpMsgHeadShared::transCodeList[] =
[HEAD_CONTENT_RANGE] = &NORMALIZER_BASIC,
[HEAD_CONTENT_TYPE] = &NORMALIZER_BASIC,
[HEAD_EXPIRES] = &NORMALIZER_BASIC,
- [HEAD_LAST_MODIFIED] = &NORMALIZER_BASIC
+ [HEAD_LAST_MODIFIED] = &NORMALIZER_BASIC,
+ [HEAD_X_FORWARDED_FOR] = &NORMALIZER_CAT,
+ [HEAD_TRUE_CLIENT_IP] = &NORMALIZER_BASIC
};
const int32_t NHttpMsgHeadShared::numNorms = HEAD__MAXVALUE-1;
--- /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 NHttpUri class
+//
+
+
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "snort.h"
+#include "nhttp_enum.h"
+#include "nhttp_normalizers.h"
+#include "nhttp_uri.h"
+
+using namespace NHttpEnums;
+
+void NHttpUri::parseUri() {
+ if (uriType != URI__NOTCOMPUTE) return;
+ if (uri.length <= 0) {
+ uriType = URI__NOSOURCE;
+ scheme.length = STAT_NOSOURCE;
+ authority.length = STAT_NOSOURCE;
+ absPath.length = STAT_NOSOURCE;
+ return;
+ }
+
+ // Four basic types of HTTP URI
+ // "*" means request does not apply to any specific resource
+ if ((uri.length == 1) && (uri.start[0] == '*')) {
+ uriType = URI_ASTERISK;
+ scheme.length = STAT_NOTPRESENT;
+ authority.length = STAT_NOTPRESENT;
+ absPath.length = STAT_NOTPRESENT;
+ }
+ // CONNECT method uses an authority
+ else if (methodId == METH_CONNECT) {
+ uriType = URI_AUTHORITY;
+ scheme.length = STAT_NOTPRESENT;
+ authority.length = uri.length;
+ authority.start = uri.start;
+ absPath.length = STAT_NOTPRESENT;
+ }
+ // Absolute path is a path but no scheme or authority
+ else if (uri.start[0] == '/') {
+ uriType = URI_ABSPATH;
+ scheme.length = STAT_NOTPRESENT;
+ authority.length = STAT_NOTPRESENT;
+ absPath.length = uri.length;
+ absPath.start = uri.start;
+ }
+ // Absolute URI includes scheme, authority, and path
+ else {
+ // Find the "://" and then the "/"
+ int j;
+ int k;
+ for (j = 0; (uri.start[j] != ':') && (j < uri.length); j++);
+ for (k = j+3; (uri.start[k] != '/') && (k < uri.length); k++);
+ if ((k < uri.length) && (uri.start[j+1] == '/') && (uri.start[j+2] == '/')) {
+ uriType = URI_ABSOLUTE;
+ scheme.length = j;
+ scheme.start = uri.start;
+ authority.length = k - j - 3;
+ authority.start = uri.start + j + 3;
+ absPath.length = uri.length - k;
+ absPath.start = uri.start + k;
+ }
+ else {
+ uriInfractions |= INF_BADURI;
+ uriType = URI__PROBLEMATIC;
+ scheme.length = STAT_PROBLEMATIC;
+ authority.length = STAT_PROBLEMATIC;
+ absPath.length = STAT_PROBLEMATIC;
+ }
+ }
+}
+
+SchemeId NHttpUri::getSchemeId() {
+ if (schemeId != SCH__NOTCOMPUTE) return schemeId;
+ if (getScheme().length <= 0) {
+ schemeId = SCH__NOSOURCE;
+ return schemeId;
+ }
+
+ // Normalize scheme name to lower case for matching purposes
+ uint8_t *lowerScheme;
+ if ((lowerScheme = scratchPad.request(scheme.length)) == nullptr) {
+ uriInfractions |= INF_NOSCRATCH;
+ schemeId = SCH__INSUFMEMORY;
+ return schemeId;
+ }
+ norm2Lower(scheme.start, scheme.length, lowerScheme, uriInfractions, nullptr);
+ schemeId = (SchemeId) strToCode(lowerScheme, scheme.length, schemeList);
+ return schemeId;
+}
+
+field NHttpUri::getNormHost() {
+ if (hostNorm.length != STAT_NOTCOMPUTE) return hostNorm;
+ if (getHost().length < 0) {
+ hostNorm.length = STAT_NOSOURCE;
+ return hostNorm;
+ }
+ UriNormalizer::normalize(host, hostNorm, false, scratchPad, hostInfractions);
+ return hostNorm;
+}
+
+field NHttpUri::getNormPath() {
+ if (pathNorm.length != STAT_NOTCOMPUTE) return pathNorm;
+ if (getPath().length < 0) {
+ pathNorm.length = STAT_NOSOURCE;
+ return pathNorm;
+ }
+ UriNormalizer::normalize(path, pathNorm, true, scratchPad, pathInfractions);
+ return pathNorm;
+}
+
+field NHttpUri::getNormQuery() {
+ if (queryNorm.length != STAT_NOTCOMPUTE) return queryNorm;
+ if (getQuery().length < 0) {
+ queryNorm.length = STAT_NOSOURCE;
+ return queryNorm;
+ }
+ UriNormalizer::normalize(query, queryNorm, true, scratchPad, queryInfractions);
+ return queryNorm;
+}
+
+field NHttpUri::getNormFragment() {
+ if (fragmentNorm.length != STAT_NOTCOMPUTE) return fragmentNorm;
+ if (getFragment().length < 0) {
+ fragmentNorm.length = STAT_NOSOURCE;
+ return fragmentNorm;
+ }
+ UriNormalizer::normalize(fragment, fragmentNorm, true, scratchPad, fragmentInfractions);
+ return fragmentNorm;
+}
+
+int32_t NHttpUri::getPortValue() {
+ if (portValue != STAT_NOTCOMPUTE) return portValue;
+ if (getPort().length <= 0) {
+ portValue = STAT_NOSOURCE;
+ return portValue;
+ }
+ portValue = 0;
+ for (int k = 0; k < port.length; k++) {
+ portValue = portValue * 10 + (port.start[k] - '0');
+ if ((port.start[k] < '0') || (port.start[k] > '9') || (portValue > 65535))
+ {
+ uriInfractions |= INF_BADPORT;
+ portValue = STAT_PROBLEMATIC;
+ break;
+ }
+ }
+ return portValue;
+}
+
+void NHttpUri::parseAuthority() {
+ if (host.length != STAT_NOTCOMPUTE) return;
+ if (getAuthority().length <= 0) {
+ host.length = STAT_NOSOURCE;
+ port.length = STAT_NOSOURCE;
+ return;
+ }
+ host.start = authority.start;
+ for (host.length = 0; (authority.start[host.length] != ':') && (host.length < authority.length); host.length++);
+ if (host.length < authority.length) {
+ port.length = authority.length - host.length - 1;
+ port.start = authority.start + host.length + 1;
+ }
+ else port.length = STAT_NOTPRESENT;
+}
+
+void NHttpUri::parseAbsPath() {
+ if (path.length != STAT_NOTCOMPUTE) return;
+ if (getAbsPath().length <= 0) {
+ path.length = STAT_NOSOURCE;
+ query.length = STAT_NOSOURCE;
+ fragment.length = STAT_NOSOURCE;
+ return;
+ }
+ path.start = absPath.start;
+ for (path.length = 0; (absPath.start[path.length] != '?') && (absPath.start[path.length] != '#') && (path.length < absPath.length); path.length++);
+ if (path.length == absPath.length) {
+ query.length = STAT_NOTPRESENT;
+ fragment.length = STAT_NOTPRESENT;
+ return;
+ }
+ if (absPath.start[path.length] == '?') {
+ query.start = absPath.start + path.length + 1;
+ for (query.length = 0; (query.start[query.length] != '#') && (query.length < absPath.length - path.length - 1); query.length++);
+ fragment.start = query.start + query.length + 1;
+ fragment.length = absPath.length - path.length - 1 - query.length - 1;
+ }
+ else {
+ query.length = STAT_NOTPRESENT;
+ fragment.start = absPath.start + path.length + 1;
+ fragment.length = absPath.length - path.length - 1;
+ }
+}
+
+// Glue normalized URI fields back together
+field NHttpUri::getNormLegacy() {
+ if (legacyNorm.length != STAT_NOTCOMPUTE) return legacyNorm;
+
+ if (getPath().length >= 0) UriNormalizer::normalize(path, pathNorm, true, scratchPad, pathInfractions);
+ if (getHost().length >= 0) UriNormalizer::normalize(host, hostNorm, false, scratchPad, hostInfractions);
+ if (getQuery().length >= 0) UriNormalizer::normalize(query, queryNorm, false, scratchPad, queryInfractions);
+ if (getFragment().length >= 0) UriNormalizer::normalize(fragment, fragmentNorm, false, scratchPad, fragmentInfractions);
+
+ // We can reuse the raw URI for the normalized URI unless at least one part of the URI has been normalized
+ if ((hostInfractions == 0) && (pathInfractions == 0) && (queryInfractions == 0) && (fragmentInfractions == 0)) {
+ legacyNorm.start = uri.start;
+ legacyNorm.length = uri.length;
+ return legacyNorm;
+ }
+
+ // Glue normalized URI pieces back together
+ const uint32_t totalLength = ((scheme.length >= 0) ? scheme.length + 3 : 0) +
+ ((hostNorm.length >= 0) ? hostNorm.length : 0) +
+ ((port.length >= 0) ? port.length + 1 : 0) +
+ ((pathNorm.length >= 0) ? pathNorm.length : 0) +
+ ((queryNorm.length >= 0) ? queryNorm.length + 1 : 0) +
+ ((fragmentNorm.length >= 0) ? fragmentNorm.length + 1 : 0);
+ uint8_t* const scratch = scratchPad.request(totalLength);
+ if (scratch != nullptr) {
+ uint8_t *current = scratch;
+ if (scheme.length >= 0) {
+ memcpy(current, scheme.start, scheme.length);
+ current += scheme.length;
+ memcpy(current, "://", 3);
+ current += 3;
+ }
+ if (hostNorm.length >= 0) {
+ memcpy(current, hostNorm.start, hostNorm.length);
+ current += hostNorm.length;
+ }
+ if (port.length >= 0) {
+ memcpy(current, ":", 1);
+ current += 1;
+ memcpy(current, port.start, port.length);
+ current += port.length;
+ }
+ if (pathNorm.length >= 0) {
+ memcpy(current, pathNorm.start, pathNorm.length);
+ current += pathNorm.length;
+ }
+ if (queryNorm.length >= 0) {
+ memcpy(current, "?", 1);
+ current += 1;
+ memcpy(current, queryNorm.start, queryNorm.length);
+ current += queryNorm.length;
+ }
+ if (fragmentNorm.length >= 0) {
+ memcpy(current, "#", 1);
+ current += 1;
+ memcpy(current, fragmentNorm.start, fragmentNorm.length);
+ current += fragmentNorm.length;
+ }
+ assert(totalLength == current - scratch);
+ scratchPad.commit(current - scratch);
+ legacyNorm.start = scratch;
+ legacyNorm.length = current - scratch;
+ }
+ else legacyNorm.length = STAT_INSUFMEMORY;
+ return legacyNorm;
+}
+
+
--- /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 NHttpUri class declaration
+//
+
+#ifndef NHTTP_URI_H
+#define NHTTP_URI_H
+
+#include "nhttp_scratch_pad.h"
+#include "nhttp_str_to_code.h"
+#include "nhttp_uri_norm.h"
+
+//-------------------------------------------------------------------------
+// NHttpUri class
+//-------------------------------------------------------------------------
+
+class NHttpUri {
+public:
+ NHttpUri(const uint8_t* start, int32_t length, NHttpEnums::MethodId method) : methodId(method) {
+ uri.length = length; uri.start = start; };
+
+ field getUri() const { return uri; };
+ NHttpEnums::UriType getUriType() { parseUri(); return uriType; };
+ field getScheme() { parseUri(); return scheme; };
+ field getAuthority() { parseUri(); return authority; };
+ field getHost() { parseAuthority(); return host; };
+ field getPort() { parseAuthority(); return port; };
+ field getAbsPath() { parseUri(); return absPath; };
+ field getPath() { parseAbsPath(); return path; };
+ field getQuery() { parseAbsPath(); return query; };
+ field getFragment() { parseAbsPath(); return fragment; };
+
+ uint64_t getUriInfractions() const { return uriInfractions; };
+ uint64_t getHostInfractions() const { return hostInfractions; };
+ uint64_t getPathInfractions() const { return pathInfractions; };
+ uint64_t getQueryInfractions() const { return queryInfractions; };
+ uint64_t getFragmentInfractions() const { return fragmentInfractions; };
+
+ NHttpEnums::SchemeId getSchemeId();
+ field getNormHost();
+ int32_t getPortValue();
+ field getNormPath();
+ field getNormQuery();
+ field getNormFragment();
+ field getNormLegacy();
+
+private:
+ static const StrCode schemeList[];
+
+ field uri;
+ const NHttpEnums::MethodId methodId;
+
+ field scheme;
+ field authority;
+ field host;
+ field port;
+ field absPath;
+ field path;
+ field query;
+ field fragment;
+
+ uint64_t uriInfractions = 0;
+ uint64_t hostInfractions = 0;
+ uint64_t pathInfractions = 0;
+ uint64_t queryInfractions = 0;
+ uint64_t fragmentInfractions = 0;
+
+ NHttpEnums::UriType uriType = NHttpEnums::URI__NOTCOMPUTE;
+ NHttpEnums::SchemeId schemeId = NHttpEnums::SCH__NOTCOMPUTE;
+ field hostNorm;
+ int32_t portValue = NHttpEnums::STAT_NOTCOMPUTE;
+ field pathNorm;
+ field queryNorm;
+ field fragmentNorm;
+ field legacyNorm;
+
+ void parseUri();
+ void parseAuthority();
+ void parseAbsPath();
+
+ ScratchPad scratchPad {NHttpEnums::MAXOCTETS*2};
+};
+
+#endif
+
+
+
+
+
//
// @author Tom Peters <thopeter@cisco.com>
//
-// @brief URI normalization functions
+// @brief URI normalization class
//
-// &&&#include <string.h>
#include <assert.h>
#include <sys/types.h>
using namespace NHttpEnums;
-void UriNormalizer::normalize(const field &input, field &result, ScratchPad &scratchPad, uint64_t &infractions) const {
+void UriNormalizer::normalize(const field &input, field &result, bool doPath, ScratchPad &scratchPad, uint64_t &infractions) {
if (result.length != STAT_NOTCOMPUTE) return;
- if (input.length < 0) {
- result.length = STAT_NOTPRESENT;
- return;
- }
+ assert (input.length >= 0);
// Almost all HTTP requests are honest and rarely need expensive normalization processing. We do a quick scan for
// red flags and only perform normalization if something comes up. Otherwise we set the normalized field to point
result.length = dataLength;
}
-bool UriNormalizer::noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) const {
+bool UriNormalizer::noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) {
for (int32_t k = 0; k < inLength; k++) {
if ((uriChar[inBuf[k]] == CHAR_NORMAL) || (uriChar[inBuf[k]] == CHAR_PATH)) continue;
infractions |= INF_URINEEDNORM;
return true;
}
-bool UriNormalizer::pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) const {
+bool UriNormalizer::pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) {
for (int32_t k = 0; k < inLength; k++) {
if (uriChar[inBuf[k]] == CHAR_NORMAL) continue;
if ((inBuf[k] == '/') && ((k == 0) || (inBuf[k-1] != '/'))) continue;
return true;
}
-int32_t UriNormalizer::normCharClean(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) const {
+int32_t UriNormalizer::normCharClean(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) {
int32_t length = 0;
for (int32_t k = 0; k < inLength; k++) {
switch (uriChar[inBuf[k]]) {
}
// Convert URI backslashes to slashes
-int32_t UriNormalizer::normBackSlash(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) const {
+int32_t UriNormalizer::normBackSlash(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) {
for (int32_t k = 0; k < inLength; k++) {
if (inBuf[k] != '\\') outBuf[k] = inBuf[k];
else {
}
// Caution: worst case output length is one greater than input length
-int32_t UriNormalizer::normPathClean(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) const {
+int32_t UriNormalizer::normPathClean(const uint8_t* inBuf, int32_t inLength, uint8_t *outBuf, uint64_t& infractions, const void *) {
int32_t length = 0;
// It simplifies the code that handles /./ and /../ to pretend there is an extra '/' after the buffer.
// Avoids making a special case of URIs that end in . or ..
class UriNormalizer {
public:
- UriNormalizer(bool doPath_) : doPath(doPath_) {};
- void normalize(const field &input, field &result, ScratchPad &scratchPad, uint64_t &infractions) const;
+ static void normalize(const field &input, field &result, bool doPath, ScratchPad &scratchPad, uint64_t &infractions);
private:
static const NHttpEnums::CharAction uriChar[256];
static const int8_t asHex[256];
static const bool goodPercent[256];
- bool noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) const;
- bool pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions) const;
+ static bool noPathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions);
+ static bool pathCheck(const uint8_t* inBuf, int32_t inLength, uint64_t& infractions);
- int32_t normCharClean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed) const;
- int32_t normBackSlash(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed) const;
- int32_t normPathClean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed) const;
-
- bool doPath;
+ static int32_t normCharClean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
+ static int32_t normBackSlash(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
+ static int32_t normPathClean(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void* notUsed);
};
#endif