aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDerick Rethans <github@derickrethans.nl>2017-12-04 18:31:03 +0000
committerMichael Mann <mmann78@netscape.net>2017-12-10 02:06:51 +0000
commit8225159e336610c25fb4d4129b0be156ed487c49 (patch)
tree4abd7f304b5494a2590486fc76e955c2b74912c0
parent22a7d8fa8d8b8c7171ede55e7f413d004cb7b099 (diff)
Add support for MongoDB OP_COMPRESSED packets
Bug: 14259 Change-Id: Iab6b494bebaa913267f94d41b7950b67dd406cb6 Reviewed-on: https://code.wireshark.org/review/24705 Petri-Dish: Michael Mann <mmann78@netscape.net> Tested-by: Petri Dish Buildbot Reviewed-by: Michael Mann <mmann78@netscape.net>
-rw-r--r--epan/dissectors/packet-mongo.c264
1 files changed, 216 insertions, 48 deletions
diff --git a/epan/dissectors/packet-mongo.c b/epan/dissectors/packet-mongo.c
index 69f7b8e093..5c91f0a239 100644
--- a/epan/dissectors/packet-mongo.c
+++ b/epan/dissectors/packet-mongo.c
@@ -35,15 +35,25 @@
#include <epan/exceptions.h>
#include <epan/expert.h>
#include "packet-tcp.h"
+#ifdef HAVE_SNAPPY
+#include <snappy-c.h>
+#endif
void proto_register_mongo(void);
void proto_reg_handoff_mongo(void);
static dissector_handle_t mongo_handle;
+/* Forward declaration */
+static int
+dissect_opcode_types(tvbuff_t *tvb, packet_info *pinfo, guint offset, proto_tree *mongo_tree, guint opcode, guint *effective_opcode);
+
/* This is not IANA assigned nor registered */
#define TCP_PORT_MONGO 27017
+/* the code can reasonably attempt to decompress buffer up to 20MB */
+#define MAX_UNCOMPRESSED_SIZE (20 * 1024 * 1024)
+
#define OP_REPLY 1
#define OP_MESSAGE 1000
#define OP_UPDATE 2001
@@ -55,6 +65,7 @@ static dissector_handle_t mongo_handle;
#define OP_KILL_CURSORS 2007
#define OP_COMMAND 2010
#define OP_COMMANDREPLY 2011
+#define OP_COMPRESSED 2012
#define OP_MSG 2013
/**************************************************************************/
@@ -70,8 +81,9 @@ static const value_string opcode_vals[] = {
{ OP_GET_MORE, "Get More" },
{ OP_DELETE, "Delete document" },
{ OP_KILL_CURSORS, "Kill Cursors" },
- { OP_COMMAND, "Command Request (Cluster internal)" },
- { OP_COMMANDREPLY, "Command Reply (Cluster internal)" },
+ { OP_COMMAND, "Command Request" },
+ { OP_COMMANDREPLY, "Command Reply" },
+ { OP_COMPRESSED, "Compressed Data" },
{ OP_MSG, "Extensible Message Format" },
{ 0, NULL }
};
@@ -88,6 +100,20 @@ static const value_string section_kind_vals[] = {
{ 0, NULL }
};
+/**************************************************************************/
+/* Compression Engines */
+/**************************************************************************/
+#define MONGO_COMPRESSOR_NOOP 0
+#define MONGO_COMPRESSOR_SNAPPY 1
+#define MONGO_COMPRESSOR_ZLIB 2
+
+static const value_string compressor_vals[] = {
+ { MONGO_COMPRESSOR_NOOP, "Noop (Uncompressed)" },
+ { MONGO_COMPRESSOR_SNAPPY, "Snappy" },
+ { MONGO_COMPRESSOR_ZLIB, "Zlib" },
+ { 0, NULL }
+};
+
/* BSON Element types */
/* See http://bsonspec.org/#/specification for detail */
#define BSON_ELEMENT_TYPE_DOUBLE 1
@@ -227,18 +253,22 @@ static int hf_mongo_commandargs = -1;
static int hf_mongo_commandreply = -1;
static int hf_mongo_outputdocs = -1;
static int hf_mongo_unknown = -1;
+static int hf_mongo_compression_info = -1;
+static int hf_mongo_original_op_code = -1;
+static int hf_mongo_uncompressed_size = -1;
+static int hf_mongo_compressor = -1;
+static int hf_mongo_compressed_data = -1;
+static int hf_mongo_unsupported_compressed = -1;
static int hf_mongo_msg_flags = -1;
static int hf_mongo_msg_flags_checksumpresent = -1;
static int hf_mongo_msg_flags_moretocome = -1;
static int hf_mongo_msg_flags_exhaustallowed = -1;
-static int hf_mongo_msg_sections = -1;
static int hf_mongo_msg_sections_section = -1;
static int hf_mongo_msg_sections_section_kind = -1;
static int hf_mongo_msg_sections_section_body = -1;
static int hf_mongo_msg_sections_section_doc_sequence = -1;
static int hf_mongo_msg_sections_section_size = -1;
static int hf_mongo_msg_sections_section_doc_sequence_id = -1;
-static int hf_mongo_msg_sections_section_doc_sequence_documents = -1;
static gint ett_mongo = -1;
static gint ett_mongo_doc = -1;
@@ -248,6 +278,7 @@ static gint ett_mongo_objectid = -1;
static gint ett_mongo_code = -1;
static gint ett_mongo_fcn = -1;
static gint ett_mongo_flags = -1;
+static gint ett_mongo_compression_info = -1;
static gint ett_mongo_sections = -1;
static gint ett_mongo_section = -1;
static gint ett_mongo_msg_flags = -1;
@@ -256,6 +287,8 @@ static gint ett_mongo_doc_sequence= -1;
static expert_field ei_mongo_document_recursion_exceeded = EI_INIT;
static expert_field ei_mongo_document_length_bad = EI_INIT;
static expert_field ei_mongo_unknown = EI_INIT;
+static expert_field ei_mongo_unsupported_compression = EI_INIT;
+static expert_field ei_mongo_too_large_compressed = EI_INIT;
static int
dissect_fullcollectionname(tvbuff_t *tvb, guint offset, proto_tree *tree)
@@ -651,6 +684,100 @@ dissect_mongo_op_commandreply(tvbuff_t *tvb, packet_info *pinfo, guint offset, p
}
static int
+dissect_mongo_op_compressed(tvbuff_t *tvb, packet_info *pinfo, guint offset, proto_tree *tree, guint *effective_opcode)
+{
+ guint opcode = 0;
+ guint8 compressor;
+ proto_item *ti;
+ proto_tree *compression_info_tree;
+
+ ti = proto_tree_add_item(tree, hf_mongo_compression_info, tvb, offset, 9, ENC_NA);
+ compression_info_tree = proto_item_add_subtree(ti, ett_mongo_compression_info);
+ proto_tree_add_item(compression_info_tree, hf_mongo_original_op_code, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(compression_info_tree, hf_mongo_uncompressed_size, tvb, offset + 4, 4, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(compression_info_tree, hf_mongo_compressor, tvb, offset + 8, 1, ENC_NA);
+ proto_tree_add_item(compression_info_tree, hf_mongo_compressed_data, tvb, offset + 9, -1, ENC_NA);
+
+ opcode = tvb_get_letohl(tvb, offset);
+ *effective_opcode = opcode;
+ compressor = tvb_get_guint8(tvb, offset + 8);
+ offset += 9;
+
+ switch(compressor) {
+ case MONGO_COMPRESSOR_NOOP:
+ offset = dissect_opcode_types(tvb, pinfo, offset, tree, opcode, effective_opcode);
+ break;
+
+#ifdef HAVE_SNAPPY
+ case MONGO_COMPRESSOR_SNAPPY: {
+ guchar *decompressed_buffer = NULL;
+ size_t orig_size = 0;
+ snappy_status ret;
+ tvbuff_t* compressed_tvb = NULL;
+
+ /* get the raw data length */
+ ret = snappy_uncompressed_length(tvb_get_ptr(tvb, offset, -1),
+ tvb_captured_length_remaining(tvb, offset),
+ &orig_size);
+ /* if we get the length and it's reasonably short to allocate a buffer for it
+ * proceed to try decompressing the data
+ */
+ if (ret == SNAPPY_OK && orig_size <= MAX_UNCOMPRESSED_SIZE) {
+ decompressed_buffer = (guchar*)wmem_alloc(pinfo->pool, orig_size);
+
+ ret = snappy_uncompress(tvb_get_ptr(tvb, offset, -1),
+ tvb_captured_length_remaining(tvb, offset),
+ decompressed_buffer,
+ &orig_size);
+
+ if (ret == SNAPPY_OK) {
+ compressed_tvb = tvb_new_child_real_data(tvb, decompressed_buffer, (guint32)orig_size, (guint32)orig_size);
+ add_new_data_source(pinfo, compressed_tvb, "Decompressed Data");
+
+ dissect_opcode_types(compressed_tvb, pinfo, 0, tree, opcode, effective_opcode);
+ } else {
+ expert_add_info_format(pinfo, ti, &ei_mongo_unsupported_compression, "Error uncompressing snappy data");
+ }
+ } else {
+ if (orig_size > MAX_UNCOMPRESSED_SIZE) {
+ expert_add_info_format(pinfo, ti, &ei_mongo_too_large_compressed, "Uncompressed size too large");
+ } else {
+ expert_add_info_format(pinfo, ti, &ei_mongo_unsupported_compression, "Error uncompressing snappy data");
+ }
+ }
+
+ offset = tvb_reported_length(tvb);
+ } break;
+#endif
+
+ case MONGO_COMPRESSOR_ZLIB: {
+ tvbuff_t* compressed_tvb = NULL;
+
+ compressed_tvb = tvb_child_uncompress(tvb, tvb, offset, tvb_captured_length_remaining(tvb, offset));
+
+ if (compressed_tvb) {
+ add_new_data_source(pinfo, compressed_tvb, "Decompressed Data");
+
+ dissect_opcode_types(compressed_tvb, pinfo, 0, tree, opcode, effective_opcode);
+ } else {
+ proto_tree_add_item(compression_info_tree, hf_mongo_unsupported_compressed, tvb, offset, -1, ENC_NA);
+ expert_add_info_format(pinfo, ti, &ei_mongo_unsupported_compression, "Error uncompressing zlib data");
+ }
+
+ offset = tvb_reported_length(tvb);
+ } break;
+
+ default:
+ proto_tree_add_item(compression_info_tree, hf_mongo_unsupported_compressed, tvb, offset, -1, ENC_NA);
+ expert_add_info_format(pinfo, ti, &ei_mongo_unsupported_compression, "Unsupported compression format: %d", compressor);
+ offset = tvb_reported_length(tvb);
+ break;
+ }
+
+ return offset;
+}
+
+static int
dissect_op_msg_section(tvbuff_t *tvb, packet_info *pinfo, guint offset, proto_tree *tree)
{
proto_item *ti;
@@ -723,41 +850,9 @@ dissect_mongo_op_msg(tvbuff_t *tvb, packet_info *pinfo, guint offset, proto_tree
}
static int
-dissect_mongo_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
+dissect_opcode_types(tvbuff_t *tvb, packet_info *pinfo, guint offset, proto_tree *mongo_tree, guint opcode, guint *effective_opcode)
{
- proto_item *ti;
- proto_tree *mongo_tree;
- guint offset = 0, opcode;
-
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "MONGO");
-
- ti = proto_tree_add_item(tree, proto_mongo, tvb, 0, -1, ENC_NA);
-
- mongo_tree = proto_item_add_subtree(ti, ett_mongo);
-
- proto_tree_add_item(mongo_tree, hf_mongo_message_length, tvb, offset, 4, ENC_LITTLE_ENDIAN);
- offset += 4;
-
- proto_tree_add_item(mongo_tree, hf_mongo_request_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
- offset += 4;
-
- proto_tree_add_item(mongo_tree, hf_mongo_response_to, tvb, offset, 4, ENC_LITTLE_ENDIAN);
- offset += 4;
-
- proto_tree_add_item(mongo_tree, hf_mongo_op_code, tvb, offset, 4, ENC_LITTLE_ENDIAN);
- opcode = tvb_get_letohl(tvb, offset);
- offset += 4;
-
- if(opcode == 1)
- {
- col_set_str(pinfo->cinfo, COL_INFO, "Response :");
- }
- else
- {
- col_set_str(pinfo->cinfo, COL_INFO, "Request :");
-
- }
- col_append_fstr(pinfo->cinfo, COL_INFO, " %s", val_to_str_const(opcode, opcode_vals, "Unknown"));
+ *effective_opcode = opcode;
switch(opcode){
case OP_REPLY:
@@ -790,6 +885,9 @@ dissect_mongo_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
case OP_COMMANDREPLY:
offset = dissect_mongo_op_commandreply(tvb, pinfo, offset, mongo_tree);
break;
+ case OP_COMPRESSED:
+ offset = dissect_mongo_op_compressed(tvb, pinfo, offset, mongo_tree, effective_opcode);
+ break;
case OP_MSG:
offset = dissect_mongo_op_msg(tvb, pinfo, offset, mongo_tree);
break;
@@ -797,6 +895,53 @@ dissect_mongo_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
/* No default Action */
break;
}
+
+ return offset;
+}
+
+static int
+dissect_mongo_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
+{
+ proto_item *ti;
+ proto_tree *mongo_tree;
+ guint offset = 0, opcode, effective_opcode = 0;
+
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "MONGO");
+
+ ti = proto_tree_add_item(tree, proto_mongo, tvb, 0, -1, ENC_NA);
+
+ mongo_tree = proto_item_add_subtree(ti, ett_mongo);
+
+ proto_tree_add_item(mongo_tree, hf_mongo_message_length, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ proto_tree_add_item(mongo_tree, hf_mongo_request_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ proto_tree_add_item(mongo_tree, hf_mongo_response_to, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ proto_tree_add_item(mongo_tree, hf_mongo_op_code, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ opcode = tvb_get_letohl(tvb, offset);
+ offset += 4;
+
+ offset = dissect_opcode_types(tvb, pinfo, offset, mongo_tree, opcode, &effective_opcode);
+
+ if(opcode == 1)
+ {
+ col_set_str(pinfo->cinfo, COL_INFO, "Response :");
+ }
+ else
+ {
+ col_set_str(pinfo->cinfo, COL_INFO, "Request :");
+
+ }
+ col_append_fstr(pinfo->cinfo, COL_INFO, " %s", val_to_str_const(effective_opcode, opcode_vals, "Unknown"));
+
+ if(opcode != effective_opcode) {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (Compressed)");
+ }
+
if(offset < tvb_reported_length(tvb))
{
ti = proto_tree_add_item(mongo_tree, hf_mongo_unknown, tvb, offset, -1, ENC_NA);
@@ -1045,6 +1190,36 @@ proto_register_mongo(void)
"If set, the database will remove only the first matching document in the"
" collection. Otherwise all matching documents will be removed", HFILL }
},
+ { &hf_mongo_compression_info,
+ { "Compression Info", "mongo.compression",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ "Compressed Packet", HFILL }
+ },
+ { &hf_mongo_original_op_code,
+ { "Original OpCode", "mongo.compression.original_opcode",
+ FT_INT32, BASE_DEC, VALS(opcode_vals), 0x0,
+ "Type of request message (Wrapped)", HFILL }
+ },
+ { &hf_mongo_uncompressed_size,
+ { "Uncompressed Size", "mongo.compression.original_size",
+ FT_INT32, BASE_DEC, NULL, 0x0,
+ "Size of the uncompressed packet", HFILL }
+ },
+ { &hf_mongo_compressor,
+ { "Compressor", "mongo.compression.compressor",
+ FT_INT8, BASE_DEC, VALS(compressor_vals), 0x0,
+ "Compression engine", HFILL }
+ },
+ { &hf_mongo_compressed_data,
+ { "Compressed Data", "mongo.compression.compressed_data",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ "The compressed data", HFILL }
+ },
+ { &hf_mongo_unsupported_compressed,
+ { "Unsupported Compressed Data", "mongo.compression.unsupported_compressed",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ "This data is compressed with an unsupported compressor engine", HFILL }
+ },
{ &hf_mongo_msg_flags,
{ "Message Flags", "mongo.msg.flags",
FT_UINT32, BASE_HEX, NULL, 0x0,
@@ -1065,11 +1240,6 @@ proto_register_mongo(void)
FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x00010000,
"The client is prepared for multiple replies to this request using the moreToCome bit.", HFILL }
},
- { &hf_mongo_msg_sections,
- { "Sections", "mongo.msg.sections",
- FT_NONE, BASE_NONE, NULL, 0x0,
- "Message body sections", HFILL }
- },
{ &hf_mongo_msg_sections_section,
{ "Section", "mongo.msg.sections.section",
FT_NONE, BASE_NONE, NULL, 0x0,
@@ -1100,11 +1270,6 @@ proto_register_mongo(void)
FT_STRING, BASE_NONE, NULL, 0x0,
"Document sequence identifier", HFILL }
},
- { &hf_mongo_msg_sections_section_doc_sequence_documents,
- { "Documents", "mongo.msg.sections.section.doc_sequence_documents",
- FT_NONE, BASE_NONE, NULL, 0x0,
- NULL, HFILL }
- },
{ &hf_mongo_number_of_cursor_ids,
{ "Number of Cursor IDS", "mongo.number_to_cursor_ids",
FT_INT32, BASE_DEC, NULL, 0x0,
@@ -1266,6 +1431,7 @@ proto_register_mongo(void)
&ett_mongo_code,
&ett_mongo_fcn,
&ett_mongo_flags,
+ &ett_mongo_compression_info,
&ett_mongo_sections,
&ett_mongo_section,
&ett_mongo_msg_flags,
@@ -1276,6 +1442,8 @@ proto_register_mongo(void)
{ &ei_mongo_document_recursion_exceeded, { "mongo.document.recursion_exceeded", PI_MALFORMED, PI_ERROR, "BSON document recursion exceeds", EXPFILL }},
{ &ei_mongo_document_length_bad, { "mongo.document.length.bad", PI_MALFORMED, PI_ERROR, "BSON document length bad", EXPFILL }},
{ &ei_mongo_unknown, { "mongo.unknown.expert", PI_UNDECODED, PI_WARN, "Unknown Data (not interpreted)", EXPFILL }},
+ { &ei_mongo_unsupported_compression, { "mongo.unsupported_compression.expert", PI_UNDECODED, PI_WARN, "This packet was compressed with an unsupported compressor", EXPFILL }},
+ { &ei_mongo_too_large_compressed, { "mongo.too_large_compressed.expert", PI_UNDECODED, PI_WARN, "The size of the uncompressed packet exceeded the maximum allowed value", EXPFILL }},
};
proto_mongo = proto_register_protocol("Mongo Wire Protocol", "MONGO", "mongo");