Inspector* NHttpApi::nhttp_ctor(Module* mod)
{
const NHttpModule* nhttpMod = (NHttpModule*) mod;
- return new NHttpInspect(nhttpMod->get_test_input(), nhttpMod->get_test_output(), nhttpMod->get_test_inspect());
+ return new NHttpInspect(nhttpMod->get_test_input(), nhttpMod->get_test_output());
}
static const char* buffers[] =
// Never use the start pointer without verifying that length > 0.
struct field {
public:
- const uint8_t *start = nullptr;
int32_t length = NHttpEnums::STAT_NOTCOMPUTE;
+ const uint8_t* start = nullptr;
+
+ field(int32_t length_, const uint8_t* start_) : length(length_), start(start_) {};
+ field() = default;
};
typedef enum
void NHttpFlowData::halfReset(SourceId sourceId) {
assert((sourceId == SRC_CLIENT) || (sourceId == SRC_SERVER));
- dataLength[sourceId] = STAT_NOTPRESENT;
octetsExpected[sourceId] = STAT_NOTPRESENT;
+
+ versionId[sourceId] = VERS__NOTPRESENT;
+ methodId[sourceId] = METH__NOTPRESENT;
+ statusCodeNum[sourceId] = STAT_NOTPRESENT;
+
+ dataLength[sourceId] = STAT_NOTPRESENT;
bodySections[sourceId] = STAT_NOTPRESENT;
bodyOctets[sourceId] = STAT_NOTPRESENT;
numChunks[sourceId] = STAT_NOTPRESENT;
chunkSections[sourceId] = STAT_NOTPRESENT;
chunkOctets[sourceId] = STAT_NOTPRESENT;
-
- versionId[sourceId] = VERS__NOTPRESENT;
- methodId[sourceId] = METH__NOTPRESENT;
- statusCodeNum[sourceId] = STAT_NOTPRESENT;
}
friend class NHttpMsgChunkHead;
friend class NHttpMsgChunkBody;
friend class NHttpMsgTrailer;
- friend class NHttpTestInput;
friend class NHttpStreamSplitter;
private:
void halfReset(NHttpEnums::SourceId sourceId);
// StreamSplitter => Inspector (facts about the most recent message section)
- NHttpEnums::SourceId sourceId = NHttpEnums::SRC__NOTCOMPUTE;
- NHttpEnums::SectionType sectionType = NHttpEnums::SEC__NOTCOMPUTE;
- bool tcpClose = false;
- uint64_t infractions = 0;
+ // 0 element refers to client request, 1 element refers to server response
+ NHttpEnums::SectionType sectionType[2] = { NHttpEnums::SEC__NOTCOMPUTE, NHttpEnums::SEC__NOTCOMPUTE };
+ bool tcpClose[2] = { false, false };
+ uint64_t infractions[2] = { 0, 0 };
// Inspector => StreamSplitter (facts about the message section that is coming next)
- // 0 element refers to client request, 1 element refers to server response
NHttpEnums::SectionType typeExpected[2] = { NHttpEnums::SEC_REQUEST, NHttpEnums::SEC_STATUS };
int64_t octetsExpected[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT }; // expected size of the upcoming body or chunk body section
}
// 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 {
- // If the raw header is not present length will be STAT_NOSOURCE and normalization is skipped
+int32_t HeaderNormalizer::normalize(const HeaderId headId, const int count, ScratchPad &scratchPad, uint64_t &infractions,
+ const HeaderId headerNameId[], const field headerValue[], const int32_t numHeaders, field &resultField) const {
if (resultField.length != STAT_NOTCOMPUTE) return resultField.length;
- if (format == NORM_NULL) {
- resultField.length = STAT_NOTCONFIGURED;
- return resultField.length;
- }
+ if (format == NORM_NULL) return resultField.length = STAT_NOTCONFIGURED;
+ if (count == 0) return resultField.length = STAT_NOSOURCE;
- // 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
- // header can be present more than once. The standard normalization is to concatenate all the repeated field values into a comma-separated list. Otherwise there should not
- // be more than one instance of this header. infractRepeats causes us to inspect for improper repeated headers. Regardless of whether we look for these extra values only
- // the first value will be normalized.
+ // Search Header IDs from all the headers in this message. concatenateRepeats means the header can properly be
+ // present more than once. The standard normalization is to concatenate all the repeated field values into a
+ // comma-separated list. Otherwise only the first value will be normalized and the rest will be ignored.
int numMatches = 0;
int32_t bufferLength = 0;
- int firstMatch = -1;
+ int currMatch;
for (int k=0; k < numHeaders; k++) {
- if (headerNameId[k] == HEAD__NOTCOMPUTE) break;
if (headerNameId[k] == headId) {
- numMatches++;
- if (numMatches == 1) firstMatch = k;
- if ((numMatches == 1) || concatenateRepeats) bufferLength += headerValue[k].length;
- if (!concatenateRepeats && !infractRepeats) break;
+ if (++numMatches == 1) currMatch = k; // remembering location of the first matching header
+ bufferLength += headerValue[k].length;
+ if (!concatenateRepeats || (numMatches >= count)) break;
}
}
- if (numMatches == 0) {
- resultField.length = STAT_NOSOURCE;
- return resultField.length;
- }
- if (infractRepeats && (numMatches >= 2)) infractions |= INF_BADHEADERREPS;
-
- // The scratchPad provides the space to store the normalized value. We are allocating twice as much memory as we need to store the normalized field value. The raw field
- // value will be copied into one half of the buffer. Concatenation and white space normalization happen during this step. Next a series of normalization functions will
- // transform the value into final form. Each normalization copies the value from one half of the buffer to the other. Based on whether the number of normalization functions
- // is odd or even, the initial placement in the buffer is chosen so that the final normalization leaves the field value at the front of the buffer. The buffer space actually
- // used is locked down in the scratchPad. The remainder of the first half and all of the second half are returned to the scratchPad for future use.
- if (concatenateRepeats) bufferLength += numMatches - 1; // allow space for concatenation commas
- // Round up to multiple of eight so that both halves are 64-bit aligned.
- // 200 is a "way too big" fudge factor to allow for modest expansion of field size during normalization. Needs improvement.
+ assert((!concatenateRepeats && (numMatches == 1)) || (concatenateRepeats && (numMatches == count)));
+ bufferLength += numMatches - 1; // allow space for concatenation commas
+
+ // We are allocating twice as much memory as we need to store the normalized field value. The raw field value will
+ // be copied into one half of the buffer. Concatenation and white space normalization happen during this step. Next
+ // a series of normalization functions will transform the value into final form. Each normalization copies the value
+ // from one half of the buffer to the other. Based on whether the number of normalization functions is odd or even,
+ // the initial placement in the buffer is chosen so that the final normalization leaves the field value at the front
+ // of the buffer. The buffer space actually used is locked down in the scratchPad. The remainder of the first half
+ // and all of the second half are returned to the scratchPad for future use.
+
+ // Round up to multiple of eight so that both halves are 64-bit aligned. 200 is a "way too big" fudge factor to allow
+ // for modest expansion of field size during normalization.
bufferLength += (8-bufferLength%8)%8 + 200;
- uint8_t * const scratch = scratchPad.request(2*bufferLength);
- if (scratch == nullptr) {
- resultField.length = STAT_INSUFMEMORY;
- return resultField.length;
- }
+ uint8_t* const scratch = scratchPad.request(2*bufferLength);
+ if (scratch == nullptr) return resultField.length = STAT_INSUFMEMORY;
- uint8_t * const frontHalf = scratch;
- uint8_t * const backHalf = scratch + bufferLength;
- uint8_t *working = (numNormalizers%2 == 0) ? frontHalf : backHalf;
- int currMatch = firstMatch;
+ uint8_t* const frontHalf = scratch;
+ uint8_t* const backHalf = scratch + bufferLength;
+ uint8_t* working = (numNormalizers%2 == 0) ? frontHalf : backHalf;
int32_t dataLength = 0;
for (int j=0; j < numMatches; j++) {
if (j >= 1) {
int32_t growth = deriveHeaderContent(headerValue[currMatch].start, headerValue[currMatch].length, working);
working += growth;
dataLength += growth;
- if (!concatenateRepeats) break;
}
for (int i=0; i < numNormalizers; i++) {
if (i%2 != numNormalizers%2) dataLength = normalizer[i](backHalf, dataLength, frontHalf, infractions, normArg[i]);
else dataLength = normalizer[i](frontHalf, dataLength, backHalf, infractions, normArg[i]);
- if (dataLength <= 0) {
- resultField.length = dataLength;
- return resultField.length;
- }
+ if (dataLength <= 0) return resultField.length = dataLength;
}
resultField.start = scratch;
resultField.length = dataLength;
}
+
class HeaderNormalizer {
public:
- constexpr HeaderNormalizer(NHttpEnums::NormFormat _format, bool _concatenateRepeats, bool _infractRepeats, int32_t (*f1)(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*),
+ constexpr HeaderNormalizer(NHttpEnums::NormFormat _format, bool _concatenateRepeats, int32_t (*f1)(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*),
const void *f1Arg, int32_t (*f2)(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*), const void *f2Arg, int32_t (*f3)(const uint8_t*, int32_t, uint8_t*, uint64_t&,
const void*), const void *f3Arg) :
format(_format),
concatenateRepeats(_concatenateRepeats),
- infractRepeats(_infractRepeats),
normalizer { f1, f2, f3 },
normArg { f1Arg, f2Arg, f3Arg },
numNormalizers((f1 != nullptr) + (f1 != nullptr)*(f2 != nullptr) + (f1 != nullptr)*(f2 != nullptr)*(f3 != nullptr)) {};
- int32_t normalize(NHttpEnums::HeaderId headId, ScratchPad &scratchPad, uint64_t &infractions, const NHttpEnums::HeaderId headerNameId[], const field headerName[], int32_t numHeaders,
- field &resultField) const;
+ int32_t normalize(const NHttpEnums::HeaderId headId, const int count, ScratchPad &scratchPad, uint64_t &infractions,
+ const NHttpEnums::HeaderId headerNameId[], const field headerValue[], const int32_t numHeaders, field &resultField) const;
NHttpEnums::NormFormat getFormat() const {return format;};
private:
const NHttpEnums::NormFormat format;
const bool concatenateRepeats;
- const bool infractRepeats;
int32_t (* const normalizer[3])(const uint8_t*, int32_t, uint8_t*, uint64_t&, const void*);
const void * normArg[3];
const int numNormalizers;
using namespace NHttpEnums;
-NHttpInspect::NHttpInspect(bool test_input, bool _test_output, bool _test_inspect) : test_output(_test_output), test_inspect(_test_inspect)
+NHttpInspect::NHttpInspect(bool test_input, bool _test_output) : test_output(_test_output)
{
NHttpTestInput::test_input = test_input;
if (NHttpTestInput::test_input) {
// Only packets from the StreamSplitter can be processed
if (!PacketHasPAFPayload(p)) return;
- process(p->data, p->dsize, p->flow);
+ process(p->data, p->dsize, p->flow, (p->packet_flags & PKT_FROM_CLIENT) ? SRC_CLIENT : SRC_SERVER);
}
-void NHttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const flow)
+void NHttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const flow, SourceId sourceId)
{
delete msgSection;
msgSection = nullptr;
assert(sessionData);
if (!NHttpTestInput::test_input) {
- switch (sessionData->sectionType) {
- 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;
+ switch (sessionData->sectionType[sourceId]) {
+ case SEC_REQUEST: msgSection = new NHttpMsgRequest(data, dsize, sessionData, sourceId); break;
+ case SEC_STATUS: msgSection = new NHttpMsgStatus(data, dsize, sessionData, sourceId); break;
+ case SEC_HEADER: msgSection = new NHttpMsgHeader(data, dsize, sessionData, sourceId); break;
+ case SEC_BODY: msgSection = new NHttpMsgBody(data, dsize, sessionData, sourceId); break;
+ case SEC_CHUNKHEAD: msgSection = new NHttpMsgChunkHead(data, dsize, sessionData, sourceId); break;
+ case SEC_CHUNKBODY: msgSection = new NHttpMsgChunkBody(data, dsize, sessionData, sourceId); break;
+ case SEC_TRAILER: msgSection = new NHttpMsgTrailer(data, dsize, sessionData, sourceId); break;
case SEC_DISCARD: return;
default: assert(0); return;
}
- 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: 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;
+ if ((testLength = NHttpTestInput::testInput->toEval(&testBuffer, testNumber, sourceId)) > 0) {
+ switch (sessionData->sectionType[sourceId]) {
+ case SEC_REQUEST: msgSection = new NHttpMsgRequest(testBuffer, testLength, sessionData, sourceId); break;
+ case SEC_STATUS: msgSection = new NHttpMsgStatus(testBuffer, testLength, sessionData, sourceId); break;
+ case SEC_HEADER: msgSection = new NHttpMsgHeader(testBuffer, testLength, sessionData, sourceId); break;
+ case SEC_BODY: msgSection = new NHttpMsgBody(testBuffer, testLength, sessionData, sourceId); break;
+ case SEC_CHUNKHEAD: msgSection = new NHttpMsgChunkHead(testBuffer, testLength, sessionData, sourceId); break;
+ case SEC_CHUNKBODY: msgSection = new NHttpMsgChunkBody(testBuffer, testLength, sessionData, sourceId); break;
+ case SEC_TRAILER: msgSection = new NHttpMsgTrailer(testBuffer, testLength, sessionData, sourceId); break;
case SEC_DISCARD: return;
default: assert(0); return;
}
- msgSection->loadSection(testBuffer, testLength, sessionData);
}
else {
printf("Zero length test data.\n");
return;
}
}
- msgSection->initSection();
msgSection->analyze();
msgSection->updateFlow();
msgSection->genEvents();
msgSection->legacyClients();
- if (test_inspect) msgSection->analyzeAll();
if (test_output) {
if (!NHttpTestInput::test_input) msgSection->printSection(stdout);
else {
class NHttpInspect : public Inspector {
public:
- NHttpInspect(bool test_input, bool _test_output, bool _test_inspect);
+ NHttpInspect(bool test_input, bool _test_output);
~NHttpInspect();
bool get_buf(unsigned, Packet*, InspectionBuffer&);
friend NHttpApi;
NHttpMsgSection *msgSection = nullptr;
- void process(const uint8_t* data, const uint16_t dsize, Flow* const flow);
+ void process(const uint8_t* data, const uint16_t dsize, Flow* const flow, NHttpEnums::SourceId sourceId_);
// 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
using namespace NHttpEnums;
-void NHttpMsgBody::loadSection(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_) {
- NHttpMsgSection::loadSection(buffer, bufSize, sessionData_);
-
- dataLength = sessionData->dataLength[sourceId];
- bodySections = sessionData->bodySections[sourceId];
- bodyOctets = sessionData->bodyOctets[sourceId];
-}
-
-void NHttpMsgBody::initSection() {
- data.length = STAT_NOTCOMPUTE;
-}
+NHttpMsgBody::NHttpMsgBody(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_, SourceId sourceId_) :
+ NHttpMsgSection(buffer, bufSize, sessionData_, sourceId_), dataLength(sessionData->dataLength[sourceId]),
+ bodySections(sessionData->bodySections[sourceId]), bodyOctets(sessionData->bodyOctets[sourceId]) {}
void NHttpMsgBody::analyze() {
bodySections++;
if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
}
-void NHttpMsgBody::printSection(FILE *output) const {
+void NHttpMsgBody::printSection(FILE *output) {
NHttpMsgSection::printMessageTitle(output, "body");
fprintf(output, "Expected data length %" PRIi64 ", sections seen %" PRIi64 ", octets seen %" PRIi64 "\n", dataLength, bodySections, bodyOctets);
printInterval(output, "Data", data.start, data.length);
class NHttpMsgBody : public NHttpMsgSection {
public:
- NHttpMsgBody() {};
- void loadSection(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_);
- void initSection();
+ NHttpMsgBody(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_, NHttpEnums::SourceId sourceId_);
void analyze();
- void printSection(FILE *output) const;
+ void printSection(FILE *output);
void genEvents();
void updateFlow();
void legacyClients();
using namespace NHttpEnums;
-void NHttpMsgChunkBody::loadSection(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_) {
- NHttpMsgBody::loadSection(buffer, bufSize, sessionData_);
-
- numChunks = sessionData->numChunks[sourceId];
- chunkSections = sessionData->chunkSections[sourceId];
- chunkOctets = sessionData->chunkOctets[sourceId];
-}
-
-void NHttpMsgChunkBody::initSection() {
- NHttpMsgBody::initSection();
-}
+NHttpMsgChunkBody::NHttpMsgChunkBody(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_, SourceId sourceId_) :
+ NHttpMsgBody(buffer, bufSize, sessionData_, sourceId_), numChunks(sessionData->numChunks[sourceId]),
+ chunkSections(sessionData->chunkSections[sourceId]), chunkOctets(sessionData->chunkOctets[sourceId]) {}
void NHttpMsgChunkBody::analyze() {
bodySections++;
if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
}
-void NHttpMsgChunkBody::printSection(FILE *output) const {
+void NHttpMsgChunkBody::printSection(FILE *output) {
NHttpMsgSection::printMessageTitle(output, "chunk body");
fprintf(output, "Expected chunk length %" PRIi64 ", cumulative sections %" PRIi64 ", cumulative octets %" PRIi64 "\n", dataLength, bodySections, bodyOctets);
fprintf(output, "cumulative chunk sections %" PRIi64 ", cumulative chunk octets %" PRIi64 "\n", chunkSections, chunkOctets);
class NHttpMsgChunkBody : public NHttpMsgBody {
public:
- NHttpMsgChunkBody() {};
- void loadSection(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_);
- void initSection();
+ NHttpMsgChunkBody(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_, NHttpEnums::SourceId sourceId_);
void analyze();
- void printSection(FILE *output) const;
+ void printSection(FILE *output);
void genEvents();
void updateFlow();
using namespace NHttpEnums;
+NHttpMsgChunkHead::NHttpMsgChunkHead(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_, SourceId sourceId_) :
+ NHttpMsgSection(buffer, bufSize, sessionData_, sourceId_), bodySections(sessionData->bodySections[sourceId]),
+ numChunks(sessionData->numChunks[sourceId]) {}
+
// Convert the hexadecimal chunk length.
// RFC says that zero may be written with multiple digits "000000".
// Arbitrary limit of 15 hex digits not including leading zeros ensures in a simple way against 64-bit overflow and should be
}
}
-void NHttpMsgChunkHead::loadSection(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_) {
- NHttpMsgSection::loadSection(buffer, bufSize, sessionData_);
-
- bodySections = sessionData->bodySections[sourceId];
- numChunks = sessionData->numChunks[sourceId];
-}
-
-void NHttpMsgChunkHead::initSection() {
- startLine.length = STAT_NOTCOMPUTE;
- chunkSize.length = STAT_NOTCOMPUTE;
- chunkExtensions.length = STAT_NOTCOMPUTE;
-}
-
void NHttpMsgChunkHead::analyze() {
bodySections++;
// First section in a new chunk is just the start line.
if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
}
-void NHttpMsgChunkHead::printSection(FILE *output) const {
+void NHttpMsgChunkHead::printSection(FILE *output) {
NHttpMsgSection::printMessageTitle(output, "chunk header");
fprintf(output, "Chunk size: %" PRIi64 "\n", dataLength);
printInterval(output, "Chunk extensions", chunkExtensions.start, chunkExtensions.length);
class NHttpMsgChunkHead : public NHttpMsgSection {
public:
- NHttpMsgChunkHead() {};
- void loadSection(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_);
- void initSection();
+ NHttpMsgChunkHead(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_, NHttpEnums::SourceId sourceId_);
void analyze();
- void printSection(FILE *output) const;
+ void printSection(FILE *output);
void genEvents();
void updateFlow();
void legacyClients();
field chunkSize;
field chunkExtensions;
- int64_t dataLength;
+ int64_t dataLength = NHttpEnums::STAT_NOTCOMPUTE;
int64_t bodySections;
int64_t numChunks;
};
if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
}
-void NHttpMsgHeader::printSection(FILE *output) const {
+void NHttpMsgHeader::printSection(FILE *output) {
NHttpMsgSection::printMessageTitle(output, "header");
NHttpMsgHeadShared::printHeaders(output);
NHttpMsgSection::printMessageWrapup(output);
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 ( (headerNorms[HEAD_TRANSFER_ENCODING]->normalize(HEAD_TRANSFER_ENCODING, scratchPad, infractions,
- headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_TRANSFER_ENCODING]) > 0) &&
+ else if ( (headerNorms[HEAD_TRANSFER_ENCODING]->normalize(HEAD_TRANSFER_ENCODING, headerCount[HEAD_TRANSFER_ENCODING],
+ scratchPad, infractions, headerNameId, headerValue, numHeaders, 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->bodyOctets[sourceId] = 0;
sessionData->numChunks[sourceId] = 0;
}
- else if ((headerNorms[HEAD_CONTENT_LENGTH]->normalize(HEAD_CONTENT_LENGTH, scratchPad, infractions,
- headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_CONTENT_LENGTH]) > 0) &&
+ else if ((headerNorms[HEAD_CONTENT_LENGTH]->normalize(HEAD_CONTENT_LENGTH, headerCount[HEAD_CONTENT_LENGTH],
+ scratchPad, infractions, headerNameId, headerValue, numHeaders, headerValueNorm[HEAD_CONTENT_LENGTH]) > 0) &&
(*(int64_t*)headerValueNorm[HEAD_CONTENT_LENGTH].start > 0)) {
// Regular body
sessionData->typeExpected[sourceId] = SEC_BODY;
class NHttpMsgHeader: public NHttpMsgHeadShared {
public:
- NHttpMsgHeader() {};
- void printSection(FILE *output) const;
+ NHttpMsgHeader(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_, NHttpEnums::SourceId sourceId_) :
+ NHttpMsgHeadShared(buffer, bufSize, sessionData_, sourceId_) {};
+ void printSection(FILE *output);
void genEvents();
void updateFlow();
};
using namespace NHttpEnums;
-// Reinitialize everything derived in preparation for analyzing a new message
-void NHttpMsgHeadShared::initSection() {
- headers.length = STAT_NOTCOMPUTE;
- numHeaders = STAT_NOTCOMPUTE;
- for(int k = 0; k < MAXHEADERS; k++) {
- headerLine[k].length = STAT_NOTCOMPUTE;
- headerName[k].length = STAT_NOTCOMPUTE;
- headerNameId[k] = HEAD__NOTCOMPUTE;
- headerValue[k].length = STAT_NOTCOMPUTE;
- }
- for (int k = 1; k < HEAD__MAXVALUE; k++) {
- headerValueNorm[k].length = STAT_NOSOURCE;
- }
-}
-
// All the header processing that is done for every message (i.e. not just-in-time) is done here.
void NHttpMsgHeadShared::analyze() {
parseWhole();
parseHeaderBlock();
parseHeaderLines();
- for (int j=0; j < MAXHEADERS; j++) {
- if (headerName[j].length <= 0) break;
+ for (int j=0; j < numHeaders; j++) {
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((HeaderId)k, scratchPad, infractions, headerNameId, headerValue, MAXHEADERS, headerValueNorm[k]);
+ if (headerNameId[j] > 0) headerCount[headerNameId[j]]++;
}
}
if (headers.length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_HEADER, headers.start, (unsigned)headers.length);
if (headers.length > 0) SetHttpBuffer(HTTP_BUFFER_HEADER, headers.start, (unsigned)headers.length);
- for (int k=0; (headerNameId[k] != HEAD__NOTCOMPUTE) && (k < MAXHEADERS); k++) {
+ for (int k=0; k < numHeaders; k++) {
if (((headerNameId[k] == HEAD_COOKIE) && (sourceId == SRC_CLIENT)) || ((headerNameId[k] == HEAD_SET_COOKIE) && (sourceId == SRC_SERVER))) {
if (headerValue[k].length > 0) SetHttpBuffer(HTTP_BUFFER_RAW_COOKIE, headerValue[k].start, (unsigned)headerValue[k].length);
break;
}
if (sourceId == SRC_CLIENT) {
- if (headerNorms[HEAD_COOKIE]->normalize(HEAD_COOKIE, scratchPad, infractions, headerNameId, headerValue, MAXHEADERS, headerValueNorm[HEAD_COOKIE]) > 0) {
+ if (headerNorms[HEAD_COOKIE]->normalize(HEAD_COOKIE, headerCount[HEAD_COOKIE], scratchPad, infractions,
+ headerNameId, headerValue, numHeaders, 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) {
+ if (headerNorms[HEAD_SET_COOKIE]->normalize(HEAD_SET_COOKIE, headerCount[HEAD_SET_COOKIE], scratchPad, infractions,
+ headerNameId, headerValue, numHeaders, 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 {
+void NHttpMsgHeadShared::printHeaders(FILE *output) {
char titleBuf[100];
if (numHeaders != STAT_NOSOURCE) fprintf(output, "Number of headers: %d\n", numHeaders);
- for (int j=0; j < numHeaders && j < 200; j++) {
+ for (int j=0; j < numHeaders; 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_NOSOURCE) {
+ if (headerNorms[k]->normalize((HeaderId)k, headerCount[k], scratchPad, infractions, headerNameId, headerValue, numHeaders, headerValueNorm[k]) != STAT_NOSOURCE) {
snprintf(titleBuf, sizeof(titleBuf), "Normalized header %d", k);
printInterval(output, titleBuf, headerValueNorm[k].start, headerValueNorm[k].length, true);
}
-
-
-
-
-
class NHttpMsgHeadShared: public NHttpMsgSection {
public:
- void initSection();
void analyze();
- void analyzeAll();
void genEvents();
void legacyClients();
protected:
- // Header normalization. There should be one of these for every different way we can process a header field value.
+ NHttpMsgHeadShared(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_, NHttpEnums::SourceId sourceId_) :
+ NHttpMsgSection(buffer, bufSize, sessionData_, sourceId_) {};
+
+ // Header normalization strategies. There should be one of these for every different way we can process a header field value.
static const HeaderNormalizer NORMALIZER_NIL;
static const HeaderNormalizer NORMALIZER_BASIC;
static const HeaderNormalizer NORMALIZER_CAT;
static const HeaderNormalizer* const headerNorms[];
static const int32_t numNorms;
- // Code conversion tables are for turning token strings into enums.
+ // Tables of header field names and header value names
static const StrCode headerList[];
static const StrCode transCodeList[];
- // "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 parseWhole();
void parseHeaderBlock();
void parseHeaderLines();
void deriveHeaderNameId(int index);
- void printHeaders(FILE *output) const;
+ void printHeaders(FILE *output);
- // This is where all the derived values, extracted message parts, and normalized values are.
- // Note that this is all scalars, buffer pointers, and buffer sizes. The actual buffers are in the message buffer (raw pieces) or the
- // scratchPad (normalized pieces).
field headers;
+
+ // All of these are indexed by the relative position of the header field in the message
static const int MAXHEADERS = 200; // I'm an arbitrary number. Need to revisit.
- int32_t numHeaders;
+ int32_t numHeaders = NHttpEnums::STAT_NOTCOMPUTE;
field headerLine[MAXHEADERS];
field headerName[MAXHEADERS];
NHttpEnums::HeaderId headerNameId[MAXHEADERS];
field headerValue[MAXHEADERS];
+
+ // Normalized values are indexed by HeaderId
+ int headerCount[NHttpEnums::HEAD__MAXVALUE] = { };
field headerValueNorm[NHttpEnums::HEAD__MAXVALUE];
};
using namespace NHttpEnums;
-// Reinitialize everything derived in preparation for analyzing a new message
-void NHttpMsgRequest::initSection() {
- NHttpMsgStart::initSection();
- method.length = STAT_NOTCOMPUTE;
- delete uri;
- uri = nullptr;
-}
-
void NHttpMsgRequest::parseStartLine() {
// There should be exactly two spaces. One following the method and one before "HTTP/".
// Additional spaces located within the URI are not allowed but we will tolerate it
if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
}
-void NHttpMsgRequest::printSection(FILE *output) const {
+void NHttpMsgRequest::printSection(FILE *output) {
NHttpMsgSection::printMessageTitle(output, "request line");
fprintf(output, "Version Id: %d\n", versionId);
fprintf(output, "Method Id: %d\n", methodId);
class NHttpMsgRequest: public NHttpMsgStart {
public:
- NHttpMsgRequest() {};
+ NHttpMsgRequest(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_, NHttpEnums::SourceId sourceId_) :
+ NHttpMsgStart(buffer, bufSize, sessionData_, sourceId_) {};
~NHttpMsgRequest() { delete uri; };
- void initSection();
- void printSection(FILE *output) const;
+ void printSection(FILE *output);
void genEvents();
void updateFlow();
void legacyClients();
private:
- // Code conversion tables are for turning token strings into enums.
static const StrCode methodList[];
- // "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();
- // 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;
NHttpUri* uri = nullptr;
};
using namespace NHttpEnums;
+NHttpMsgSection::NHttpMsgSection(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_, SourceId sourceId_) :
+ length(bufSize), sessionData(sessionData_), sourceId(sourceId_), tcpClose(sessionData->tcpClose[sourceId]),
+ scratchPad(2*length+500), infractions(sessionData->infractions[sourceId]), versionId(sessionData->versionId[sourceId]),
+ methodId(sessionData->methodId[sourceId]), statusCodeNum(sessionData->statusCodeNum[sourceId])
+{
+ rawBuf = new uint8_t[length];
+ memcpy(rawBuf, buffer, length);
+ msgText = rawBuf;
+}
+
// Return the number of octets before the first CRLF. Return length if CRLF not present.
//
// wrappable: CRLF does not count in a header field when immediately followed by <SP> or <LF>. These whitespace characters
return length;
}
-// Load a new message section
-void NHttpMsgSection::loadSection(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_) {
- length = bufsize;
- memcpy(rawBuf, buffer, length);
-
- sessionData = sessionData_;
- infractions = sessionData->infractions;
- sourceId = sessionData->sourceId;
- tcpClose = sessionData->tcpClose;
- versionId = sessionData->versionId[sourceId];
- methodId = sessionData->methodId[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) || (length == STAT_NOSOURCE)) return;
int outCount = fprintf(output, "%s, length = %d, ", name, length);
class NHttpMsgSection {
public:
- virtual void loadSection(const uint8_t *buffer, const uint16_t bufsize, NHttpFlowData *sessionData_);
- virtual ~NHttpMsgSection() = default;
- virtual void initSection() = 0;
+ virtual ~NHttpMsgSection() {delete[] rawBuf;};
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() = 0;
- virtual void legacyClients() = 0;
+ virtual void printSection(FILE *output) = 0; // Test tool prints all derived message parts
+ virtual void genEvents() = 0; // Converts collected information into required preprocessor events
+ virtual void updateFlow() = 0; // Manages the splitter and communication between message sections
+ virtual void legacyClients() = 0; // Populates the raw and normalized buffer interface used by old Snort
protected:
+ NHttpMsgSection(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_, NHttpEnums::SourceId sourceId_);
+
// Convenience methods
static uint32_t findCrlf(const uint8_t* buffer, int32_t length, bool wrappable);
static void printInterval(FILE *output, const char* name, const uint8_t *text, int32_t length, bool intVals = false);
void printMessageWrapup(FILE *output) const;
// The current strategy is to copy the entire raw message section into this object. Here it is.
- int32_t length; // Length of the original message section in octets
- uint8_t rawBuf[NHttpEnums::MAXOCTETS]; // The original HTTP message section octets
- // This pointer is the handle for working with the original message data. It makes it simple to later replace rawBuf with some other form of storage
- // such as the buffer in the packet structure or something dynamic. Const x 2 because this pointer should never change and people working with the
- // original message should not be changing it. Only loading a completely new message into rawBuf should do that.
- const uint8_t * const msgText = rawBuf;
+ int32_t length;
+ uint8_t* rawBuf;
+ // This pseudonym for rawBuf isolates details of how the raw message is stored from everything else.
+ const uint8_t* msgText;
NHttpFlowData* sessionData;
- ScratchPad scratchPad {NHttpEnums::MAXOCTETS*2};
+ NHttpEnums::SourceId sourceId;
+ bool tcpClose;
+ ScratchPad scratchPad;
// 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
+ // These are all scalars, buffer pointers, and buffer sizes. The actual buffers are in message buffer (raw pieces) or the
// scratchPad (normalized pieces).
uint64_t infractions;
- bool tcpClose;
- NHttpEnums::SourceId sourceId;
NHttpEnums::VersionId versionId;
NHttpEnums::MethodId methodId;
int32_t statusCodeNum;
//
// @author Tom Peters <thopeter@cisco.com>
//
-// @brief NHttpMsgStart virtual class rolls up all the common elements of start line processing.
+// @brief NHttpMsgStart virtual class rolls up all the common elements of request and status line processing.
//
using namespace NHttpEnums;
-// Reinitialize everything derived in preparation for analyzing a new message
-void NHttpMsgStart::initSection() {
- startLine.length = STAT_NOTCOMPUTE;
- version.length = STAT_NOTCOMPUTE;
-}
-
-// 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);
class NHttpMsgStart: public NHttpMsgSection {
public:
- NHttpMsgStart() {};
- void initSection();
void analyze();
void genEvents();
protected:
- // "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.
+ NHttpMsgStart(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_, NHttpEnums::SourceId sourceId_) :
+ NHttpMsgSection(buffer, bufSize, sessionData_, sourceId_) {};
virtual void parseStartLine() = 0;
void deriveVersionId();
- // This is where all the derived values, extracted message parts, and normalized values are.
- // Note that this is all scalars, buffer pointers, and buffer sizes. The actual buffers are in the message buffer (raw pieces) or the
- // scratchPad (normalized pieces).
field startLine;
field version;
};
using namespace NHttpEnums;
-// Reinitialize everything derived in preparation for analyzing a new message
-void NHttpMsgStatus::initSection() {
- NHttpMsgStart::initSection();
- statusCode.length = STAT_NOTCOMPUTE;
- reasonPhrase.length = STAT_NOTCOMPUTE;
-}
-
// All the header processing that is done for every message (i.e. not just-in-time) is done here.
void NHttpMsgStatus::analyze() {
NHttpMsgStart::analyze();
if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
}
-void NHttpMsgStatus::printSection(FILE *output) const {
+void NHttpMsgStatus::printSection(FILE *output) {
NHttpMsgSection::printMessageTitle(output, "status line");
fprintf(output, "Version Id: %d\n", versionId);
fprintf(output, "Status Code Num: %d\n", statusCodeNum);
class NHttpMsgStatus: public NHttpMsgStart {
public:
- NHttpMsgStatus() {};
- void initSection();
+ NHttpMsgStatus(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_, NHttpEnums::SourceId sourceId_) :
+ NHttpMsgStart(buffer, bufSize, sessionData_, sourceId_) {};
void analyze();
- void printSection(FILE *output) const;
+ void printSection(FILE *output);
void genEvents();
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
- // things into a standard form without changing the underlying format.
void parseStartLine();
void deriveStatusCodeNum();
- // This is where all the derived values, extracted message parts, and normalized values are.
- // Note that this is all scalars, buffer pointers, and buffer sizes. The actual buffers are in the message buffer (raw pieces) or the
- // scratchPad (normalized pieces).
field statusCode;
field reasonPhrase;
};
if (infractions != 0) SnortEventqAdd(NHTTP_GID, EVENT_ASCII); // I'm just an example event
}
-void NHttpMsgTrailer::printSection(FILE *output) const {
+void NHttpMsgTrailer::printSection(FILE *output) {
NHttpMsgSection::printMessageTitle(output, "trailer");
NHttpMsgHeadShared::printHeaders(output);
NHttpMsgSection::printMessageWrapup(output);
class NHttpMsgTrailer: public NHttpMsgHeadShared {
public:
- NHttpMsgTrailer() {};
- void printSection(FILE *output) const;
+ NHttpMsgTrailer(const uint8_t *buffer, const uint16_t bufSize, NHttpFlowData *sessionData_, NHttpEnums::SourceId sourceId_) :
+ NHttpMsgHeadShared(buffer, bufSize, sessionData_, sourceId_) {};
+ void printSection(FILE *output);
void genEvents();
void updateFlow();
};
public:
ScratchPad(uint32_t _capacity) : capacity(_capacity), buffer(new uint64_t[_capacity/8+1]) {};
~ScratchPad() { delete[] buffer; };
- void reinit() {used = 0;};
+ /* &&& not needed anymore I think */ 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
// Convenience function. All the housekeeping that must be done before we can return PAF_FLUSH to stream.
void NHttpStreamSplitter::prepareFlush(NHttpFlowData* sessionData, uint32_t* flushOffset, SourceId sourceId, SectionType sectionType, bool tcpClose,
uint64_t infractions, uint32_t numOctets) {
- sessionData->sourceId = sourceId;
- sessionData->sectionType = sectionType;
- sessionData->tcpClose = tcpClose;
- sessionData->infractions = infractions;
+ sessionData->sectionType[sourceId] = sectionType;
+ sessionData->tcpClose[sourceId] = tcpClose;
+ sessionData->infractions[sourceId] = infractions;
if (tcpClose) sessionData->typeExpected[sourceId] = SEC_CLOSED;
if (!NHttpTestInput::test_input) *flushOffset = numOctets;
else NHttpTestInput::testInput->pafFlush(numOctets);
{ TRANSCODE_DEFLATE, "deflate"},
{ 0, nullptr} };
-const HeaderNormalizer NHttpMsgHeadShared::NORMALIZER_NIL {NORM_NULL, false, false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
-const HeaderNormalizer NHttpMsgHeadShared::NORMALIZER_BASIC {NORM_FIELD, false, false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
-const HeaderNormalizer NHttpMsgHeadShared::NORMALIZER_CAT {NORM_FIELD, true, false, normRemoveLws, nullptr, nullptr, nullptr, nullptr, nullptr};
-const HeaderNormalizer NHttpMsgHeadShared::NORMALIZER_NOREPEAT {NORM_FIELD, false, true, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
-const HeaderNormalizer NHttpMsgHeadShared::NORMALIZER_DECIMAL {NORM_INT64, false, true, normDecimalInteger, nullptr, nullptr, nullptr, nullptr, nullptr};
-const HeaderNormalizer NHttpMsgHeadShared::NORMALIZER_TRANSCODE {NORM_ENUM64, true, false, normRemoveLws, nullptr, norm2Lower, nullptr, normSeqStrCode, NHttpMsgHeadShared::transCodeList};
+const HeaderNormalizer NHttpMsgHeadShared::NORMALIZER_NIL {NORM_NULL, false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgHeadShared::NORMALIZER_BASIC {NORM_FIELD, false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgHeadShared::NORMALIZER_CAT {NORM_FIELD, true, normRemoveLws, nullptr, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgHeadShared::NORMALIZER_DECIMAL {NORM_INT64, false, normDecimalInteger, nullptr, nullptr, nullptr, nullptr, nullptr};
+const HeaderNormalizer NHttpMsgHeadShared::NORMALIZER_TRANSCODE {NORM_ENUM64, true, normRemoveLws, nullptr, norm2Lower, nullptr, normSeqStrCode, NHttpMsgHeadShared::transCodeList};
const HeaderNormalizer* const NHttpMsgHeadShared::headerNorms[HEAD__MAXVALUE] = { [0] = &NORMALIZER_NIL,
[HEAD__OTHER] = &NORMALIZER_BASIC,
}
-uint16_t NHttpTestInput::toEval(uint8_t **buffer, int64_t &testNumber_) {
+uint16_t NHttpTestInput::toEval(uint8_t **buffer, int64_t &testNumber_, SourceId &sourceId) {
if (!flushed) return 0;
testNumber_ = testNumber;
+ sourceId = lastSourceId;
*buffer = msgBuf;
if (fillOctets > 0) {
uint32_t fillOut = (fillOctets <= 16384) ? fillOctets : 16384;
~NHttpTestInput();
void toPaf(uint8_t*& data, uint32_t &length, NHttpEnums::SourceId &sourceId, bool &tcpClose, bool &needBreak);
void pafFlush(uint32_t length);
- uint16_t toEval(uint8_t **buffer, int64_t &testNumber);
+ uint16_t toEval(uint8_t **buffer, int64_t &testNumber, NHttpEnums::SourceId &sourceId);
// Hard for NHttpInspect and PAF to share these without making them "global". This is as good a place as any for them to live.
static bool test_input;
class NHttpUri {
public:
- NHttpUri(const uint8_t* start, int32_t length, NHttpEnums::MethodId method) : methodId(method) {
- uri.length = length; uri.start = start; };
-
+ NHttpUri(const uint8_t* start, int32_t length, NHttpEnums::MethodId method) : uri(length, start), methodId(method) {};
field getUri() const { return uri; };
NHttpEnums::UriType getUriType() { parseUri(); return uriType; };
field getScheme() { parseUri(); return scheme; };