--- /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 Section-specific splitters
+//
+
+#include "nhttp_splitter.h"
+
+using namespace NHttpEnums;
+
+SectionType NHttpStartSplitter::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)) ||
+ ((buffer[k] == '\n') && (num_crlf == 1))) {
+ num_crlf++;
+ if (num_crlf < 2) {
+ continue;
+ }
+ }
+ else {
+ num_crlf = 0;
+ continue;
+ }
+ 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;
+ }
+ octets_seen += length;
+ return SEC__NOTPRESENT;
+}
+
+SectionType NHttpHeaderSplitter::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)) ||
+ ((buffer[k] == '\n') && (num_crlf%2 == 1))) {
+ num_crlf++;
+ if ((num_crlf == 2) && (octets_seen + k + 1) == 2) {
+ num_flush = k+1;
+ return SEC_HEADER;
+ }
+ if (num_crlf < 4) {
+ continue;
+ }
+ }
+ else {
+ num_crlf = 0;
+ continue;
+ }
+ num_flush = k+1;
+ return SEC_HEADER;
+ }
+ octets_seen += length;
+ return SEC__NOTPRESENT;
+}
+
+
+
--- /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 NHttpSplitter class and subclasses declarations
+//
+
+#ifndef NHTTP_SPLITTER_H
+#define NHTTP_SPLITTER_H
+
+#include "nhttp_enum.h"
+
+//-------------------------------------------------------------------------
+// NHttpSplitter class
+//-------------------------------------------------------------------------
+
+class NHttpSplitter {
+public:
+ 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 ~NHttpSplitter() = default;
+ uint32_t get_num_flush() { return num_flush; };
+ uint32_t get_octets_seen() { return octets_seen; };
+
+protected:
+ uint32_t octets_seen = 0;
+ uint32_t num_crlf = 0;
+ uint32_t num_flush = 0;
+};
+
+class NHttpStartSplitter : public NHttpSplitter {
+public:
+ NHttpEnums::SectionType split(const uint8_t* buffer, uint32_t length);
+};
+
+class NHttpHeaderSplitter : public NHttpSplitter {
+public:
+ NHttpEnums::SectionType split(const uint8_t* buffer, uint32_t length);
+};
+
+#endif
+
#include "nhttp_enum.h"
#include "nhttp_test_manager.h"
#include "nhttp_test_input.h"
+#include "nhttp_splitter.h"
#include "nhttp_stream_splitter.h"
#include "nhttp_inspect.h"
else {
NHttpTestManager::get_test_input_source()->flush(num_octets);
}
- session_data->octets_seen[source_id] = 0;
- session_data->num_crlf[source_id] = 0;
session_data->peek_ahead_octets[source_id] = 0;
session_data->unused_octets_visible[source_id] = length - num_octets;
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) {
+ assert(length <= 63780);
+
// When the system begins providing TCP connection close information this won't always be false. FIXIT-H
bool tcp_close = false;
0, session_data->header_octets_visible[source_id], length);
return StreamSplitter::FLUSH;
}
+ // Did we peek ahead and not find the complete headers?
+ if (session_data->peek_ahead_octets[source_id] > 0) {
+ assert(length == session_data->peek_ahead_octets[source_id]);
+ session_data->peek_ahead_octets[source_id] = 0;
+ return StreamSplitter::SEARCH;
+ }
switch (type) {
case SEC_REQUEST:
case SEC_STATUS:
- case SEC_HEADER:
case SEC_CHUNKHEAD:
- case SEC_TRAILER:
+ case SEC_HEADER:
+ case SEC_TRAILER: {
paf_max = 63780;
- for (uint32_t k = session_data->peek_ahead_octets[source_id]; k < length; k++) {
- session_data->octets_seen[source_id]++;
- // Count the alternating <CR> and <LF> characters we have seen in a row
- if (((data[k] == '\r') && (session_data->num_crlf[source_id]%2 == 0)) ||
- ((data[k] == '\n') && (session_data->num_crlf[source_id]%2 == 1))) {
- session_data->num_crlf[source_id]++;
- }
- else {
- session_data->num_crlf[source_id] = 0;
- }
-
- // If the first two octets are CRLF then flush them separately. We are 1) DISCARDing CRLF some
- // 1.0 implementation put following previous message, 2) DISCARDing CRLF between chunk and following
- // chunk header, and 3) flushing normal empty header or trailer.
- if ((session_data->num_crlf[source_id] == 2) && (session_data->octets_seen[source_id] == 2)) {
- prepare_flush(session_data, flush_offset, source_id,
- ((type == SEC_REQUEST) || (type == SEC_STATUS) || (type == SEC_CHUNKHEAD)) ? SEC_DISCARD : type,
- tcp_close && (k == length-1), 0, k+1, length);
- return StreamSplitter::FLUSH;
- }
- // The start line and chunk header section always end with the first <CRLF>
- else if ((session_data->num_crlf[source_id] == 2) &&
- ((type == SEC_REQUEST) || (type == SEC_STATUS) || (type == SEC_CHUNKHEAD))) {
- prepare_flush(session_data, flush_offset, source_id, type, tcp_close && (k == length-1), 0, k+1, length);
- 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.
- for (uint32_t m = k+1; m < length; m++) {
- session_data->octets_seen[source_id]++;
- // Count the alternating <CR> and <LF> characters we have seen in a row
- if (((data[m] == '\r') && (session_data->num_crlf[source_id]%2 == 0)) ||
- ((data[m] == '\n') && (session_data->num_crlf[source_id]%2 == 1))) {
- session_data->num_crlf[source_id]++;
- }
- else {
- session_data->num_crlf[source_id] = 0;
- }
- if ( (session_data->num_crlf[source_id] == 4) ||
- ((session_data->num_crlf[source_id] == 2) && (session_data->octets_seen[source_id] == 2))) {
- session_data->header_octets_visible[source_id] = m-k;
- return StreamSplitter::FLUSH;
- }
- }
- session_data->peek_ahead_octets[source_id] = length - (k+1);
- }
- return StreamSplitter::FLUSH;
- }
- // The header and trailer sections always end with the first double <CRLF>
- else if (session_data->num_crlf[source_id] == 4) {
- prepare_flush(session_data, flush_offset, source_id, type, tcp_close && (k == length-1), 0, k+1, length);
- return StreamSplitter::FLUSH;
- }
- // We must do this to protect ourself from buffer overrun.
- else if (session_data->octets_seen[source_id] >= 63780) {
+ NHttpSplitter* splitter = ((type == SEC_HEADER) || (type == SEC_TRAILER)) ?
+ (NHttpSplitter*)&session_data->header_splitter[source_id] :
+ (NHttpSplitter*)&session_data->start_splitter[source_id];
+ 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) {
+ 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::ABORT;
}
+ if (!tcp_close) {
+ // Incomplete headers wait patiently for more data
+ return StreamSplitter::SEARCH;
+ }
+ prepare_flush(session_data, flush_offset, source_id, type, true, INF_TRUNCATED, length, length);
+ splitter->reset();
+ return StreamSplitter::FLUSH;
}
- session_data->peek_ahead_octets[source_id] = 0;
- // Incomplete headers wait patiently for more data
- if (!tcp_close) {
- return StreamSplitter::SEARCH;
- }
- // Discard the oddball case where the new "message" starts with <CR><close>
- else if ((session_data->octets_seen[source_id] == 1) && (session_data->num_crlf[source_id] == 1)) {
- prepare_flush(session_data, flush_offset, source_id, SEC_DISCARD, true, 0, length, length);
+ const uint32_t flush_octets = session_data->peek_ahead_octets[source_id] + splitter->get_num_flush();
+ if (split_result == SEC_DISCARD) {
+ prepare_flush(session_data, flush_offset, source_id, SEC_DISCARD, tcp_close && (flush_octets == length), 0,
+ flush_octets, length);
+ splitter->reset();
+ return StreamSplitter::FLUSH;
}
- // TCP connection close, flush the partial header
- else {
- prepare_flush(session_data, flush_offset, source_id, type, true, INF_TRUNCATED, length, length);
+ prepare_flush(session_data, flush_offset, source_id, type, 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.
+ NHttpSplitter* peek_splitter = &session_data->header_splitter[source_id];
+ const SectionType peek_result = peek_splitter->split(data + flush_octets, length - flush_octets);
+ if (peek_result == SEC_HEADER) {
+ session_data->header_octets_visible[source_id] = peek_splitter->get_num_flush();
+ peek_splitter->reset();
+ }
+ else {
+ session_data->peek_ahead_octets[source_id] = length - flush_octets;
+ }
}
return StreamSplitter::FLUSH;
+ }
case SEC_BODY:
case SEC_CHUNKBODY:
paf_max = 16384 - session_data->chunk_buffer_length[source_id];
}
else {
// The TCP connection has closed and this is the possibly incomplete final section
- prepare_flush(session_data, flush_offset, source_id, type, true, 0, length, length);
+ prepare_flush(session_data, flush_offset, source_id, type, true, 0, length, length);
}
return StreamSplitter::FLUSH;
case SEC_ABORT: