]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
splitter understands chunk headers
authorTom Peters <thopeter@cisco.com>
Wed, 24 Sep 2014 18:16:26 +0000 (14:16 -0400)
committerTom Peters <thopeter@cisco.com>
Wed, 24 Sep 2014 18:16:26 +0000 (14:16 -0400)
19 files changed:
src/service_inspectors/nhttp_inspect/CMakeLists.txt
src/service_inspectors/nhttp_inspect/Makefile.am
src/service_inspectors/nhttp_inspect/nhttp_enum.h
src/service_inspectors/nhttp_inspect/nhttp_flow_data.cc
src/service_inspectors/nhttp_inspect/nhttp_flow_data.h
src/service_inspectors/nhttp_inspect/nhttp_inspect.cc
src/service_inspectors/nhttp_inspect/nhttp_inspect.h
src/service_inspectors/nhttp_inspect/nhttp_msg_chunk.cc [moved from src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.cc with 73% similarity]
src/service_inspectors/nhttp_inspect/nhttp_msg_chunk.h [moved from src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.h with 82% similarity]
src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.cc [deleted file]
src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.h [deleted file]
src/service_inspectors/nhttp_inspect/nhttp_msg_header.cc
src/service_inspectors/nhttp_inspect/nhttp_splitter.cc
src/service_inspectors/nhttp_inspect/nhttp_splitter.h
src/service_inspectors/nhttp_inspect/nhttp_stream_splitter.cc
src/service_inspectors/nhttp_inspect/nhttp_tables.cc
src/service_inspectors/nhttp_inspect/nhttp_test_input.cc
src/service_inspectors/nhttp_inspect/nhttp_test_msgs.txt
src/service_inspectors/nhttp_inspect/nhttp_transaction.cc

index 63148f6d9c42757f1ba13d423c8c648a764218f5..ed0c8d6ccff58508bbc5d6e9b50182442aa9a786 100644 (file)
@@ -16,10 +16,8 @@ set (FILE_LIST
     nhttp_msg_header.h
     nhttp_msg_body.cc
     nhttp_msg_body.h
-    nhttp_msg_chunk_head.cc
-    nhttp_msg_chunk_head.h
-    nhttp_msg_chunk_body.cc
-    nhttp_msg_chunk_body.h
+    nhttp_msg_chunk.cc
+    nhttp_msg_chunk.h
     nhttp_msg_trailer.cc
     nhttp_msg_trailer.h
     nhttp_head_norm.cc
index b32aa79df66785996af054207fdea0651c79cdc6..6937754c0b90a8400c849c15b460269ec6ccbedf 100644 (file)
@@ -9,8 +9,7 @@ nhttp_msg_status.cc nhttp_msg_status.h \
 nhttp_msg_head_shared.cc nhttp_msg_head_shared.h \
 nhttp_msg_header.cc nhttp_msg_header.h \
 nhttp_msg_body.cc nhttp_msg_body.h \
-nhttp_msg_chunk_head.cc nhttp_msg_chunk_head.h \
-nhttp_msg_chunk_body.cc nhttp_msg_chunk_body.h \
+nhttp_msg_chunk.cc nhttp_msg_chunk.h \
 nhttp_msg_trailer.cc nhttp_msg_trailer.h \
 nhttp_head_norm.cc nhttp_head_norm.h \
 nhttp_uri.cc nhttp_uri.h \
index 87a2098ebbf6d057401c108fca59195b2b5bffad..a4842d250f57f9c08d0b9274d7f684f9d6ecb367 100644 (file)
@@ -47,13 +47,13 @@ typedef enum { SRC__NOTCOMPUTE=-4, SRC_CLIENT=0, SRC_SERVER=1 } SourceId;
 
 // Type of message section
 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;
+   SEC_STATUS, SEC_HEADER, SEC_BODY, SEC_CHUNK, SEC_TRAILER } SectionType;
 
 // Result of scanning by splitter
-typedef enum { SCAN_NOTFOUND, SCAN_FOUND, SCAN_DISCARD } ScanResult;
+typedef enum { SCAN_NOTFOUND, SCAN_FOUND, SCAN_DISCARD, SCAN_ABORT } ScanResult;
 
 // Result of processing a message section--what needs to happen next
-typedef enum { RES_INSPECT, RES_IGNORE, RES_AGGREGATE, RES_FLUSHCHUNKS } ProcessResult;
+typedef enum { RES_INSPECT, RES_IGNORE, RES_AGGREGATE } ProcessResult;
 
 // 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"
index aab974dd27ee189da637f579c58635487af52822..4bd6455bb3e17ced686f5c2a85ce8e06a50d38cb 100644 (file)
@@ -58,7 +58,6 @@ void NHttpFlowData::half_reset(SourceId source_id) {
 
     data_length[source_id] = STAT_NOTPRESENT;
     body_octets[source_id] = STAT_NOTPRESENT;
-    num_chunks[source_id] = STAT_NOTPRESENT;
 }
 
 void NHttpFlowData::show(FILE* out_file) const {
@@ -70,7 +69,6 @@ void NHttpFlowData::show(FILE* out_file) const {
     fprintf(out_file, "Type expected: %d/%d\n", type_expected[0], type_expected[1]);
     fprintf(out_file, "Data length: %" PRIi64 "/%" PRIi64 "\n", data_length[0], data_length[1]);
     fprintf(out_file, "Body octets: %" PRIi64 "/%" PRIi64 "\n", body_octets[0], body_octets[1]);
-    fprintf(out_file, "Number of chunks: %" PRIi64 "/%" PRIi64 "\n", num_chunks[0], num_chunks[1]);
     fprintf(out_file, "Unused octets visible: %u/%u\n", unused_octets_visible[0], unused_octets_visible[1]);
     fprintf(out_file, "Header octets visible: %u/%u\n", header_octets_visible[0], header_octets_visible[1]);
     fprintf(out_file, "Section buffer length: %d/%d\n", section_buffer_length[0], section_buffer_length[1]);
index 3c3f037c1e9fb93d31bbb7b719bd2c876bf439c9..626fe2b0fb9460bb74c59e78424e72a551210631 100644 (file)
@@ -51,8 +51,7 @@ public:
     friend class NHttpMsgRequest;
     friend class NHttpMsgStatus;
     friend class NHttpMsgBody;
-    friend class NHttpMsgChunkHead;
-    friend class NHttpMsgChunkBody;
+    friend class NHttpMsgChunk;
     friend class NHttpMsgTrailer;
     friend class NHttpStreamSplitter;
     friend class NHttpTransaction;
@@ -64,7 +63,7 @@ private:
     NHttpRequestSplitter request_splitter[2];
     NHttpStatusSplitter status_splitter[2];
     NHttpHeaderSplitter header_splitter[2];
-    NHttpChunkHeaderSplitter chunkhead_splitter[2];
+    NHttpChunkSplitter chunk_splitter[2];
     NHttpTrailerSplitter trailer_splitter[2];
     uint32_t unused_octets_visible[2] = { 0, 0 };
     uint32_t header_octets_visible[2] = { 0, 0 };
@@ -81,7 +80,7 @@ private:
 
     // Inspector => StreamSplitter (facts about the message section that is coming next)
     NHttpEnums::SectionType type_expected[2] = { NHttpEnums::SEC_REQUEST, NHttpEnums::SEC_STATUS };
-    int64_t data_length[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };   // length of the data from Content-Length field or chunk header.      
+    int64_t data_length[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };   // length of the data from Content-Length field      
 
     // Inspector's internal data about the current message
     // Some items don't apply in both directions. Have two copies anyway just to simplify code and minimize
@@ -91,7 +90,6 @@ private:
     int32_t status_code_num[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };
 
     int64_t body_octets[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };   // number of user data octets seen so far (regular body or chunks)
-    int64_t num_chunks[2] = { NHttpEnums::STAT_NOTPRESENT, NHttpEnums::STAT_NOTPRESENT };    // number of chunks seen so far
 
     // Transaction management including pipelining
     NHttpTransaction* transaction[2] = { nullptr, nullptr };
index aea9ff86a1bd9c252bf39f7526c1cdd8d1857cd4..ecf4ddeaef25dacf6dcfba24d25c62ac6d3818f6 100644 (file)
@@ -113,8 +113,7 @@ ProcessResult NHttpInspect::process(const uint8_t* data, const uint16_t dsize, F
       case SEC_STATUS: msg_section = new NHttpMsgStatus(data, dsize, session_data, source_id, buf_owner); break;
       case SEC_HEADER: msg_section = new NHttpMsgHeader(data, dsize, session_data, source_id, buf_owner); break;
       case SEC_BODY: msg_section = new NHttpMsgBody(data, dsize, session_data, source_id, buf_owner); break;
-      case SEC_CHUNKHEAD: msg_section = new NHttpMsgChunkHead(data, dsize, session_data, source_id, buf_owner); break;
-      case SEC_CHUNKBODY: msg_section = new NHttpMsgChunkBody(data, dsize, session_data, source_id, buf_owner); break;
+      case SEC_CHUNK: msg_section = new NHttpMsgChunk(data, dsize, session_data, source_id, buf_owner); break;
       case SEC_TRAILER: msg_section = new NHttpMsgTrailer(data, dsize, session_data, source_id, buf_owner); break;
       case SEC_DISCARD: if (buf_owner) delete[] data; return RES_IGNORE;
       default: assert(0); if (buf_owner) delete[] data; return RES_IGNORE;
index 8409aaebe631aa0b5f3d334966dcf9651f3efb2a..2db56a415fa8c0cfd4f7468e8a6d2b7ddc4763e5 100644 (file)
@@ -32,8 +32,7 @@
 #include "nhttp_msg_status.h"
 #include "nhttp_msg_header.h"
 #include "nhttp_msg_body.h"
-#include "nhttp_msg_chunk_head.h"
-#include "nhttp_msg_chunk_body.h"
+#include "nhttp_msg_chunk.h"
 #include "nhttp_msg_trailer.h"
 #include "nhttp_test_manager.h"
 #include "nhttp_stream_splitter.h"
similarity index 73%
rename from src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.cc
rename to src/service_inspectors/nhttp_inspect/nhttp_msg_chunk.cc
index 9755a29bbbdfaeaeb50cef52081933bdf61d6783..04ce04b8629bfa13282145bcab76a59ed058ffdb 100644 (file)
@@ -23,7 +23,7 @@
 //
 //  @author     Tom Peters <thopeter@cisco.com>
 //
-//  @brief      NHttpMsgChunkBody class analyzes data portion (not start line) of an HTTP chunk.
+//  @brief      NHttpMsgChunk class analyzes HTTP chunked body.
 //
 
 
 
 #include "snort.h"
 #include "nhttp_enum.h"
-#include "nhttp_msg_chunk_body.h"
+#include "nhttp_msg_chunk.h"
 
 using namespace NHttpEnums;
 
-NHttpMsgChunkBody::NHttpMsgChunkBody(const uint8_t *buffer, const uint16_t buf_size, NHttpFlowData *session_data_,
+NHttpMsgChunk::NHttpMsgChunk(const uint8_t *buffer, const uint16_t buf_size, NHttpFlowData *session_data_,
    SourceId source_id_, bool buf_owner) : NHttpMsgBody(buffer, buf_size, session_data_, source_id_, buf_owner)
 {
    transaction->set_body(this);
 }
 
-void NHttpMsgChunkBody::analyze() {
-    body_octets += msg_text.length;
+void NHttpMsgChunk::gen_events() {}
 
-    data.start = msg_text.start;
-    data.length = msg_text.length;
-
-    if (tcp_close) infractions |= INF_TRUNCATED;
-}
-
-
-void NHttpMsgChunkBody::gen_events() {}
-
-void NHttpMsgChunkBody::print_section(FILE *output) {
-    NHttpMsgSection::print_message_title(output, "chunk body");
+void NHttpMsgChunk::print_section(FILE *output) {
+    NHttpMsgSection::print_message_title(output, "chunk");
     fprintf(output, "Cumulative octets %" PRIi64 "\n", body_octets);
     data.print(output, "Data");
     NHttpMsgSection::print_message_wrapup(output);
 }
 
-void NHttpMsgChunkBody::update_flow() {
+void NHttpMsgChunk::update_flow() {
     if (tcp_close) {
         session_data->type_expected[source_id] = SEC_CLOSED;
         session_data->half_reset(source_id);
     }
     else {
+        // Zero-length chunk is not visible here. StreamSplitter updates expected_type to SEC_TRAILER when necessary.
         session_data->body_octets[source_id] = body_octets;
     }
 }
similarity index 82%
rename from src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_body.h
rename to src/service_inspectors/nhttp_inspect/nhttp_msg_chunk.h
index 06de6e841b4e70f8e05444e0fb97435a85da15e0..1ec187ff33ca1845565e2dcb5867f13782120b1d 100644 (file)
 //
 //  @author     Tom Peters <thopeter@cisco.com>
 //
-//  @brief      NHttpMsgChunkBody class declaration
+//  @brief      NHttpMsgChunk class declaration
 //
 
-#ifndef NHTTP_MSG_CHUNK_BODY_H
-#define NHTTP_MSG_CHUNK_BODY_H
+#ifndef NHTTP_MSG_CHUNK_H
+#define NHTTP_MSG_CHUNK_H
 
 #include "nhttp_msg_body.h"
 
 //-------------------------------------------------------------------------
-// NHttpMsgChunkBody class
+// NHttpMsgChunk class
 //-------------------------------------------------------------------------
 
-class NHttpMsgChunkBody : public NHttpMsgBody {
+class NHttpMsgChunk : public NHttpMsgBody {
 public:
-    NHttpMsgChunkBody(const uint8_t *buffer, const uint16_t buf_size, NHttpFlowData *session_data_,
+    NHttpMsgChunk(const uint8_t *buffer, const uint16_t buf_size, NHttpFlowData *session_data_,
        NHttpEnums::SourceId source_id_, bool buf_owner);
-    void analyze();
     void print_section(FILE *output);
     void gen_events();
     void update_flow();
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.cc b/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.cc
deleted file mode 100644 (file)
index ac00a61..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-/****************************************************************************
- *
-** 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      NHttpMsgChunkHead class analyzes header line for a chunk.
-//
-
-
-#include <assert.h>
-#include <string.h>
-#include <sys/types.h>
-#include <stdio.h>
-
-#include "snort.h"
-#include "nhttp_enum.h"
-#include "nhttp_msg_chunk_head.h"
-
-using namespace NHttpEnums;
-
-NHttpMsgChunkHead::NHttpMsgChunkHead(const uint8_t *buffer, const uint16_t buf_size, NHttpFlowData *session_data_,
-   SourceId source_id_, bool buf_owner) :
-   NHttpMsgSection(buffer, buf_size, session_data_, source_id_, buf_owner),
-   num_chunks(session_data->num_chunks[source_id])
-{
-   transaction->set_body(this);
-}
-
-
-// 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
-// vastly bigger than any legitimate chunk.
-void NHttpMsgChunkHead::derive_chunk_length() {
-    if (chunk_size.length <= 0) {
-        data_length = STAT_NOSOURCE;
-        infractions |= INF_BADCHUNKSIZE;
-        return;
-    }
-    data_length = 0;
-    int non_leading_zeros = 0;
-    for (int k=0; k < chunk_size.length; k++) {
-        if (non_leading_zeros || (chunk_size.start[k] != '0')) non_leading_zeros++;
-        if (non_leading_zeros > 15) {
-            data_length = STAT_PROBLEMATIC;
-            infractions |= INF_BADCHUNKSIZE;
-            return;
-        }
-
-        data_length *= 16;
-        if ((chunk_size.start[k] >= '0') && (chunk_size.start[k] <= '9')) data_length += chunk_size.start[k] - '0';
-        else if ((chunk_size.start[k] >= 'A') && (chunk_size.start[k] <= 'F')) data_length += chunk_size.start[k] - 'A' + 10;
-        else if ((chunk_size.start[k] >= 'a') && (chunk_size.start[k] <= 'f')) data_length += chunk_size.start[k] - 'a' + 10;
-        else {
-            data_length = STAT_PROBLEMATIC;
-            infractions |= INF_BADCHUNKSIZE;
-            return;
-        }
-    }
-}
-
-void NHttpMsgChunkHead::analyze() {
-    // First section in a new chunk is just the start line.
-    num_chunks++;
-    start_line.start = msg_text.start;
-    if (!tcp_close) {
-        start_line.length = msg_text.length - 2;
-    }
-    else {
-        start_line.length = find_crlf(start_line.start, msg_text.length, false);
-    }
-    chunk_size.start = msg_text.start;
-    // Start line format is chunk size in hex followed by optional semicolon and extensions field
-    for (chunk_size.length = 0; (chunk_size.length < start_line.length) && (start_line.start[chunk_size.length] != ';'); chunk_size.length++);
-    if (chunk_size.length == start_line.length) {
-        chunk_extensions.length = STAT_NOTPRESENT;
-    }
-    else if (chunk_size.length == start_line.length - 1) {
-        chunk_extensions.length = STAT_EMPTYSTRING;
-    }
-    else {
-        chunk_extensions.start = msg_text.start + chunk_size.length + 1;
-        chunk_extensions.length = start_line.length - chunk_size.length - 1;
-    }
-    derive_chunk_length();
-    if (tcp_close) infractions |= INF_TRUNCATED;
-}
-
-void NHttpMsgChunkHead::gen_events() {}
-
-void NHttpMsgChunkHead::print_section(FILE *output) {
-    NHttpMsgSection::print_message_title(output, "chunk header");
-    fprintf(output, "Chunk size: %" PRIi64 "\n", data_length);
-    chunk_extensions.print(output, "Chunk extensions");
-    NHttpMsgSection::print_message_wrapup(output);
-}
-
-void NHttpMsgChunkHead::update_flow() {
-    if (tcp_close) {
-        session_data->type_expected[source_id] = SEC_CLOSED;
-        session_data->half_reset(source_id);
-    }
-    else if (data_length > 0) {
-        session_data->type_expected[source_id] = SEC_CHUNKBODY;
-        session_data->num_chunks[source_id] = num_chunks;
-        session_data->data_length[source_id] = data_length;
-    }
-    else { // FIXIT-H what if data_length is bad (probable loss of sync)?
-        // This was zero-length last chunk, trailer comes next
-        session_data->type_expected[source_id] = SEC_TRAILER;
-    }
-}
-
-ProcessResult NHttpMsgChunkHead::worth_detection() {
-    return ((data_length > 0) && !tcp_close) ? RES_IGNORE : RES_FLUSHCHUNKS;
-}
-
-// Legacy support function. Puts message fields into the buffers used by old Snort.
-void NHttpMsgChunkHead::legacy_clients() {
-    ClearHttpBuffers();
-    legacy_request();
-    legacy_status();
-    legacy_header(false);
-    if (start_line.length > 0) SetHttpBuffer(HTTP_BUFFER_CLIENT_BODY, start_line.start, (unsigned)start_line.length);
-}
-
-
-
-
-
-
-
-
-
-
diff --git a/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.h b/src/service_inspectors/nhttp_inspect/nhttp_msg_chunk_head.h
deleted file mode 100644 (file)
index 606b9fb..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/****************************************************************************
- *
-** 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      NHttpMsgChunkHead class declaration
-//
-
-#ifndef NHTTP_MSG_CHUNK_HEAD_H
-#define NHTTP_MSG_CHUNK_HEAD_H
-
-#include "nhttp_msg_section.h"
-#include "nhttp_field.h"
-
-//-------------------------------------------------------------------------
-// NHttpMsgChunkHead class
-//-------------------------------------------------------------------------
-
-class NHttpMsgChunkHead : public NHttpMsgSection {
-public:
-    NHttpMsgChunkHead(const uint8_t *buffer, const uint16_t buf_size, NHttpFlowData *session_data_,
-       NHttpEnums::SourceId source_id_, bool buf_owner);
-    void analyze();
-    void print_section(FILE *output);
-    void gen_events();
-    void update_flow();
-    void legacy_clients();
-    NHttpEnums::ProcessResult worth_detection();
-
-private:
-    void derive_chunk_length();
-
-    Field start_line;
-    Field chunk_size;
-    Field chunk_extensions;
-
-    int64_t data_length = NHttpEnums::STAT_NOTCOMPUTE;
-    int64_t num_chunks;
-};
-
-#endif
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
index ab039d601d3df6c68d1cca59da7b88e12ad0a69a..c7f5bd5b2fc1eff80e2e0c2af564fe139e1e0641 100644 (file)
@@ -90,9 +90,8 @@ void NHttpMsgHeader::update_flow() {
              (get_header_value_norm(HEAD_TRANSFER_ENCODING).length - 8))) == TRANSCODE_CHUNKED) ) {
         // FIXIT-M inspect for Content-Length header which should not be present
         // Chunked body
-        session_data->type_expected[source_id] = SEC_CHUNKHEAD;
+        session_data->type_expected[source_id] = SEC_CHUNK;
         session_data->body_octets[source_id] = 0;
-        session_data->num_chunks[source_id] = 0;
     }
     else if ((get_header_value_norm(HEAD_CONTENT_LENGTH).length > 0) &&
             (*(int64_t*)header_value_norm[HEAD_CONTENT_LENGTH].start > 0)) {
index b1eb274ce61e7733aac4a9f583d5993ecd7b61d5..419aa13d2d8f400afcff0fdd529031dcdca5a4ea 100644 (file)
 //  @brief      Section-specific splitters
 //
 
+//#include "nhttp_uri_norm.h"
 #include "nhttp_splitter.h"
 
 using namespace NHttpEnums;
 
+void NHttpSplitter::conditional_reset() {
+    if (complete) {
+        octets_seen = 0;
+        num_crlf = 0;
+        num_flush = 0;
+        complete = false;
+    }
+}
+
 ScanResult NHttpRequestSplitter::split(const uint8_t* buffer, uint32_t length) {
+    conditional_reset();
     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)) ||
@@ -46,6 +57,7 @@ ScanResult NHttpRequestSplitter::split(const uint8_t* buffer, uint32_t length) {
         }
         num_flush = k+1;
         // If the first two octets are CRLF then they must be discarded.
+        complete = true;
         return ((octets_seen + k + 1) == 2) ? SCAN_DISCARD : SCAN_FOUND;
     }
     octets_seen += length;
@@ -53,6 +65,7 @@ ScanResult NHttpRequestSplitter::split(const uint8_t* buffer, uint32_t length) {
 }
 
 ScanResult NHttpStatusSplitter::split(const uint8_t* buffer, uint32_t length) {
+    conditional_reset();
     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)) ||
@@ -74,29 +87,78 @@ ScanResult NHttpStatusSplitter::split(const uint8_t* buffer, uint32_t length) {
     return SCAN_NOTFOUND;
 }
 
-ScanResult NHttpChunkHeaderSplitter::split(const uint8_t* buffer, uint32_t length) {
+ScanResult NHttpChunkSplitter::split(const uint8_t* buffer, uint32_t length) {
+    conditional_reset();
+    if (header_complete) {
+        // Previously read the chunk header. Now just flush the length.
+        num_flush = expected_length;
+        complete = true;
+        return SCAN_FOUND;
+    }
     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;
+        if (buffer[k] == '\r') {
+            num_crlf = 1;
+            continue;
+        }
+        if ((buffer[k] == '\n') && (num_crlf == 1)) {
+            if ((octets_seen + k + 1) == 2) {
+                // \r\n leftover from previous chunk
+                complete = true;
+                num_flush = k+1;
+                return SCAN_DISCARD;
+            }
+            if (!length_started) {
+                // chunk header specifies no length
+                complete = true;
+                return SCAN_ABORT;
             }
+            // flush completed chunk header
+            header_complete = true;
+            num_flush = k+1;
+            return SCAN_DISCARD;
         }
-        else {
-            num_crlf = 0;
+        num_crlf = 0;
+        if (buffer[k] == ';') {
+            semicolon = true;
+        }
+        if (semicolon) {
+            // we don't look at chunk header extensions
             continue;
         }
-        num_flush = k+1;
-        // If the first two octets are CRLF then they must be discarded.
-        return ((octets_seen + k + 1) == 2) ? SCAN_DISCARD : SCAN_FOUND;
+        if (as_hex[buffer[k]] == -1) {
+            // illegal character present in chunk length
+            complete = true;
+            return SCAN_ABORT;
+        }
+        length_started = true;
+        if (digits_seen >= 8) {
+            // overflow protection: must fit into 32 bits
+            complete = true;
+            return SCAN_ABORT;
+        }
+        expected_length = expected_length * 16 + as_hex[buffer[k]];
+        if (expected_length > 0) {
+            // leading zeroes don't count
+            digits_seen++;
+        }
     }
     octets_seen += length;
     return SCAN_NOTFOUND;
 }
 
+void NHttpChunkSplitter::conditional_reset() {
+    if (complete) {
+        expected_length = 0;
+        length_started = false;
+        digits_seen = 0;
+        semicolon = false;
+        header_complete = false;
+    }
+    NHttpSplitter::conditional_reset();
+}
+
 ScanResult NHttpHeaderSplitter::split(const uint8_t* buffer, uint32_t length) {
+    conditional_reset();
     if (peek_status == SCAN_FOUND) {
         return SCAN_FOUND;
     }
@@ -110,6 +172,7 @@ ScanResult NHttpHeaderSplitter::split(const uint8_t* buffer, uint32_t length) {
             num_crlf++;
             if ((num_crlf == 2) && (octets_seen + k + 1) == 2) {
                 num_flush = k+1;
+                complete = true;
                 return SCAN_FOUND;
             }
             if (num_crlf < 4) {
@@ -121,6 +184,7 @@ ScanResult NHttpHeaderSplitter::split(const uint8_t* buffer, uint32_t length) {
             continue;
         }
         num_flush = k + 1 + peek_octets;
+        complete = true;
         return SCAN_FOUND;
     }
     octets_seen += length;
@@ -134,8 +198,16 @@ ScanResult NHttpHeaderSplitter::peek(const uint8_t* buffer, uint32_t length) {
     return peek_status;
 }
 
+void NHttpHeaderSplitter::conditional_reset() {
+    if (complete) {
+        peek_octets = 0;
+        peek_status = SCAN_NOTFOUND;
+    }
+    NHttpSplitter::conditional_reset();
+}
 
 ScanResult NHttpTrailerSplitter::split(const uint8_t* buffer, uint32_t length) {
+    conditional_reset();
     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)) ||
@@ -143,6 +215,7 @@ ScanResult NHttpTrailerSplitter::split(const uint8_t* buffer, uint32_t length) {
             num_crlf++;
             if ((num_crlf == 2) && (octets_seen + k + 1) == 2) {
                 num_flush = k+1;
+                complete = true;
                 return SCAN_FOUND;
             }
             if (num_crlf < 4) {
@@ -154,6 +227,7 @@ ScanResult NHttpTrailerSplitter::split(const uint8_t* buffer, uint32_t length) {
             continue;
         }
         num_flush = k+1;
+        complete = true;
         return SCAN_FOUND;
     }
     octets_seen += length;
index 24c819bd125c9a96e4e1a6f230c25c90c9e3f368..ecf745a7a1eedc730b9cde7c1c4614b3c416603a 100644 (file)
 class NHttpSplitter {
 public:
     virtual ~NHttpSplitter() = default;
-    virtual void reset() { octets_seen = 0; num_crlf = 0; num_flush = 0; };
     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; };
 
 protected:
+    static const int8_t as_hex[256];
+
     uint32_t octets_seen = 0;
     uint32_t num_crlf = 0;
     uint32_t num_flush = 0;
+    bool complete = false;
+
+    virtual void conditional_reset();
 };
 
 class NHttpRequestSplitter : public NHttpSplitter {
@@ -65,16 +69,23 @@ class NHttpHeaderSplitter : public NHttpSplitter {
 public:
     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; };
+    void conditional_reset();
     uint32_t get_octets_seen() { return octets_seen - peek_octets; };
 private:
     uint32_t peek_octets = 0;
     NHttpEnums::ScanResult peek_status = NHttpEnums::SCAN_NOTFOUND;
 };
 
-class NHttpChunkHeaderSplitter : public NHttpSplitter {
+class NHttpChunkSplitter : public NHttpSplitter {
 public:
     NHttpEnums::ScanResult split(const uint8_t* buffer, uint32_t length);
+    void conditional_reset();
+private:
+    uint32_t expected_length = 0;
+    bool length_started = false;
+    uint32_t digits_seen = 0;
+    bool semicolon = false;
+    bool header_complete = false;
 };
 
 class NHttpTrailerSplitter : public NHttpSplitter {
index 5d3b8eda0119b3371e9348c7a3e8cbd5637f7004..21a382b0e33caccce2636b587e34e3a6aff3b3ff 100644 (file)
@@ -43,28 +43,42 @@ using namespace NHttpEnums;
 // Convenience function. All the housekeeping that must be done before we can return FLUSH to stream.
 void NHttpStreamSplitter::prepare_flush(NHttpFlowData* session_data, uint32_t* flush_offset, SourceId source_id,
       SectionType section_type, bool tcp_close, uint64_t infractions, uint32_t num_octets, uint32_t length) {
-    assert(num_octets > 0);
     session_data->section_type[source_id] = section_type;
     session_data->tcp_close[source_id] = tcp_close;
     session_data->infractions[source_id] = infractions;
+    switch (section_type) {
+      case SEC_BODY:
+        paf_max = 16384;
+        break;
+      case SEC_CHUNK:
+        paf_max = 16384 - session_data->chunk_buffer_length[source_id];
+        if (num_octets == 0) {
+            session_data->type_expected[source_id] = SEC_TRAILER;
+        }
+        break;
+      default:
+        paf_max = 63780;
+        break;
+    }
     if (tcp_close) {
         session_data->type_expected[source_id] = SEC_CLOSED;
     }
     if (!NHttpTestManager::use_test_input()) {
-        *flush_offset = num_octets;
+        // when TCP connection closes do not flush octets that have not arrived yet
+        *flush_offset = (!tcp_close || (num_octets <= length)) ? num_octets : length;
     }
     else {
-        NHttpTestManager::get_test_input_source()->flush(num_octets);
+        NHttpTestManager::get_test_input_source()->flush((!tcp_close || (num_octets <= length)) ? num_octets : length);
     }
-    session_data->unused_octets_visible[source_id] = length - num_octets;
+    session_data->unused_octets_visible[source_id] = (length >= num_octets) ? length - num_octets : 0;
     session_data->header_octets_visible[source_id] = 0;
 }
 
-// Convenience function. Size buffer required to accommodate the current section plus possible aggregation.
+// Convenience function. Size buffer required to reassemble 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:
+      case SEC_CHUNK:
         return 16384;
       case SEC_REQUEST:
       case SEC_STATUS:
@@ -116,16 +130,15 @@ StreamSplitter::Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* dat
     switch (type) {
       case SEC_REQUEST:
       case SEC_STATUS:
-      case SEC_CHUNKHEAD:
+      case SEC_CHUNK:
       case SEC_HEADER:
       case SEC_TRAILER:
       {
-        paf_max = 63780;
         NHttpSplitter* splitter;
         switch (type) {
           case SEC_REQUEST: splitter = (NHttpSplitter*)&session_data->request_splitter[source_id]; break;
           case SEC_STATUS: splitter = (NHttpSplitter*)&session_data->status_splitter[source_id]; break;
-          case SEC_CHUNKHEAD: splitter = (NHttpSplitter*)&session_data->chunkhead_splitter[source_id]; break;
+          case SEC_CHUNK: splitter = (NHttpSplitter*)&session_data->chunk_splitter[source_id]; break;
           case SEC_HEADER: splitter = (NHttpSplitter*)&session_data->header_splitter[source_id]; break;
           case SEC_TRAILER: splitter = (NHttpSplitter*)&session_data->trailer_splitter[source_id]; break;
           default: assert(0); break;
@@ -134,42 +147,45 @@ StreamSplitter::Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* dat
         const uint32_t max_length = (length <= (63780 - splitter->get_octets_seen())) ? length :
            (63780 - splitter->get_octets_seen());
         const ScanResult split_result = splitter->split(data, max_length);
-        if (split_result == SCAN_NOTFOUND) {
+        switch (split_result) {
+          case SCAN_NOTFOUND:
             if (splitter->get_octets_seen() == 63780) {
-                // FIXIT-M need to implement processing and detection instead of just discarding this data
+                prepare_flush(session_data, flush_offset, source_id, SEC_DISCARD, false, 0, 0, length);
                 session_data->type_expected[source_id] = SEC_ABORT;
                 return StreamSplitter::ABORT;
             }
-            if (!tcp_close) {
-                // Incomplete headers wait patiently for more data
-                return StreamSplitter::SEARCH;
+            if (tcp_close) {
+                prepare_flush(session_data, flush_offset, source_id, type, true, INF_TRUNCATED, length, length);
+                return StreamSplitter::FLUSH;
             }
-            prepare_flush(session_data, flush_offset, source_id, type, true, INF_TRUNCATED, length, length);
-            splitter->reset();
+            // Incomplete headers wait patiently for more data
+            return StreamSplitter::SEARCH;
+          case SCAN_ABORT:
+            prepare_flush(session_data, flush_offset, source_id, SEC_DISCARD, false, 0, 0, length);
+            session_data->type_expected[source_id] = SEC_ABORT;
             return StreamSplitter::FLUSH;
-        }
-        const uint32_t flush_octets = splitter->get_num_flush();
-        if (split_result == SCAN_DISCARD) {
-            prepare_flush(session_data, flush_offset, source_id, SEC_DISCARD, tcp_close && (flush_octets == length), 0,
+          case SCAN_DISCARD: {
+            const uint32_t flush_octets = splitter->get_num_flush();
+            prepare_flush(session_data, flush_offset, source_id, SEC_DISCARD, tcp_close && (flush_octets >= length), 0,
                flush_octets, length);
-            splitter->reset();
             return StreamSplitter::FLUSH;
-        }
-        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.
-            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) == SCAN_FOUND) {
-                session_data->header_octets_visible[source_id] = session_data->header_splitter[source_id].get_num_flush();
+          }
+          case SCAN_FOUND: {
+            const uint32_t flush_octets = splitter->get_num_flush();
+            prepare_flush(session_data, flush_offset, source_id, type, tcp_close && (flush_octets == length), 0,
+               flush_octets, 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.
+                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) == SCAN_FOUND) {
+                    session_data->header_octets_visible[source_id] = session_data->header_splitter[source_id].get_num_flush();
+                }
             }
+            return StreamSplitter::FLUSH;
+          }
         }
-        return StreamSplitter::FLUSH;
       }
       case SEC_BODY:
-      case SEC_CHUNKBODY:
-        paf_max = 16384 - session_data->chunk_buffer_length[source_id];
         if ((!tcp_close) || (length > session_data->data_length[source_id])) {
             prepare_flush(session_data, flush_offset, source_id, type, false, 0, session_data->data_length[source_id],
                length);
@@ -203,7 +219,7 @@ const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total,
         }
         uint8_t* test_buffer;
         NHttpTestManager::get_test_input_source()->reassemble(&test_buffer, len, source_id, session_data);
-        if (len == 0) {
+        if (test_buffer == nullptr) {
             // There is no more test data
             return nullptr;
         }
@@ -215,12 +231,12 @@ const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total,
     assert(total <= 63780);
     assert(offset+len <= total);
 
-    bool is_chunk_body = session_data->section_type[source_id] == SEC_CHUNKBODY;
+    bool is_chunk = session_data->section_type[source_id] == SEC_CHUNK;
 
     uint8_t*& chunk_buffer = session_data->chunk_buffer[source_id];
     int32_t& chunk_buffer_length = session_data->chunk_buffer_length[source_id];
-    uint8_t*& buffer = !is_chunk_body ? session_data->section_buffer[source_id] : chunk_buffer;
-    int32_t& buffer_length = !is_chunk_body ? session_data->section_buffer_length[source_id] : chunk_buffer_length;
+    uint8_t*& buffer = !is_chunk ? session_data->section_buffer[source_id] : chunk_buffer;
+    int32_t& buffer_length = !is_chunk ? session_data->section_buffer_length[source_id] : chunk_buffer_length;
 
     if (buffer == nullptr) {
         buffer = new uint8_t[size_buffer_needed(total, session_data->section_type[source_id],
@@ -230,29 +246,28 @@ const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total,
     memcpy(buffer + buffer_length + offset, data, len);
     if (flags & PKT_PDU_TAIL) {
         ProcessResult send_to_detection;
-        if (!is_chunk_body) {
+        if (!is_chunk) {
             // start line/headers/body individual section processing with aggregation prior to being sent to detection
             // only the last section added to the buffer goes to the inspector
             send_to_detection = my_inspector->process(buffer + buffer_length, offset + len, flow, source_id,
                buffer_length == 0);
         }
         else {
-            // Because of aggregation chunk body sections do not go to Inspector on schedule or in chronological order
-            // 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;
-
             // small chunks are aggregated before processing and are kept here until the buffer is full (paf_max)
-            // all the chunks in the buffer go to the inspector together
+            // all the chunks in the buffer go to the inspector together. Zero-length chunk flushes accumulated chunks.
             int32_t total_chunk_len = chunk_buffer_length + offset + len;
-            if (total_chunk_len < 16384) {
-                paf_max = 16384 - total_chunk_len;
+            if ((total_chunk_len < 16384) && (len != 0)) {
                 chunk_buffer_length = total_chunk_len;
                 return nullptr;
             }
-            else {
-                paf_max = 16384;
+            if (total_chunk_len == 0) {
+                // Zero-length chunk cannot be processed by itself.
+                delete[] chunk_buffer;
+                chunk_buffer = nullptr;
+                chunk_buffer_length = 0;
+                return nullptr;
             }
+            paf_max = 16384;
             send_to_detection = my_inspector->process(chunk_buffer, total_chunk_len, flow, source_id, true);
         }
 
@@ -274,26 +289,6 @@ const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total,
           case RES_AGGREGATE:
             buffer_length += offset + len;
             return nullptr;
-          case RES_FLUSHCHUNKS:
-            buffer = nullptr;
-            buffer_length = 0;
-            if (chunk_buffer != nullptr) {
-                // FIXIT-M these three variables about the buffered chunks need to be managed properly
-                session_data->section_type[source_id] = SEC_CHUNKBODY;
-                session_data->tcp_close[source_id] = false;
-                session_data->infractions[source_id] = 0;
-
-                my_inspector->process(chunk_buffer, chunk_buffer_length, flow, source_id, true);
-                nhttp_buf.data = chunk_buffer;
-                nhttp_buf.length = chunk_buffer_length;
-                chunk_buffer = nullptr;
-                chunk_buffer_length = 0;
-                if (NHttpTestManager::use_test_output()) {
-                    fprintf(NHttpTestManager::get_output_file(), "Flushed chunks for detection %u octets\n\n", nhttp_buf.length);
-                }
-                return &nhttp_buf;
-            }
-            return nullptr;
         }
     }
     return nullptr;
index 3615f822c51af10b9da685d31ed978c11e14d695..5f637fa70bfad0be640d4c5dce815c08638a7683 100644 (file)
@@ -43,6 +43,7 @@
 #include "nhttp_msg_header.h"
 #include "nhttp_module.h"
 #include "nhttp_uri_norm.h"
+#include "nhttp_splitter.h"
 
 using namespace NHttpEnums;
 
@@ -301,22 +302,29 @@ const RuleMap NHttpModule::nhttp_events[] =
     { 0, nullptr }
 };
 
+const int8_t NHttpSplitter::as_hex[256] = {
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
 
+   -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 
+   -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 
-
-
-
-
-
-
-
-
-
-
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1  };
 
 
index 4c7a6af83af0450047b58edc3e114413a739d6bd..1fafd20ce93665aba37144f0f5cb431e01f2eda0 100644 (file)
@@ -224,6 +224,11 @@ void NHttpTestInput::flush(uint32_t length) {
 
 
 void NHttpTestInput::reassemble(uint8_t **buffer, unsigned &length, SourceId &source_id, NHttpFlowData* session_data) {
+    if (!just_flushed) {
+        length = 0;
+        *buffer = nullptr;
+        return;
+    }
     source_id = last_source_id;
     *buffer = msg_buf;
 
index 15f223babb0c13afbd260a86baaffafa01a45143..4693d4164b88411382d229ee169b473a38821979 100644 (file)
@@ -1,11 +1,11 @@
-# Enter HTTP test message text as you want it to be presented to PAF.
+# Enter HTTP test message text as you want it to be presented to the StreamSplitter.
 #
-# The easiest way to format is to put a blank line between message sections so that each message section is its own "paragraph".
-# Within a paragraph the placement of new lines does not have any effect. Format a paragraph any way you are comfortable. Extra blank lines
-# between paragraphs also do not have any effect.
+# The easiest way to format is to put a blank line between message sections so that each message section is its own "paragraph". Within a paragraph
+# the placement of single new lines does not have any effect. Format a paragraph any way you are comfortable. Extra blank lines between paragraphs
+# also do not have any effect.
 #
 # Each paragraph represents a TCP segment. The splitter can be tested by putting multiple sections in the same paragraph (splitter must split)
-# or continuing a section in the next paragraph (splitter must search and reassamble).
+# or continuing a section in the next paragraph (splitter must search and reassemble).
 #
 # It is not necessary to specify complete body and chunk body sections. Specify at least one octet that will begin the section and the
 # remainder will be autofilled in 16384-octet sections up to the length flushed.
 # least one character of body/chunk data either as part of the previous header segment or at the beginning of a new segment following the headers.
 # All data bytes included in the file will be used followed by required fill data in the pattern ABC...XYZABC...
 #
+# Fill data will not be provided for a paragraph that is preceded by tcpclose. The body or chunk will terminate at the end of the paragraph.
+#
 # There must not be excess data for a test case. Once a data stream ends with a TCP close there must be a break command before further data is sent.
 # Similarly a message section that triggers an abort of processing must end the paragraph and be followed by a break. These rules apply half-duplex
 # so it would be possible to send data in the opposite direction before the break.
 #
-# FIXIT-L it would be useful if a paragraph could continue following an abort to simulate a processing abort mid-segment.
-#
 # This test tool does not implement the feature of being hardened against bad input. If you write a badly formatted or improper test case the
 # program may assert or crash. The responsibility is on the developer to get it right. Currently that is the best use of resources.
 
@@ -605,9 +605,9 @@ HTTP/1.1 208 Example with chunks\r\nTransfer-Encoding: chunked\r\n\r\n
 
 chunkoflength138
 
-e6\r\nchunkoflength230
+\r\ne6\r\nchunkoflength230
 
-0\r\n
+\r\n0\r\n
 
 \r\n
 
@@ -627,27 +627,27 @@ Accept: *\r\n
 1C320\r\n
 chunk1
 
-1F40; testing-the-extension-feature\r\n
+\r\n1F40; testing-the-extension-feature\r\n
 chunk2
 
-13FFE;  \r\n
+\r\n13FFE;  \r\n
 
 chunk3
 
-4000;\r\n
+\r\n4000;\r\n
 chunk4
 
-3FFF;\r\n
+\r\n3FFF;\r\n
 
 chunk5
 
-3FFE\r\n
+\r\n3FFE\r\n
 chunk6
 
-4001;\r\n
+\r\n4001;\r\n
 chunk7
 
-00000;chunkextension=3\r\n
+\r\n00000;chunkextension=3\r\n
 
 date: Sun, 01 Oct 2000 23:25:17 GMT\r\n
 X-madeupdate: Mon, 02 Oct 2000 23:25:17 GMT\r\n
@@ -749,13 +749,15 @@ Transfer-Encoding: chunked\r\n
 A000\r\n
 chunk1-start
 
-B000\r\n
+\r\nB000\r\n
 chunk2-start
 
-C000\r\n
+\r\nC000\r\n
 
 chunk3-start
 
+\r\n
+
 0\r\n
 date: Mon, 02 Oct 2000 20:25:00 GMT\r\n
 fakeheader: 1234\r\n
index 4d0f46bdd168f1852d61d349c1d6b05bb2886ae8..3998d34c6135fbbe0fcc628c79adceb632a445bf 100644 (file)
@@ -33,9 +33,6 @@
 #include "nhttp_msg_request.h"
 #include "nhttp_msg_status.h"
 #include "nhttp_msg_header.h"
-#include "nhttp_msg_body.h"
-#include "nhttp_msg_chunk_head.h"
-#include "nhttp_msg_chunk_body.h"
 #include "nhttp_msg_trailer.h"
 
 using namespace NHttpEnums;
@@ -107,5 +104,3 @@ NHttpTransaction* NHttpTransaction::attach_my_transaction(NHttpFlowData* session
 }
 
 
-
-