typedef enum { SEC_DISCARD = -10, SEC_CLOSED = -9, SEC_ABORT = -8, SEC__NOTCOMPUTE=-4, SEC__NOTPRESENT=-1, SEC_REQUEST = 2,
SEC_STATUS, SEC_HEADER, SEC_BODY, SEC_CHUNKHEAD, SEC_CHUNKBODY, SEC_TRAILER } SectionType;
+// Result of scanning by splitter
+typedef enum { SCAN_NOTFOUND, SCAN_FOUND, SCAN_DISCARD } ScanResult;
+
// Result of processing a message section--what needs to happen next
typedef enum { RES_INSPECT, RES_IGNORE, RES_AGGREGATE, RES_FLUSHCHUNKS } ProcessResult;
using namespace NHttpEnums;
-SectionType NHttpRequestSplitter::split(const uint8_t* buffer, uint32_t length) {
+ScanResult NHttpRequestSplitter::split(const uint8_t* buffer, uint32_t length) {
for (uint32_t k = 0; k < length; k++) {
// Count the alternating <CR> and <LF> characters we have seen in a row
if (((buffer[k] == '\r') && (num_crlf == 0)) ||
}
num_flush = k+1;
// If the first two octets are CRLF then they must be discarded.
- return ((octets_seen + k + 1) == 2) ? SEC_DISCARD : SEC_REQUEST;
+ return ((octets_seen + k + 1) == 2) ? SCAN_DISCARD : SCAN_FOUND;
}
octets_seen += length;
- return SEC__NOTPRESENT;
+ return SCAN_NOTFOUND;
}
-SectionType NHttpStatusSplitter::split(const uint8_t* buffer, uint32_t length) {
+ScanResult NHttpStatusSplitter::split(const uint8_t* buffer, uint32_t length) {
for (uint32_t k = 0; k < length; k++) {
// Count the alternating <CR> and <LF> characters we have seen in a row
if (((buffer[k] == '\r') && (num_crlf == 0)) ||
}
num_flush = k+1;
// If the first two octets are CRLF then they must be discarded.
- return ((octets_seen + k + 1) == 2) ? SEC_DISCARD : SEC_STATUS;
+ return ((octets_seen + k + 1) == 2) ? SCAN_DISCARD : SCAN_FOUND;
}
octets_seen += length;
- return SEC__NOTPRESENT;
+ return SCAN_NOTFOUND;
}
-SectionType NHttpChunkHeaderSplitter::split(const uint8_t* buffer, uint32_t length) {
+ScanResult NHttpChunkHeaderSplitter::split(const uint8_t* buffer, uint32_t length) {
for (uint32_t k = 0; k < length; k++) {
// Count the alternating <CR> and <LF> characters we have seen in a row
if (((buffer[k] == '\r') && (num_crlf == 0)) ||
}
num_flush = k+1;
// If the first two octets are CRLF then they must be discarded.
- return ((octets_seen + k + 1) == 2) ? SEC_DISCARD : SEC_CHUNKHEAD;
+ return ((octets_seen + k + 1) == 2) ? SCAN_DISCARD : SCAN_FOUND;
}
octets_seen += length;
- return SEC__NOTPRESENT;
+ return SCAN_NOTFOUND;
}
-SectionType NHttpHeaderSplitter::split(const uint8_t* buffer, uint32_t length) {
- if (peek_status == SEC_HEADER) {
- return SEC_HEADER;
+ScanResult NHttpHeaderSplitter::split(const uint8_t* buffer, uint32_t length) {
+ if (peek_status == SCAN_FOUND) {
+ return SCAN_FOUND;
}
buffer += peek_octets;
length -= peek_octets;
num_crlf++;
if ((num_crlf == 2) && (octets_seen + k + 1) == 2) {
num_flush = k+1;
- return SEC_HEADER;
+ return SCAN_FOUND;
}
if (num_crlf < 4) {
continue;
continue;
}
num_flush = k + 1 + peek_octets;
- return SEC_HEADER;
+ return SCAN_FOUND;
}
octets_seen += length;
- return SEC__NOTPRESENT;
+ return SCAN_NOTFOUND;
}
-SectionType NHttpHeaderSplitter::peek(const uint8_t* buffer, uint32_t length) {
+ScanResult NHttpHeaderSplitter::peek(const uint8_t* buffer, uint32_t length) {
assert(octets_seen == 0);
peek_status = split(buffer, length);
peek_octets = length;
}
-SectionType NHttpTrailerSplitter::split(const uint8_t* buffer, uint32_t length) {
+ScanResult NHttpTrailerSplitter::split(const uint8_t* buffer, uint32_t length) {
for (uint32_t k = 0; k < length; k++) {
// Count the alternating <CR> and <LF> characters we have seen in a row
if (((buffer[k] == '\r') && (num_crlf%2 == 0)) ||
num_crlf++;
if ((num_crlf == 2) && (octets_seen + k + 1) == 2) {
num_flush = k+1;
- return SEC_TRAILER;
+ return SCAN_FOUND;
}
if (num_crlf < 4) {
continue;
continue;
}
num_flush = k+1;
- return SEC_TRAILER;
+ return SCAN_FOUND;
}
octets_seen += length;
- return SEC__NOTPRESENT;
+ return SCAN_NOTFOUND;
}
public:
virtual ~NHttpSplitter() = default;
virtual void reset() { octets_seen = 0; num_crlf = 0; num_flush = 0; };
- virtual NHttpEnums::SectionType split(const uint8_t* buffer, uint32_t length) = 0;
- virtual NHttpEnums::SectionType peek(const uint8_t*, uint32_t) { assert(0); return NHttpEnums::SEC_ABORT; };
+ virtual NHttpEnums::ScanResult split(const uint8_t* buffer, uint32_t length) = 0;
+ virtual NHttpEnums::ScanResult peek(const uint8_t*, uint32_t) { assert(0); return NHttpEnums::SCAN_NOTFOUND; };
uint32_t get_num_flush() { return num_flush; };
virtual uint32_t get_octets_seen() { return octets_seen; };
class NHttpRequestSplitter : public NHttpSplitter {
public:
- NHttpEnums::SectionType split(const uint8_t* buffer, uint32_t length);
+ NHttpEnums::ScanResult split(const uint8_t* buffer, uint32_t length);
};
class NHttpStatusSplitter : public NHttpSplitter {
public:
- NHttpEnums::SectionType split(const uint8_t* buffer, uint32_t length);
+ NHttpEnums::ScanResult split(const uint8_t* buffer, uint32_t length);
};
class NHttpHeaderSplitter : public NHttpSplitter {
public:
- NHttpEnums::SectionType split(const uint8_t* buffer, uint32_t length);
- NHttpEnums::SectionType peek(const uint8_t* buffer, uint32_t length);
- void reset() { NHttpSplitter::reset(); peek_octets = 0; peek_status = NHttpEnums::SEC__NOTPRESENT; };
+ NHttpEnums::ScanResult split(const uint8_t* buffer, uint32_t length);
+ NHttpEnums::ScanResult peek(const uint8_t* buffer, uint32_t length);
+ void reset() { NHttpSplitter::reset(); peek_octets = 0; peek_status = NHttpEnums::SCAN_NOTFOUND; };
uint32_t get_octets_seen() { return octets_seen - peek_octets; };
private:
uint32_t peek_octets = 0;
- NHttpEnums::SectionType peek_status = NHttpEnums::SEC__NOTPRESENT;
+ NHttpEnums::ScanResult peek_status = NHttpEnums::SCAN_NOTFOUND;
};
class NHttpChunkHeaderSplitter : public NHttpSplitter {
public:
- NHttpEnums::SectionType split(const uint8_t* buffer, uint32_t length);
+ NHttpEnums::ScanResult split(const uint8_t* buffer, uint32_t length);
};
class NHttpTrailerSplitter : public NHttpSplitter {
public:
- NHttpEnums::SectionType split(const uint8_t* buffer, uint32_t length);
+ NHttpEnums::ScanResult split(const uint8_t* buffer, uint32_t length);
};
#endif
session_data->header_octets_visible[source_id] = 0;
}
-StreamSplitter::Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* data, uint32_t length, uint32_t, uint32_t* flush_offset) {
+// Convenience function. Size buffer required to accommodate the current section plus possible aggregation.
+uint32_t NHttpStreamSplitter::size_buffer_needed(unsigned total, NHttpEnums::SectionType type,
+ uint32_t possible_additional) {
+ switch (type) {
+ case SEC_CHUNKBODY:
+ return 16384;
+ case SEC_REQUEST:
+ case SEC_STATUS:
+ case SEC_HEADER:
+ return total + possible_additional;
+ default:
+ return total;
+ }
+}
+
+StreamSplitter::Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* data, uint32_t length, uint32_t,
+ uint32_t* flush_offset) {
+
assert(length <= 63780);
// When the system begins providing TCP connection close information this won't always be false. FIXIT-H
bool tcp_close = false;
// This is the session state information we share with HTTP Inspect and store with stream. A session is defined
- // by a TCP connection. Since PAF is the first to see a new TCP connection the new flow data object is created here.
+ // by a TCP connection. Since scan() is the first to see a new TCP connection the new flow data object is created
+ // here.
NHttpFlowData* session_data = (NHttpFlowData*)flow->get_application_data(NHttpFlowData::nhttp_flow_id);
if (session_data == nullptr) flow->set_application_data(session_data = new NHttpFlowData);
assert(session_data != nullptr);
const uint32_t max_length = (length <= (63780 - splitter->get_octets_seen())) ? length :
(63780 - splitter->get_octets_seen());
- const SectionType split_result = splitter->split(data, max_length);
- if (split_result == SEC__NOTPRESENT) {
+ const ScanResult split_result = splitter->split(data, max_length);
+ if (split_result == SCAN_NOTFOUND) {
if (splitter->get_octets_seen() == 63780) {
// FIXIT-M need to implement processing and detection instead of just discarding this data
session_data->type_expected[source_id] = SEC_ABORT;
return StreamSplitter::FLUSH;
}
const uint32_t flush_octets = splitter->get_num_flush();
- if (split_result == SEC_DISCARD) {
+ if (split_result == SCAN_DISCARD) {
prepare_flush(session_data, flush_offset, source_id, SEC_DISCARD, tcp_close && (flush_octets == length), 0,
flush_octets, length);
splitter->reset();
if ((type == SEC_REQUEST) || (type == SEC_STATUS)) {
// Look ahead to see if entire header section is already here so we can aggregate it for detection.
const uint32_t peek_max_length = ((length - flush_octets <= 63780)) ? (length - flush_octets) : 63780;
- if (session_data->header_splitter[source_id].peek(data + flush_octets, peek_max_length) == SEC_HEADER) {
+ if (session_data->header_splitter[source_id].peek(data + flush_octets, peek_max_length) == SCAN_FOUND) {
session_data->header_octets_visible[source_id] = session_data->header_splitter[source_id].get_num_flush();
}
}
}
}
-const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned /*total FIXIT-H */, unsigned offset, const uint8_t* data,
+const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total, unsigned offset, const uint8_t* data,
unsigned len, uint32_t flags, unsigned& copied)
{
static THREAD_LOCAL StreamBuffer nhttp_buf;
}
data = test_buffer;
offset = 0;
+ total = len;
}
+ assert(total <= 63780);
+ assert(offset+len <= total);
+
bool is_chunk_body = session_data->section_type[source_id] == SEC_CHUNKBODY;
uint8_t*& chunk_buffer = session_data->chunk_buffer[source_id];
int32_t& buffer_length = !is_chunk_body ? session_data->section_buffer_length[source_id] : chunk_buffer_length;
if (buffer == nullptr) {
- buffer = new uint8_t[65536];
+ buffer = new uint8_t[size_buffer_needed(total, session_data->section_type[source_id],
+ session_data->unused_octets_visible[source_id])];
}
memcpy(buffer + buffer_length + offset, data, len);
}
else {
// Because of aggregation chunk body sections do not go to Inspector on schedule or in chronological order
- // with respect to otherchunks. That means NHttpMsgChunkBody::update_flow() cannot do it design-intended
+ // with respect to chunk header sections. That means NHttpMsgChunkBody::update_flow() cannot do it design-intended
// job of updating type_expected in time for StreamSplitter to find the next chunk header. So we do it here.
session_data->type_expected[source_id] = SEC_CHUNKHEAD;
void prepare_flush(NHttpFlowData* session_data, uint32_t* flush_offset, NHttpEnums::SourceId source_id,
NHttpEnums::SectionType section_type, bool tcp_close, uint64_t infractions, uint32_t num_octets, uint32_t length);
void create_event(NHttpEnums::EventSid sid);
+ uint32_t size_buffer_needed(unsigned total, NHttpEnums::SectionType type, uint32_t possible_additional);
NHttpInspect* const my_inspector;