nhttp_msg_status.cc
nhttp_msg_status.h
nhttp_msg_head_shared.cc
+ nhttp_msg_head_shared_util.cc
nhttp_msg_head_shared.h
nhttp_msg_header.cc
nhttp_msg_header.h
nhttp_msg_start.cc nhttp_msg_start.h \
nhttp_msg_request.cc nhttp_msg_request.h \
nhttp_msg_status.cc nhttp_msg_status.h \
-nhttp_msg_head_shared.cc nhttp_msg_head_shared.h \
+nhttp_msg_head_shared.cc nhttp_msg_head_shared_util.cc nhttp_msg_head_shared.h \
nhttp_msg_header.cc nhttp_msg_header.h \
nhttp_msg_body.cc nhttp_msg_body.h \
nhttp_msg_body_cl.cc nhttp_msg_body_cl.h \
INF_POST_WO_BODY,
INF_UTF_NORM_FAIL,
INF_UTF7,
+ INF_UNSUPPORTED_ENCODING,
+ INF_UNKNOWN_ENCODING,
+ INF_STACKED_ENCODINGS,
INF__MAX_VALUE
};
EVENT_FINAL_NOT_CHUNKED,
EVENT_CHUNKED_BEFORE_END,
EVENT_MISFORMATTED_HTTP,
+ EVENT_UNSUPPORTED_ENCODING,
+ EVENT_UNKNOWN_ENCODING,
+ EVENT_STACKED_ENCODINGS,
EVENT__MAX_VALUE
};
: NHttpMsgSection(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_)
{ }
~NHttpMsgHeadShared();
+ // Get the next item in a comma-separated header value and convert it to an enum value
+ static int32_t get_next_code(const Field& field, int32_t& offset, const StrCode table[]);
#ifdef REG_TEST
void print_headers(FILE* output);
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2016 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.
+//--------------------------------------------------------------------------
+// nhttp_msg_head_shared_util.cc author Tom Peters <thopeter@cisco.com>
+
+#include "nhttp_msg_head_shared.h"
+
+int32_t NHttpMsgHeadShared::get_next_code(const Field& field, int32_t& offset,
+ const StrCode table[])
+{
+ assert(field.length > 0);
+ const uint8_t* start = field.start + offset;
+ int32_t length;
+ for (length = 0; (offset+length < field.length) && (*(start+length) != ','); length++);
+ offset += length + 1;
+ return str_to_code(start, length, table);
+}
+
if (!params->unzip)
return;
- // FIXIT-M add support for compression in transfer encoding
- // FIXIT-M add support for multiple content encoding values
- const Field& norm_content_encoding = get_header_value_norm(HEAD_CONTENT_ENCODING);
- if (norm_content_encoding.length <= 0)
- return;
+ CompressId& compression = session_data->compression[source_id];
- const Contentcoding compress_code = (Contentcoding)norm_last_token_code(
- norm_content_encoding, NHttpMsgHeadShared::content_code_list);
+ // Search the Content-Encoding and Transfer-Encoding headers to find the type of compression
+ // used. We detect and alert on multiple layers of compression but we only decompress the
+ // outermost layer. We proceed through the headers inside-out, starting at the front of the
+ // list of Content-Encodings and ending with the last Transfer-Encoding. Thus the last encoding
+ // we encounter (other than chunked) is the one we use. If we don't recognize or support the
+ // last encoding we won't do anything.
- CompressId& compression = session_data->compression[source_id];
+ const Field& norm_content_encoding = get_header_value_norm(HEAD_CONTENT_ENCODING);
+ int32_t cont_offset = 0;
+ while (norm_content_encoding.length > cont_offset)
+ {
+ const Contentcoding content_code = (Contentcoding)get_next_code(norm_content_encoding,
+ cont_offset, NHttpMsgHeadShared::content_code_list);
+ if ((compression != CMP_NONE) && (content_code != CONTENTCODE_IDENTITY))
+ {
+ infractions += INF_STACKED_ENCODINGS;
+ events.create_event(EVENT_STACKED_ENCODINGS);
+ compression = CMP_NONE;
+ }
+ switch (content_code)
+ {
+ case CONTENTCODE_GZIP:
+ case CONTENTCODE_X_GZIP:
+ compression = CMP_GZIP;
+ break;
+ case CONTENTCODE_DEFLATE:
+ compression = CMP_DEFLATE;
+ break;
+ case CONTENTCODE_COMPRESS:
+ case CONTENTCODE_EXI:
+ case CONTENTCODE_PACK200_GZIP:
+ case CONTENTCODE_X_COMPRESS:
+ infractions += INF_UNSUPPORTED_ENCODING;
+ events.create_event(EVENT_UNSUPPORTED_ENCODING);
+ break;
+ case CONTENTCODE_IDENTITY:
+ break;
+ case CONTENTCODE__OTHER:
+ infractions += INF_UNKNOWN_ENCODING;
+ events.create_event(EVENT_UNKNOWN_ENCODING);
+ break;
+ }
+ }
- if ((compress_code == CONTENTCODE_GZIP) || (compress_code == CONTENTCODE_X_GZIP))
- compression = CMP_GZIP;
- else if (compress_code == CONTENTCODE_DEFLATE)
- compression = CMP_DEFLATE;
- else
+ const Field& norm_transfer_encoding = get_header_value_norm(HEAD_TRANSFER_ENCODING);
+ int32_t trans_offset = 0;
+ while (norm_transfer_encoding.length > trans_offset)
+ {
+ const Transcoding transfer_code = (Transcoding)get_next_code(norm_transfer_encoding,
+ trans_offset, NHttpMsgHeadShared::trans_code_list);
+ if ((compression != CMP_NONE) &&
+ !((transfer_code == TRANSCODE_IDENTITY) || (transfer_code == TRANSCODE_CHUNKED)))
+ {
+ infractions += INF_STACKED_ENCODINGS;
+ events.create_event(EVENT_STACKED_ENCODINGS);
+ compression = CMP_NONE;
+ }
+ switch (transfer_code)
+ {
+ case TRANSCODE_GZIP:
+ case TRANSCODE_X_GZIP:
+ compression = CMP_GZIP;
+ break;
+ case TRANSCODE_DEFLATE:
+ compression = CMP_DEFLATE;
+ break;
+ case TRANSCODE_COMPRESS:
+ case TRANSCODE_X_COMPRESS:
+ infractions += INF_UNSUPPORTED_ENCODING;
+ events.create_event(EVENT_UNSUPPORTED_ENCODING);
+ break;
+ case TRANSCODE_CHUNKED:
+ case TRANSCODE_IDENTITY:
+ break;
+ case TRANSCODE__OTHER:
+ infractions += INF_UNKNOWN_ENCODING;
+ events.create_event(EVENT_UNKNOWN_ENCODING);
+ break;
+ }
+ }
+
+ if (compression == CMP_NONE)
return;
session_data->compress_stream[source_id] = new z_stream;
{ EVENT_FINAL_NOT_CHUNKED, "Transfer-Encoding did not end with chunked" },
{ EVENT_CHUNKED_BEFORE_END, "Transfer-Encoding with chunked not at end" },
{ EVENT_MISFORMATTED_HTTP, "Misformatted HTTP traffic" },
+ { EVENT_UNSUPPORTED_ENCODING, "Unsupported Transfer-Encoding or Content-Encoding used" },
+ { EVENT_UNKNOWN_ENCODING, "Unknown Transfer-Encoding or Content-Encoding used" },
+ { EVENT_STACKED_ENCODINGS, "Multiple layers of compression encodings applied" },
{ 0, nullptr }
};
add_cpputest(nhttp_normalizers_test nhttp_inspect framework)
add_cpputest(nhttp_module_test nhttp_inspect framework)
add_cpputest(nhttp_transaction_test nhttp_inspect framework -lz)
+add_cpputest(nhttp_msg_head_shared_util_test nhttp_inspect framework)
nhttp_uri_norm_test \
nhttp_normalizers_test \
nhttp_module_test \
-nhttp_transaction_test
+nhttp_transaction_test \
+nhttp_msg_head_shared_util_test
TESTS = $(check_PROGRAMS)
../nhttp_test_input.o \
@CPPUTEST_LDFLAGS@
+nhttp_msg_head_shared_util_test_CPPFLAGS = $(AM_CPPFLAGS) @CPPUTEST_CPPFLAGS@
+nhttp_msg_head_shared_util_test_LDADD = \
+../nhttp_msg_head_shared_util.o \
+../nhttp_field.o \
+../nhttp_str_to_code.o \
+@CPPUTEST_LDFLAGS@
+
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2016 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.
+//--------------------------------------------------------------------------
+
+// nhttp_msg_head_shared_util_test.cc author Tom Peters <thopeter@cisco.com>
+// unit test main
+
+#include "service_inspectors/nhttp_inspect/nhttp_msg_head_shared.h"
+#include "service_inspectors/nhttp_inspect/nhttp_field.h"
+#include "service_inspectors/nhttp_inspect/nhttp_str_to_code.h"
+#include "service_inspectors/nhttp_inspect/nhttp_test_manager.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
+
+// Stubs whose sole purpose is to make the test code link
+long NHttpTestManager::print_amount {};
+bool NHttpTestManager::print_hex {};
+
+TEST_GROUP(nhttp_msg_head_shared_util)
+{
+ enum Color { COLOR_OTHER=1, COLOR_GREEN, COLOR_BLUE, COLOR_RED, COLOR_YELLOW, COLOR_PURPLE };
+ int32_t offset = 0;
+ const StrCode color_table[6] =
+ {
+ { COLOR_GREEN, "green" },
+ { COLOR_BLUE, "blue" },
+ { COLOR_RED, "red" },
+ { COLOR_YELLOW, "yellow" },
+ { COLOR_PURPLE, "purple" },
+ { 0, nullptr }
+ };
+
+ // This allows access to test a protected static member function
+ class NHttpMsgHeadTest : public NHttpMsgHeadShared
+ {
+ public:
+ static int32_t get_next_code_test(const Field& field, int32_t& offset,
+ const StrCode table[])
+ {
+ return NHttpMsgHeadShared::get_next_code(field, offset, table);
+ }
+ };
+};
+
+TEST(nhttp_msg_head_shared_util, basic)
+{
+ Field input(10, (const uint8_t*) "green,blue");
+ Color color = (Color) NHttpMsgHeadTest::get_next_code_test(input, offset, color_table);
+ CHECK(offset == 6);
+ CHECK(color == COLOR_GREEN);
+ color = (Color) NHttpMsgHeadTest::get_next_code_test(input, offset, color_table);
+ CHECK(offset == 11);
+ CHECK(color == COLOR_BLUE);
+}
+
+TEST(nhttp_msg_head_shared_util, single_token)
+{
+ Field input(6, (const uint8_t*) "purple");
+ Color color = (Color) NHttpMsgHeadTest::get_next_code_test(input, offset, color_table);
+ CHECK(offset == 7);
+ CHECK(color == COLOR_PURPLE);
+}
+
+TEST(nhttp_msg_head_shared_util, unknown_token)
+{
+ Field input(14, (const uint8_t*) "madeup,red,red");
+ Color color = (Color) NHttpMsgHeadTest::get_next_code_test(input, offset, color_table);
+ CHECK(offset == 7);
+ CHECK(color == COLOR_OTHER);
+}
+
+TEST(nhttp_msg_head_shared_util, null_token)
+{
+ Field input(11, (const uint8_t*) "green,,blue");
+ Color color = (Color) NHttpMsgHeadTest::get_next_code_test(input, offset, color_table);
+ CHECK(offset == 6);
+ CHECK(color == COLOR_GREEN);
+ color = (Color) NHttpMsgHeadTest::get_next_code_test(input, offset, color_table);
+ CHECK(offset == 7);
+ CHECK(color == COLOR_OTHER);
+ color = (Color) NHttpMsgHeadTest::get_next_code_test(input, offset, color_table);
+ CHECK(offset == 12);
+ CHECK(color == COLOR_BLUE);
+}
+
+int main(int argc, char** argv)
+{
+ return CommandLineTestRunner::RunAllTests(argc, argv);
+}
+