]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Parsing enhancements including LF without CR
authorTom Peters <thopeter@cisco.com>
Wed, 1 Oct 2014 15:38:10 +0000 (11:38 -0400)
committerTom Peters <thopeter@cisco.com>
Wed, 1 Oct 2014 15:38:10 +0000 (11:38 -0400)
13 files changed:
src/service_inspectors/nhttp_inspect/nhttp_flow_data.h
src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_head_shared.h
src/service_inspectors/nhttp_inspect/nhttp_msg_header.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_section.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_section.h
src/service_inspectors/nhttp_inspect/nhttp_msg_start.cc
src/service_inspectors/nhttp_inspect/nhttp_msg_trailer.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_stream_splitter.h
src/service_inspectors/nhttp_inspect/nhttp_test_msgs.txt

index c6d3a1a154e6899e0dc84f23e911a0c6f7d658f5..6693ca1b7b4ca8ed1b58c4366fa15581e0411735 100644 (file)
@@ -60,8 +60,7 @@ private:
     void half_reset(NHttpEnums::SourceId source_id);
 
     // StreamSplitter internal data
-    NHttpRequestSplitter request_splitter[2];
-    NHttpStatusSplitter status_splitter[2];
+    NHttpStartSplitter start_splitter[2];
     NHttpHeaderSplitter header_splitter[2];
     NHttpChunkSplitter chunk_splitter[2];
     NHttpTrailerSplitter trailer_splitter[2];
@@ -75,6 +74,7 @@ private:
     // StreamSplitter => Inspector (facts about the most recent message section)
     // 0 element refers to client request, 1 element refers to server response
     NHttpEnums::SectionType section_type[2] = { NHttpEnums::SEC__NOTCOMPUTE, NHttpEnums::SEC__NOTCOMPUTE };
+    uint32_t num_excess[2] = { 0, 0 };
     bool tcp_close[2] = { false, false };
     uint64_t infractions[2] = { 0, 0 };
 
index 60b66aaf7d7b67b2f0bf5b56b139947f87f60ad7..5f4412db5da5b53da46cb822aac5107b928220df 100644 (file)
@@ -40,7 +40,6 @@ using namespace NHttpEnums;
 
 // All the header processing that is done for every message (i.e. not just-in-time) is done here.
 void NHttpMsgHeadShared::analyze() {
-    parse_whole();
     parse_header_block();
     parse_header_lines();
     for (int j=0; j < num_headers; j++) {
@@ -49,64 +48,19 @@ void NHttpMsgHeadShared::analyze() {
     }
 }
 
-void NHttpMsgHeadShared::parse_whole() {
-    // Normal case with header fields
-    if (!tcp_close && (msg_text.length >= 5)) {
-        headers.start = msg_text.start;
-        headers.length = msg_text.length - 4;
-        assert(!memcmp(msg_text.start+msg_text.length-4, "\r\n\r\n", 4));
-    }
-    // Normal case no header fields
-    else if (!tcp_close) {
-        headers.length = STAT_NOTPRESENT;
-        assert((msg_text.length == 2) && !memcmp(msg_text.start, "\r\n", 2));
-    }
-    // Normal case with header fields and TCP connection close
-    else if ((msg_text.length >= 5) && !memcmp(msg_text.start+msg_text.length-4, "\r\n\r\n", 4)) {
-        headers.start = msg_text.start;
-        headers.length = msg_text.length - 4;
-    }
-    // Normal case no header fields and TCP connection close
-    else if ((msg_text.length == 2) && !memcmp(msg_text.start, "\r\n", 2)) {
-        headers.length = STAT_NOTPRESENT;
-    }
-    // Abnormal cases truncated by TCP connection close
-    else {
-        infractions |= INF_TRUNCATED;
-        // Lone <CR>
-        if ((msg_text.length == 1) && (msg_text.start[0] == '\r')) {
-            headers.length = STAT_NOTPRESENT;
-        }
-        // Truncation occurred somewhere in the header fields
-        else {
-            headers.start = msg_text.start;
-            headers.length = msg_text.length;
-            // When present, remove partial <CR><LF><CR><LF> sequence from the end
-            if ((msg_text.length >= 4) && !memcmp(msg_text.start+msg_text.length-3, "\r\n\r", 3)) headers.length -= 3;
-            else if ((msg_text.length >= 3) && !memcmp(msg_text.start+msg_text.length-2, "\r\n", 2)) headers.length -= 2;
-            else if ((msg_text.length >= 2) && (msg_text.start[msg_text.length-1] == '\r')) headers.length -= 1;
-        }
-    }
-}
-
 // Divide up the block of header fields into individual header field lines.
 void NHttpMsgHeadShared::parse_header_block() {
-    if (headers.length < 0) {
-        num_headers = STAT_NOSOURCE;
-        return;
-    }
-
     int32_t bytes_used = 0;
     num_headers = 0;
-    while (bytes_used < headers.length) {
-        header_line[num_headers].start = headers.start + bytes_used;
-        header_line[num_headers].length = find_crlf(header_line[num_headers].start, headers.length - bytes_used, true);
+    while (bytes_used < msg_text.length) {
+        header_line[num_headers].start = msg_text.start + bytes_used;
+        header_line[num_headers].length = find_crlf(header_line[num_headers].start, msg_text.length - bytes_used);
         bytes_used += header_line[num_headers++].length + 2;
         if (num_headers >= MAXHEADERS) {
              break;
         }
     }
-    if (bytes_used < headers.length) {
+    if (bytes_used < msg_text.length) {
         infractions |= INF_TOOMANYHEADERS;
     }
 }
index 94c58848e2aa86f2909d5934770f565a8898c808..6e014e3a3f680cd7179fe6ec10a64f7ffc554c42 100644 (file)
@@ -44,7 +44,7 @@ public:
     void gen_events();
 
     int32_t get_num_headers() const { return num_headers; };
-    const Field& get_headers() const { return headers; };
+    const Field& get_headers() const { return msg_text; };
     const Field& get_header_line(int k) const { return header_line[k]; };
     const Field& get_header_name(int k) const { return header_name[k]; };
     const Field& get_header_value(int k) const { return header_value[k]; };
@@ -72,15 +72,12 @@ protected:
     static const StrCode header_list[];
     static const StrCode trans_code_list[];
 
-    void parse_whole();
     void parse_header_block();
     void parse_header_lines();
     void derive_header_name_id(int index);
 
     void print_headers(FILE *output);
 
-    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 num_headers = NHttpEnums::STAT_NOTCOMPUTE;
index c7f5bd5b2fc1eff80e2e0c2af564fe139e1e0641..f005baefc8c3ac5bd9f00bb4de47adfe01b2990b 100644 (file)
@@ -119,7 +119,7 @@ ProcessResult NHttpMsgHeader::worth_detection() {
     }
 
     // Do not send empty headers by themselves to detection
-    return ((headers.length != STAT_NOTPRESENT) || (session_data->section_buffer_length[source_id] > 0))
+    return ((msg_text.length > 0) || (session_data->section_buffer_length[source_id] > 0))
        ? RES_INSPECT : RES_IGNORE;
 }
 
index dd1de27a14a6e45f2c2fd0d692475e58905df054..fb4ffbc938aef4f304e0b38b9c64f67824e2314e 100644 (file)
@@ -56,14 +56,13 @@ NHttpMsgSection::NHttpMsgSection(const uint8_t *buffer, const uint16_t buf_size,
    delete_msg_on_destruct(buf_owner)
 {}
 
-// 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
-// at the beginning of the next line indicate that the previous header has wrapped and is continuing on the next line.
-uint32_t NHttpMsgSection::find_crlf(const uint8_t* buffer, int32_t length, bool wrappable) {
+// Return the number of octets before the CRLF that ends a header. Return length if CRLF not present. CRLF does not
+// count when immediately followed by <SP> or <LF>. These whitespace characters at the beginning of the next line
+// indicate that the previous header has wrapped and is continuing on the next line.
+uint32_t NHttpMsgSection::find_crlf(const uint8_t* buffer, int32_t length) {
     for (int32_t k=0; k < length-1; k++) {
         if ((buffer[k] == '\r') && (buffer[k+1] == '\n'))
-            if (!wrappable || (k+2 >= length) || ((buffer[k+2] != ' ') && (buffer[k+2] != '\t'))) return k;
+            if ((k+2 >= length) || ((buffer[k+2] != ' ') && (buffer[k+2] != '\t'))) return k;
     }
     return length;
 }
index 06f63e52f01907ea717c28c5af2145046461d1da..31435a01f28eadf1e33b63f4f505f96e24c5a809 100644 (file)
@@ -59,7 +59,7 @@ protected:
        NHttpEnums::SourceId source_id_, bool buf_owner);
 
     // Convenience methods
-    static uint32_t find_crlf(const uint8_t* buffer, int32_t length, bool wrappable);
+    static uint32_t find_crlf(const uint8_t* buffer, int32_t length);
     void print_message_title(FILE *output, const char *title) const;
     void print_message_wrapup(FILE *output) const;
     void create_event(NHttpEnums::EventSid sid);
index c2c8bceaef666393f624ca2308b61a79ea9bb53f..5924383ce2f190eef6ff6540a47b809b5ebad300 100644 (file)
@@ -40,9 +40,7 @@ using namespace NHttpEnums;
 
 void NHttpMsgStart::analyze() {
     start_line.start = msg_text.start;
-    start_line.length = find_crlf(start_line.start, msg_text.length, false);
-    // special case of TCP close between CR and LF
-    if (tcp_close && (msg_text.length == start_line.length) && (start_line.start[start_line.length-1] == '\r')) start_line.length--;
+    start_line.length = msg_text.length;
     parse_start_line();
     derive_version_id();
 }
index ed4f7ce8915966a27bc4a344bd460519be9f9e92..17c5c654c4548216249f50e6e90bbfa753381b77 100644 (file)
@@ -68,7 +68,7 @@ void NHttpMsgTrailer::update_flow() {
 
 ProcessResult NHttpMsgTrailer::worth_detection() {
     // Do not send empty trailers to detection
-    return (headers.length != STAT_NOTPRESENT) ? RES_INSPECT : RES_IGNORE;
+    return (msg_text.length > 0) ? RES_INSPECT : RES_IGNORE;
 }
 
 // Legacy support function. Puts message fields into the buffers used by old Snort.
index 419aa13d2d8f400afcff0fdd529031dcdca5a4ea..b4320e081840f62a0c4fa50d0e5e44e8cf99d645 100644 (file)
@@ -40,48 +40,44 @@ void NHttpSplitter::conditional_reset() {
     }
 }
 
-ScanResult NHttpRequestSplitter::split(const uint8_t* buffer, uint32_t length) {
+ScanResult NHttpStartSplitter::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)) ||
-            ((buffer[k] == '\n') && (num_crlf == 1))) {
-            num_crlf++;
-            if (num_crlf < 2) {
-                continue;
+        // Discard magic six white space characters CR, LF, Tab, VT, FF, and SP when they occur before the start line.
+        // If we have seen nothing but white space so far ...
+        if (num_crlf == octets_seen + k) {
+            if ((buffer[k] == 32) || ((buffer[k] >= 9) && (buffer[k] <= 13))) {
+                if (num_crlf < MAX_LEADING_WHITESPACE) {
+                    num_crlf++;
+                    continue;
+                }
+                else {
+                    complete = true;
+                    return SCAN_ABORT;
+                }
+            }
+            if (num_crlf > 0) {
+                num_flush = k;           // current octet not flushed with white space
+                complete = true;
+                return SCAN_DISCARD;
             }
         }
-        else {
-            num_crlf = 0;
-            continue;
-        }
-        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;
-    return SCAN_NOTFOUND;
-}
 
-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)) ||
-            ((buffer[k] == '\n') && (num_crlf == 1))) {
+        // If we get this far then the leading white space issue is behind us and num_crlf was reset to zero
+        if (buffer[k] == '\n') {
             num_crlf++;
-            if (num_crlf < 2) {
-                continue;
-            }
+            num_flush = k+1;
+            complete = true;
+            return SCAN_FOUND;
         }
-        else {
-            num_crlf = 0;
-            continue;
+        if (num_crlf == 1) {
+            // CR not followed by LF
+            complete = true;
+            return SCAN_ABORT;
+        }
+        if (buffer[k] == '\r') {
+            num_crlf = 1;
         }
-        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;
     }
     octets_seen += length;
     return SCAN_NOTFOUND;
@@ -96,13 +92,9 @@ ScanResult NHttpChunkSplitter::split(const uint8_t* buffer, uint32_t length) {
         return SCAN_FOUND;
     }
     for (uint32_t k = 0; k < length; k++) {
-        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
+        if (buffer[k] == '\n') {
+            if (octets_seen + k == num_crlf) {
+                // \r\n or \n leftover from previous chunk
                 complete = true;
                 num_flush = k+1;
                 return SCAN_DISCARD;
@@ -117,7 +109,15 @@ ScanResult NHttpChunkSplitter::split(const uint8_t* buffer, uint32_t length) {
             num_flush = k+1;
             return SCAN_DISCARD;
         }
-        num_crlf = 0;
+        if (num_crlf == 1) {
+            // CR not followed by LF
+            complete = true;
+            return SCAN_ABORT;
+        }
+        if (buffer[k] == '\r') {
+            num_crlf = 1;
+            continue;
+        }
         if (buffer[k] == ';') {
             semicolon = true;
         }
@@ -164,29 +164,33 @@ ScanResult NHttpHeaderSplitter::split(const uint8_t* buffer, uint32_t length) {
     }
     buffer += peek_octets;
     length -= peek_octets;
-    peek_octets = 0;
     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))) {
+        if (buffer[k] == '\n') {
             num_crlf++;
-            if ((num_crlf == 2) && (octets_seen + k + 1) == 2) {
-                num_flush = k+1;
+            if ((first_lf == 0) && (num_crlf < octets_seen + k + 1)) {
+                first_lf = num_crlf;
+            }
+            else {
+                num_flush = k + 1 + peek_octets;
                 complete = true;
                 return SCAN_FOUND;
             }
-            if (num_crlf < 4) {
-                continue;
+        }
+        else if (buffer[k] == '\r') {
+            if (num_crlf == first_lf) {
+                num_crlf++;
+            }
+            else {
+                num_crlf = 1;
+                first_lf = 0;
             }
         }
         else {
             num_crlf = 0;
-            continue;
+            first_lf = 0;
         }
-        num_flush = k + 1 + peek_octets;
-        complete = true;
-        return SCAN_FOUND;
     }
+    peek_octets = 0;
     octets_seen += length;
     return SCAN_NOTFOUND;
 }
@@ -202,6 +206,7 @@ void NHttpHeaderSplitter::conditional_reset() {
     if (complete) {
         peek_octets = 0;
         peek_status = SCAN_NOTFOUND;
+        first_lf = 0;
     }
     NHttpSplitter::conditional_reset();
 }
@@ -209,30 +214,40 @@ void NHttpHeaderSplitter::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)) ||
-            ((buffer[k] == '\n') && (num_crlf%2 == 1))) {
+        if (buffer[k] == '\n') {
             num_crlf++;
-            if ((num_crlf == 2) && (octets_seen + k + 1) == 2) {
-                num_flush = k+1;
+            if ((first_lf == 0) && (num_crlf < octets_seen + k + 1)) {
+                first_lf = num_crlf;
+            }
+            else {
+                num_flush = k + 1;
                 complete = true;
                 return SCAN_FOUND;
             }
-            if (num_crlf < 4) {
-                continue;
+        }
+        else if (buffer[k] == '\r') {
+            if (num_crlf == first_lf) {
+                num_crlf++;
+            }
+            else {
+                num_crlf = 1;
+                first_lf = 0;
             }
         }
         else {
             num_crlf = 0;
-            continue;
+            first_lf = 0;
         }
-        num_flush = k+1;
-        complete = true;
-        return SCAN_FOUND;
     }
     octets_seen += length;
     return SCAN_NOTFOUND;
 }
 
+void NHttpTrailerSplitter::conditional_reset() {
+    if (complete) {
+        first_lf = 0;
+    }
+    NHttpSplitter::conditional_reset();
+}
 
 
index a1b1a234f6905a995198829d31ac52b290f9ee73..6421d3a37eb444c19dcedfd2d2fcbfceaf9170cf 100644 (file)
@@ -43,6 +43,7 @@ public:
     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; };
+    virtual uint32_t get_num_excess() { return 0; };
 
 protected:
     uint32_t octets_seen = 0;
@@ -53,14 +54,12 @@ protected:
     virtual void conditional_reset();
 };
 
-class NHttpRequestSplitter : public NHttpSplitter {
-public:
-    NHttpEnums::ScanResult split(const uint8_t* buffer, uint32_t length);
-};
-
-class NHttpStatusSplitter : public NHttpSplitter {
+class NHttpStartSplitter : public NHttpSplitter {
 public:
     NHttpEnums::ScanResult split(const uint8_t* buffer, uint32_t length);
+    uint32_t get_num_excess() { return num_crlf; };
+private:
+    static const int MAX_LEADING_WHITESPACE = 20;
 };
 
 class NHttpHeaderSplitter : public NHttpSplitter {
@@ -69,14 +68,17 @@ public:
     NHttpEnums::ScanResult peek(const uint8_t* buffer, uint32_t length);
     void conditional_reset();
     uint32_t get_octets_seen() { return octets_seen - peek_octets; };
+    uint32_t get_num_excess() { return num_crlf; };
 private:
     uint32_t peek_octets = 0;
+    unsigned first_lf = 0;
     NHttpEnums::ScanResult peek_status = NHttpEnums::SCAN_NOTFOUND;
 };
 
 class NHttpChunkSplitter : public NHttpSplitter {
 public:
     NHttpEnums::ScanResult split(const uint8_t* buffer, uint32_t length);
+    uint32_t get_num_excess() { return octets_seen; };
     void conditional_reset();
 private:
     uint32_t expected_length = 0;
@@ -89,6 +91,10 @@ private:
 class NHttpTrailerSplitter : public NHttpSplitter {
 public:
     NHttpEnums::ScanResult split(const uint8_t* buffer, uint32_t length);
+    void conditional_reset();
+    uint32_t get_num_excess() { return num_crlf; };
+private:
+    unsigned first_lf = 0;
 };
 
 #endif
index 3545f3d6b7072bdcf21551c0167d497dd9fe63bd..a0fbb725258b7221a25b0e42decc0a00b6674f69 100644 (file)
@@ -42,8 +42,10 @@ 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) {
+      SectionType section_type, bool tcp_close, uint64_t infractions, uint32_t num_octets, uint32_t length,
+      uint32_t num_excess) {
     session_data->section_type[source_id] = section_type;
+    session_data->num_excess[source_id] = num_excess;
     session_data->tcp_close[source_id] = tcp_close;
     session_data->infractions[source_id] = infractions;
     switch (section_type) {
@@ -136,8 +138,8 @@ StreamSplitter::Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* dat
       {
         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_REQUEST:
+          case SEC_STATUS: splitter = (NHttpSplitter*)&session_data->start_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;
@@ -150,30 +152,31 @@ StreamSplitter::Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* dat
         switch (split_result) {
           case SCAN_NOTFOUND:
             if (splitter->get_octets_seen() == 63780) {
-                prepare_flush(session_data, flush_offset, source_id, SEC_DISCARD, false, 0, 0, length);
+                prepare_flush(session_data, flush_offset, source_id, SEC_DISCARD, false, 0, 0, length, 0);
                 session_data->type_expected[source_id] = SEC_ABORT;
                 return StreamSplitter::ABORT;
             }
             if (tcp_close) {
-                prepare_flush(session_data, flush_offset, source_id, type, true, INF_TRUNCATED, length, length);
+                prepare_flush(session_data, flush_offset, source_id, type, true, INF_TRUNCATED, length, length,
+                   splitter->get_num_excess());
                 return StreamSplitter::FLUSH;
             }
             // 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);
+            prepare_flush(session_data, flush_offset, source_id, SEC_ABORT, false, 0, length, length, 0);
             session_data->type_expected[source_id] = SEC_ABORT;
             return StreamSplitter::FLUSH;
           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);
+               flush_octets, length, 0);
             return StreamSplitter::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);
+               flush_octets, length, splitter->get_num_excess());
             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;
@@ -188,7 +191,7 @@ StreamSplitter::Status NHttpStreamSplitter::scan (Flow* flow, const uint8_t* dat
       case SEC_BODY: {
         prepare_flush(session_data, flush_offset, source_id, type,
            tcp_close && (length <= session_data->data_length[source_id]),
-           0, session_data->data_length[source_id], length);
+           0, session_data->data_length[source_id], length, 0);
         return StreamSplitter::FLUSH;
       }
       case SEC_ABORT:
@@ -229,7 +232,12 @@ const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total,
 
     // FIXIT-P stream should be enhanced to do discarding for us
     // For now flush-then-discard here is how scan() handles things we don't need to examine.
-    if (session_data->section_type[source_id] == SEC_DISCARD) {
+    if ((session_data->section_type[source_id] == SEC_DISCARD) ||
+        (session_data->section_type[source_id] == SEC_ABORT)) {
+        if (NHttpTestManager::use_test_output()) {
+            fprintf(NHttpTestManager::get_output_file(), "%s %u octets\n\n",
+               (session_data->section_type[source_id] == SEC_DISCARD) ? "Discarded" : "Aborted", len);
+        }
         return nullptr;
     }
 
@@ -245,13 +253,15 @@ const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total,
            session_data->unused_octets_visible[source_id])];
     }
 
-    memcpy(buffer + buffer_length + offset, data, len);
+    uint32_t num_excess = session_data->num_excess[source_id];
+    unsigned num_to_copy = (len <= total - offset - num_excess) ? len : total - offset - num_excess;
+    memcpy(buffer + buffer_length + offset, data, num_to_copy);
     if (flags & PKT_PDU_TAIL) {
         ProcessResult send_to_detection;
         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,
+            send_to_detection = my_inspector->process(buffer + buffer_length, offset + len - num_excess, flow, source_id,
                buffer_length == 0);
         }
         else {
@@ -277,7 +287,7 @@ const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total,
         switch (send_to_detection) {
           case RES_INSPECT:
             nhttp_buf.data = buffer;
-            nhttp_buf.length = buffer_length + offset + len;
+            nhttp_buf.length = buffer_length + offset + len - num_excess;
             buffer = nullptr;
             buffer_length = 0;
             if (NHttpTestManager::use_test_output()) {
@@ -289,7 +299,7 @@ const StreamBuffer* NHttpStreamSplitter::reassemble(Flow* flow, unsigned total,
             buffer_length = 0;
             return nullptr;
           case RES_AGGREGATE:
-            buffer_length += offset + len;
+            buffer_length += offset + len - num_excess;
             return nullptr;
         }
     }
index 4258a4cc732d243627a03d7ccb58eb4637352a59..ecb978db45ffde2f425a13298e8d7c46e745816b 100644 (file)
@@ -46,7 +46,8 @@ public:
     unsigned max() { return NHttpTestManager::use_test_input() ? 16384 : paf_max; };
 private:
     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);
+       NHttpEnums::SectionType section_type, bool tcp_close, uint64_t infractions, uint32_t num_octets, uint32_t length,
+       uint32_t num_excess);
     void create_event(NHttpEnums::EventSid sid);
     uint32_t size_buffer_needed(unsigned total, NHttpEnums::SectionType type, uint32_t possible_additional);
     NHttpInspect* const my_inspector;
index 4693d4164b88411382d229ee169b473a38821979..37e6fcedbcfc423c6972c5b2e38564f1f1e2fc4a 100644 (file)
 #
 # 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.
-#
 # 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.