]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #937 in SNORT/snort3 from nhttp77 to master
authorTom Peters (thopeter) <thopeter@cisco.com>
Tue, 27 Jun 2017 14:48:02 +0000 (10:48 -0400)
committerTom Peters (thopeter) <thopeter@cisco.com>
Tue, 27 Jun 2017 14:48:02 +0000 (10:48 -0400)
Squashed commit of the following:

commit dd997afe9a8aa2f17dd0939f5d4942c99336e7d6
Author: Tom Peters <thopeter@cisco.com>
Date:   Thu Jun 22 16:06:34 2017 -0400

    Content-Transfer-Encoding

14 files changed:
src/service_inspectors/http_inspect/CMakeLists.txt
src/service_inspectors/http_inspect/Makefile.am
src/service_inspectors/http_inspect/http_enum.h
src/service_inspectors/http_inspect/http_inspect.cc
src/service_inspectors/http_inspect/http_msg_header.cc
src/service_inspectors/http_inspect/http_msg_header.h
src/service_inspectors/http_inspect/http_msg_section.h
src/service_inspectors/http_inspect/http_msg_start.cc
src/service_inspectors/http_inspect/http_msg_start.h
src/service_inspectors/http_inspect/http_msg_trailer.cc
src/service_inspectors/http_inspect/http_msg_trailer.h
src/service_inspectors/http_inspect/http_stream_splitter_finish.cc [new file with mode: 0644]
src/service_inspectors/http_inspect/http_stream_splitter_scan.cc
src/service_inspectors/http_inspect/http_tables.cc

index 0cca17976bbf7e0ee3b7f2a3b8e6fec3bbe05eec..a34eed09863633e24cc3408c0fc49619e4cf7bf3 100644 (file)
@@ -53,6 +53,7 @@ set (FILE_LIST
     http_enum.h
     http_field.cc
     http_field.h
+    http_stream_splitter_finish.cc
     http_stream_splitter_reassemble.cc
     http_stream_splitter_scan.cc
     http_stream_splitter.h
index 742561a0b8ccfe493b3fcc258eadd53f2082e7d7..8635bac79de3d1adc76e2d2f1626b16015f0b913 100644 (file)
@@ -22,7 +22,8 @@ http_module.cc http_module.h \
 http_test_input.cc http_test_input.h \
 http_flow_data.cc http_flow_data.h \
 http_transaction.cc http_transaction.h \
-http_stream_splitter_reassemble.cc http_stream_splitter_scan.cc http_stream_splitter.h \
+http_stream_splitter_finish.cc http_stream_splitter_reassemble.cc http_stream_splitter_scan.cc \
+http_stream_splitter.h \
 http_cutter.cc http_cutter.h \
 http_enum.h \
 http_test_manager.cc http_test_manager.h \
index b14f3a28e8c8ca58f412ccf513f74cbe58f60282..9e94e6e27404030de5d3de31c40705c257832041 100644 (file)
@@ -122,7 +122,7 @@ enum HeaderId { HEAD__NOT_COMPUTE=-14, HEAD__PROBLEMATIC=-12, HEAD__NOT_PRESENT=
     HEAD_WWW_AUTHENTICATE, HEAD_ALLOW, HEAD_CONTENT_ENCODING, HEAD_CONTENT_LANGUAGE,
     HEAD_CONTENT_LENGTH, HEAD_CONTENT_LOCATION, HEAD_CONTENT_MD5, HEAD_CONTENT_RANGE,
     HEAD_CONTENT_TYPE, HEAD_EXPIRES, HEAD_LAST_MODIFIED, HEAD_X_FORWARDED_FOR, HEAD_TRUE_CLIENT_IP,
-    HEAD_X_WORKING_WITH,
+    HEAD_X_WORKING_WITH, HEAD_CONTENT_TRANSFER_ENCODING,
     HEAD__MAX_VALUE };
 
 // All the infractions we might find while parsing and analyzing a message
@@ -224,6 +224,8 @@ enum Infraction
     INF_EXPECT_WITHOUT_BODY_CL0,
     INF_EXPECT_WITHOUT_BODY_NO_CL,
     INF_CHUNKED_ONE_POINT_ZERO,
+    INF_CTE_HEADER,
+    INF_ILLEGAL_TRAILER,
     INF__MAX_VALUE
 };
 
@@ -328,6 +330,8 @@ enum EventSid
     EVENT_UNKNOWN_1XX_STATUS,
     EVENT_EXPECT_WITHOUT_BODY,             // 90
     EVENT_CHUNKED_ONE_POINT_ZERO,
+    EVENT_CTE_HEADER,
+    EVENT_ILLEGAL_TRAILER,
     EVENT__MAX_VALUE
 };
 
index 484b7ef8e455b0ccb7293fdb9db504ec75d66bb0..533ea640c1faaf48a2123983d7f9615585566d62 100644 (file)
@@ -197,6 +197,7 @@ const Field& HttpInspect::process(const uint8_t* data, const uint16_t dsize, Flo
     }
 
     session_data->latest_section->analyze();
+    session_data->latest_section->gen_events();
     session_data->latest_section->update_flow();
 
 #ifdef REG_TEST
index 3688e06e3da164cb564a62e3d52330a030436689..b067f8bb414d3f4c6d7201301967359b35789d7f 100644 (file)
@@ -55,10 +55,8 @@ void HttpMsgHeader::publish()
     }
 }
 
-void HttpMsgHeader::update_flow()
+void HttpMsgHeader::gen_events()
 {
-    session_data->section_type[source_id] = SEC__NOT_COMPUTE;
-
     if (get_header_count(HEAD_CONTENT_LENGTH) > 1)
     {
         add_infraction(INF_MULTIPLE_CONTLEN);
@@ -70,6 +68,18 @@ void HttpMsgHeader::update_flow()
         add_infraction(INF_BOTH_CL_AND_TE);
         create_event(EVENT_BOTH_CL_AND_TE);
     }
+    // Content-Transfer-Encoding is a MIME header not sanctioned by HTTP. Which may not prevent
+    // some clients from recognizing it and applying a decoding that Snort does not expect.
+    if (get_header_count(HEAD_CONTENT_TRANSFER_ENCODING) > 0)
+    {
+        add_infraction(INF_CTE_HEADER);
+        create_event(EVENT_CTE_HEADER);
+    }
+}
+
+void HttpMsgHeader::update_flow()
+{
+    session_data->section_type[source_id] = SEC__NOT_COMPUTE;
 
     // The following logic to determine body type is by no means the last word on this topic.
     if (tcp_close)
index 529fe5a9e7acc095495cc2f3e2bfaf9b0697dba2..658631788e5504a97906202bd14cdc7050692f95 100644 (file)
@@ -38,7 +38,7 @@ public:
     HttpEnums::InspectSection get_inspection_section() const override
         { return detection_section ? HttpEnums::IS_DETECTION : HttpEnums::IS_NONE; }
     void update_flow() override;
-
+    void gen_events() override;
     void publish() override;
 
 private:
index 65bd7c69220e42340e5196ae6e73c0dd94624b06..173ebd4855ddf9802511b9f60d9130578c650422 100644 (file)
@@ -42,6 +42,12 @@ public:
     // Minimum necessary processing for every message
     virtual void analyze() = 0;
 
+    // analyze() generates many events in the course of its work. Many other events are generated
+    // by JIT normalization but only if someone asks for the item in question. gen_events()
+    // addresses a third category--things that do not come up during analysis but must be
+    // inspected for every message even if no one else asks about them.
+    virtual void gen_events() {}
+
     // Manages the splitter and communication between message sections
     virtual void update_flow() = 0;
 
index 7a735c9435be3fe2c9fd3ca11b2a17b536999ab3..283e8cba9517178d743f708db9ec773765d9b535 100644 (file)
@@ -29,7 +29,6 @@ void HttpMsgStart::analyze()
 {
     start_line.set(msg_text);
     parse_start_line();
-    gen_events();
 }
 
 void HttpMsgStart::derive_version_id()
index 372cc6187c2504e7ad3cc27360669c6e3965a272..08ec2e7490054aace5e35c553669552d1e64e7c5 100644 (file)
@@ -39,7 +39,6 @@ protected:
         : HttpMsgSection(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_)
         { }
     virtual void parse_start_line() = 0;
-    virtual void gen_events() = 0;
     void derive_version_id();
 
     Field start_line;
index 5ce5b7b059a286084fc3650aef45c3bbc0088ad2..80d49dac2b45de647123906aa6aa89b0734a9be4 100644 (file)
@@ -35,6 +35,51 @@ HttpMsgTrailer::HttpMsgTrailer(const uint8_t* buffer, const uint16_t buf_size,
     transaction->set_trailer(this, source_id);
 }
 
+void HttpMsgTrailer::gen_events()
+{
+    // Trailers not allowed by RFC 7230
+    static const HeaderId bad_trailer[] =
+    {
+        HEAD_AGE,
+        HEAD_AUTHORIZATION,
+        HEAD_CACHE_CONTROL,
+        HEAD_CONTENT_ENCODING,
+        HEAD_CONTENT_LENGTH,
+        HEAD_CONTENT_RANGE,
+        HEAD_CONTENT_TRANSFER_ENCODING,
+        HEAD_CONTENT_TYPE,
+        HEAD_COOKIE,
+        HEAD_DATE,
+        HEAD_EXPECT,
+        HEAD_EXPIRES,
+        HEAD_HOST,
+        HEAD_LOCATION,
+        HEAD_MAX_FORWARDS,
+        HEAD_PRAGMA,
+        HEAD_PROXY_AUTHENTICATE,
+        HEAD_PROXY_AUTHORIZATION,
+        HEAD_RANGE,
+        HEAD_RETRY_AFTER,
+        HEAD_SET_COOKIE,
+        HEAD_TE,
+        HEAD_TRAILER,
+        HEAD_TRANSFER_ENCODING,
+        HEAD_VARY,
+        HEAD_WARNING,
+        HEAD_WWW_AUTHENTICATE
+    };
+
+    for (HeaderId id: bad_trailer)
+    {
+        if (get_header_count(id) > 0)
+        {
+            add_infraction(INF_ILLEGAL_TRAILER);
+            create_event(EVENT_ILLEGAL_TRAILER);
+            break;
+        }
+    }
+}
+
 void HttpMsgTrailer::update_flow()
 {
     session_data->half_reset(source_id);
index e44217c6722481ab8fd87fef94128eae68faf57e..b88adae6bceff3a8fefe297585bf86bc0a9ad3c2 100644 (file)
@@ -34,6 +34,7 @@ public:
         const HttpParaList* params_);
     HttpEnums::InspectSection get_inspection_section() const override
         { return HttpEnums::IS_TRAILER; }
+    void gen_events() override;
     void update_flow() override;
 
 #ifdef REG_TEST
diff --git a/src/service_inspectors/http_inspect/http_stream_splitter_finish.cc b/src/service_inspectors/http_inspect/http_stream_splitter_finish.cc
new file mode 100644 (file)
index 0000000..2f24626
--- /dev/null
@@ -0,0 +1,121 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2017 Cisco and/or its affiliates. All rights reserved.
+//
+// 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.
+//--------------------------------------------------------------------------
+// http_stream_splitter_finish.cc author Tom Peters <thopeter@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "file_api/file_flows.h"
+
+#include "http_msg_request.h"
+#include "http_stream_splitter.h"
+#include "http_test_input.h"
+
+using namespace HttpEnums;
+
+bool HttpStreamSplitter::finish(Flow* flow)
+{
+    HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::http_flow_id);
+    // FIXIT-M - this assert has been changed to check for null session data and return false if so
+    //           due to lack of reliable feedback to stream that scan has been called...if that is
+    //           addressed in stream reassembly rewrite this can be reverted to an assert
+    //assert(session_data != nullptr);
+    if(!session_data)
+        return false;
+
+#ifdef REG_TEST
+    if (HttpTestManager::use_test_output() && !HttpTestManager::use_test_input())
+    {
+        printf("Finish from flow data %" PRIu64 " direction %d\n", session_data->seq_num,
+            source_id);
+        fflush(stdout);
+    }
+#endif
+
+    if (session_data->type_expected[source_id] == SEC_ABORT)
+    {
+        return false;
+    }
+
+    session_data->tcp_close[source_id] = true;
+
+    // If there is leftover data for which we returned PAF_SEARCH and never flushed, we need to set
+    // up to process because it is about to go to reassemble(). But we don't support partial start
+    // lines.
+    if ((session_data->section_type[source_id] == SEC__NOT_COMPUTE) &&
+        (session_data->cutter[source_id] != nullptr)               &&
+        (session_data->cutter[source_id]->get_octets_seen() > 0))
+    {
+        if ((session_data->type_expected[source_id] == SEC_REQUEST) ||
+            (session_data->type_expected[source_id] == SEC_STATUS))
+        {
+            *session_data->get_infractions(source_id) += INF_PARTIAL_START;
+            // FIXIT-M why not use generate_misformatted_http()?
+            session_data->get_events(source_id)->create_event(EVENT_LOSS_OF_SYNC);
+            return false;
+        }
+
+        uint32_t not_used;
+        prepare_flush(session_data, &not_used, session_data->type_expected[source_id], 0,
+            session_data->cutter[source_id]->get_num_excess(),
+            session_data->cutter[source_id]->get_num_head_lines(),
+            session_data->cutter[source_id]->get_is_broken_chunk(),
+            session_data->cutter[source_id]->get_num_good_chunks(),
+            session_data->cutter[source_id]->get_octets_seen(),
+            true);
+        return true;
+    }
+
+    // If there is no more data to process we need to wrap up file processing right now
+    if ((session_data->section_type[source_id] == SEC__NOT_COMPUTE) &&
+        (session_data->file_depth_remaining[source_id] > 0)        &&
+        (session_data->cutter[source_id] != nullptr)               &&
+        (session_data->cutter[source_id]->get_octets_seen() == 0))
+    {
+        if (!session_data->mime_state[source_id])
+        {
+            FileFlows* file_flows = FileFlows::get_file_flows(flow);
+            const bool download = (source_id == SRC_SERVER);
+
+            size_t file_index = 0;
+
+            if (session_data->transaction[source_id] != nullptr)
+            {
+                HttpMsgRequest* request = session_data->transaction[source_id]->get_request();
+                if ((request != nullptr) and (request->get_http_uri() != nullptr))
+                {
+                    file_index = request->get_http_uri()->get_file_proc_hash();
+                }
+            }
+
+            file_flows->file_process(nullptr, 0, SNORT_FILE_END, !download, file_index);
+        }
+        else
+        {
+            session_data->mime_state[source_id]->process_mime_data(flow, nullptr, 0, true,
+                SNORT_FILE_POSITION_UNKNOWN);
+            delete session_data->mime_state[source_id];
+            session_data->mime_state[source_id] = nullptr;
+        }
+        return false;
+    }
+
+    return true;
+}
+
index 22b89f29bd62c487bbc9b3b938f731a84f06e3df..f18a0e637ef96c1d920100f2ea1faeb000b73099 100644 (file)
 #include "config.h"
 #endif
 
-#include "file_api/file_flows.h"
-
 #include "http_inspect.h"
-#include "http_msg_request.h"
 #include "http_stream_splitter.h"
 #include "http_test_input.h"
 
@@ -212,93 +209,3 @@ StreamSplitter::Status HttpStreamSplitter::scan(Flow* flow, const uint8_t* data,
     }
 }
 
-bool HttpStreamSplitter::finish(Flow* flow)
-{
-    HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::http_flow_id);
-    // FIXIT-M - this assert has been changed to check for null session data and return false if so
-    //           due to lack of reliable feedback to stream that scan has been called...if that is
-    //           addressed in stream reassembly rewrite this can be reverted to an assert
-    //assert(session_data != nullptr);
-    if(!session_data)
-        return false;
-
-#ifdef REG_TEST
-    if (HttpTestManager::use_test_output() && !HttpTestManager::use_test_input())
-    {
-        printf("Finish from flow data %" PRIu64 " direction %d\n", session_data->seq_num,
-            source_id);
-        fflush(stdout);
-    }
-#endif
-
-    if (session_data->type_expected[source_id] == SEC_ABORT)
-    {
-        return false;
-    }
-
-    session_data->tcp_close[source_id] = true;
-
-    // If there is leftover data for which we returned PAF_SEARCH and never flushed, we need to set
-    // up to process because it is about to go to reassemble(). But we don't support partial start
-    // lines.
-    if ((session_data->section_type[source_id] == SEC__NOT_COMPUTE) &&
-        (session_data->cutter[source_id] != nullptr)               &&
-        (session_data->cutter[source_id]->get_octets_seen() > 0))
-    {
-        if ((session_data->type_expected[source_id] == SEC_REQUEST) ||
-            (session_data->type_expected[source_id] == SEC_STATUS))
-        {
-            *session_data->get_infractions(source_id) += INF_PARTIAL_START;
-            // FIXIT-M why not use generate_misformatted_http()?
-            session_data->get_events(source_id)->create_event(EVENT_LOSS_OF_SYNC);
-            return false;
-        }
-
-        uint32_t not_used;
-        prepare_flush(session_data, &not_used, session_data->type_expected[source_id], 0,
-            session_data->cutter[source_id]->get_num_excess(),
-            session_data->cutter[source_id]->get_num_head_lines(),
-            session_data->cutter[source_id]->get_is_broken_chunk(),
-            session_data->cutter[source_id]->get_num_good_chunks(),
-            session_data->cutter[source_id]->get_octets_seen(),
-            true);
-        return true;
-    }
-
-    // If there is no more data to process we need to wrap up file processing right now
-    if ((session_data->section_type[source_id] == SEC__NOT_COMPUTE) &&
-        (session_data->file_depth_remaining[source_id] > 0)        &&
-        (session_data->cutter[source_id] != nullptr)               &&
-        (session_data->cutter[source_id]->get_octets_seen() == 0))
-    {
-        if (!session_data->mime_state[source_id])
-        {
-            FileFlows* file_flows = FileFlows::get_file_flows(flow);
-            const bool download = (source_id == SRC_SERVER);
-
-            size_t file_index = 0;
-
-            if (session_data->transaction[source_id] != nullptr)
-            {
-                HttpMsgRequest* request = session_data->transaction[source_id]->get_request();
-                if ((request != nullptr) and (request->get_http_uri() != nullptr))
-                {
-                    file_index = request->get_http_uri()->get_file_proc_hash();
-                }
-            }
-
-            file_flows->file_process(nullptr, 0, SNORT_FILE_END, !download, file_index);
-        }
-        else
-        {
-            session_data->mime_state[source_id]->process_mime_data(flow, nullptr, 0, true,
-                SNORT_FILE_POSITION_UNKNOWN);
-            delete session_data->mime_state[source_id];
-            session_data->mime_state[source_id] = nullptr;
-        }
-        return false;
-    }
-
-    return true;
-}
-
index 2fe85fc6005e5c4e5ddbe1cd4cd9e44ef285fb4b..dcb51f11f211be4813afb245b412724367aabc03 100644 (file)
@@ -81,59 +81,60 @@ const StrCode HttpMsgRequest::method_list[] =
 
 const StrCode HttpMsgHeadShared::header_list[] =
 {
-    { HEAD_CACHE_CONTROL,        "cache-control" },
-    { HEAD_CONNECTION,           "connection" },
-    { HEAD_DATE,                 "date" },
-    { HEAD_PRAGMA,               "pragma" },
-    { HEAD_TRAILER,              "trailer" },
-    { HEAD_COOKIE,               "cookie" },
-    { HEAD_SET_COOKIE,           "set-cookie" },
-    { HEAD_TRANSFER_ENCODING,    "transfer-encoding" },
-    { HEAD_UPGRADE,              "upgrade" },
-    { HEAD_VIA,                  "via" },
-    { HEAD_WARNING,              "warning" },
-    { HEAD_ACCEPT,               "accept" },
-    { HEAD_ACCEPT_CHARSET,       "accept-charset" },
-    { HEAD_ACCEPT_ENCODING,      "accept-encoding" },
-    { HEAD_ACCEPT_LANGUAGE,      "accept-language" },
-    { HEAD_AUTHORIZATION,        "authorization" },
-    { HEAD_EXPECT,               "expect" },
-    { HEAD_FROM,                 "from" },
-    { HEAD_HOST,                 "host" },
-    { HEAD_IF_MATCH,             "if-match" },
-    { HEAD_IF_MODIFIED_SINCE,    "if-modified-since" },
-    { HEAD_IF_NONE_MATCH,        "if-none-match" },
-    { HEAD_IF_RANGE,             "if-range" },
-    { HEAD_IF_UNMODIFIED_SINCE,  "if-unmodified-since" },
-    { HEAD_MAX_FORWARDS,         "max-forwards" },
-    { HEAD_PROXY_AUTHORIZATION,  "proxy-authorization" },
-    { HEAD_RANGE,                "range" },
-    { HEAD_REFERER,              "referer" },
-    { HEAD_TE,                   "te" },
-    { HEAD_USER_AGENT,           "user-agent" },
-    { HEAD_ACCEPT_RANGES,        "accept-ranges" },
-    { HEAD_AGE,                  "age" },
-    { HEAD_ETAG,                 "etag" },
-    { HEAD_LOCATION,             "location" },
-    { HEAD_PROXY_AUTHENTICATE,   "proxy-authenticate" },
-    { HEAD_RETRY_AFTER,          "retry-after" },
-    { HEAD_SERVER,               "server" },
-    { HEAD_VARY,                 "vary" },
-    { HEAD_WWW_AUTHENTICATE,     "www-authenticate" },
-    { HEAD_ALLOW,                "allow" },
-    { HEAD_CONTENT_ENCODING,     "content-encoding" },
-    { HEAD_CONTENT_LANGUAGE,     "content-language" },
-    { HEAD_CONTENT_LENGTH,       "content-length" },
-    { HEAD_CONTENT_LOCATION,     "content-location" },
-    { HEAD_CONTENT_MD5,          "content-md5" },
-    { HEAD_CONTENT_RANGE,        "content-range" },
-    { HEAD_CONTENT_TYPE,         "content-type" },
-    { HEAD_EXPIRES,              "expires" },
-    { HEAD_LAST_MODIFIED,        "last-modified" },
-    { HEAD_X_FORWARDED_FOR,      "x-forwarded-for" },
-    { HEAD_TRUE_CLIENT_IP,       "true-client-ip" },
-    { HEAD_X_WORKING_WITH,       "x-working-with" },
-    { 0,                         nullptr }
+    { HEAD_CACHE_CONTROL,             "cache-control" },
+    { HEAD_CONNECTION,                "connection" },
+    { HEAD_DATE,                      "date" },
+    { HEAD_PRAGMA,                    "pragma" },
+    { HEAD_TRAILER,                   "trailer" },
+    { HEAD_COOKIE,                    "cookie" },
+    { HEAD_SET_COOKIE,                "set-cookie" },
+    { HEAD_TRANSFER_ENCODING,         "transfer-encoding" },
+    { HEAD_UPGRADE,                   "upgrade" },
+    { HEAD_VIA,                       "via" },
+    { HEAD_WARNING,                   "warning" },
+    { HEAD_ACCEPT,                    "accept" },
+    { HEAD_ACCEPT_CHARSET,            "accept-charset" },
+    { HEAD_ACCEPT_ENCODING,           "accept-encoding" },
+    { HEAD_ACCEPT_LANGUAGE,           "accept-language" },
+    { HEAD_AUTHORIZATION,             "authorization" },
+    { HEAD_EXPECT,                    "expect" },
+    { HEAD_FROM,                      "from" },
+    { HEAD_HOST,                      "host" },
+    { HEAD_IF_MATCH,                  "if-match" },
+    { HEAD_IF_MODIFIED_SINCE,         "if-modified-since" },
+    { HEAD_IF_NONE_MATCH,             "if-none-match" },
+    { HEAD_IF_RANGE,                  "if-range" },
+    { HEAD_IF_UNMODIFIED_SINCE,       "if-unmodified-since" },
+    { HEAD_MAX_FORWARDS,              "max-forwards" },
+    { HEAD_PROXY_AUTHORIZATION,       "proxy-authorization" },
+    { HEAD_RANGE,                     "range" },
+    { HEAD_REFERER,                   "referer" },
+    { HEAD_TE,                        "te" },
+    { HEAD_USER_AGENT,                "user-agent" },
+    { HEAD_ACCEPT_RANGES,             "accept-ranges" },
+    { HEAD_AGE,                       "age" },
+    { HEAD_ETAG,                      "etag" },
+    { HEAD_LOCATION,                  "location" },
+    { HEAD_PROXY_AUTHENTICATE,        "proxy-authenticate" },
+    { HEAD_RETRY_AFTER,               "retry-after" },
+    { HEAD_SERVER,                    "server" },
+    { HEAD_VARY,                      "vary" },
+    { HEAD_WWW_AUTHENTICATE,          "www-authenticate" },
+    { HEAD_ALLOW,                     "allow" },
+    { HEAD_CONTENT_ENCODING,          "content-encoding" },
+    { HEAD_CONTENT_LANGUAGE,          "content-language" },
+    { HEAD_CONTENT_LENGTH,            "content-length" },
+    { HEAD_CONTENT_LOCATION,          "content-location" },
+    { HEAD_CONTENT_MD5,               "content-md5" },
+    { HEAD_CONTENT_RANGE,             "content-range" },
+    { HEAD_CONTENT_TYPE,              "content-type" },
+    { HEAD_EXPIRES,                   "expires" },
+    { HEAD_LAST_MODIFIED,             "last-modified" },
+    { HEAD_X_FORWARDED_FOR,           "x-forwarded-for" },
+    { HEAD_TRUE_CLIENT_IP,            "true-client-ip" },
+    { HEAD_X_WORKING_WITH,            "x-working-with" },
+    { HEAD_CONTENT_TRANSFER_ENCODING, "content-transfer-encoding" },
+    { 0,                              nullptr }
 };
 
 const StrCode HttpMsgHeadShared::content_code_list[] =
@@ -247,7 +248,8 @@ const HeaderNormalizer* const HttpMsgHeadShared::header_norms[HEAD__MAX_VALUE] =
     [HEAD_LAST_MODIFIED] = &NORMALIZER_BASIC,
     [HEAD_X_FORWARDED_FOR] = &NORMALIZER_CAT,
     [HEAD_TRUE_CLIENT_IP] = &NORMALIZER_BASIC,
-    [HEAD_X_WORKING_WITH] = &NORMALIZER_BASIC
+    [HEAD_X_WORKING_WITH] = &NORMALIZER_BASIC,
+    [HEAD_CONTENT_TRANSFER_ENCODING] = &NORMALIZER_CAT,
 };
 /* *INDENT-ON* */
 
@@ -335,7 +337,7 @@ const RuleMap HttpModule::http_events[] =
     { EVENT_MISFORMATTED_HTTP,          "misformatted HTTP traffic" },
     { EVENT_UNSUPPORTED_ENCODING,       "unsupported Content-Encoding used" },
     { EVENT_UNKNOWN_ENCODING,           "unknown Content-Encoding used" },
-    { EVENT_STACKED_ENCODINGS,          "multiple layers of compression encodings applied" },
+    { EVENT_STACKED_ENCODINGS,          "multiple Content-Encodings applied" },
     { EVENT_RESPONSE_WO_REQUEST,        "server response before client request" },
     { EVENT_PDF_SWF_OVERRUN,            "PDF/SWF decompression of server response too big" },
     { EVENT_BAD_CHAR_IN_HEADER_NAME,    "nonprinting character in HTTP message header name" },
@@ -349,6 +351,8 @@ const RuleMap HttpModule::http_events[] =
     { EVENT_UNKNOWN_1XX_STATUS,         "1XX status code other than 100 or 101" },
     { EVENT_EXPECT_WITHOUT_BODY,        "Expect header sent without a message body" },
     { EVENT_CHUNKED_ONE_POINT_ZERO,     "HTTP 1.0 message with Transfer-Encoding header" },
+    { EVENT_CTE_HEADER,                 "Content-Transfer-Encoding used as HTTP header" },
+    { EVENT_ILLEGAL_TRAILER,            "illegal field in chunked message trailers" },
     { 0, nullptr }
 };