aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-mcpe.c
diff options
context:
space:
mode:
authorPHO <pho@cielonegro.org>2016-10-03 09:22:35 +0900
committerAnders Broman <a.broman58@gmail.com>2016-10-12 03:57:03 +0000
commitc06189f7c6e6e428d914bfabdd96e5de42a2400d (patch)
treeba15bdfcf65047c6c50c94f48cbcfe4b70c99030 /epan/dissectors/packet-mcpe.c
parent1cd22559a8a907263f1d032e8066482b22f8fbe3 (diff)
raknet, mcpe: Improve dissectors
packet-raknet.c now correctly dissects the following offline messages: * ID_UNCONNECTED_PING * ID_UNCONNECTED_PING_OPEN_CONNECTIONS * ID_OPEN_CONNECTION_REQUEST_1 * ID_OPEN_CONNECTION_REPLY_1 * ID_OPEN_CONNECTION_REQUEST_2 * ID_OPEN_CONNECTION_REPLY_2 * ID_OUT_OF_BAND_INTERNAL * ID_CONNECTION_ATTEMPT_FAILED * ID_ALREADY_CONNECTED * ID_NO_FREE_INCOMING_CONNECTIONS * ID_CONNECTION_BANNED * ID_INCOMPATIBLE_PROTOCOL_VERSION * ID_IP_RECENTLY_CONNECTED * ID_UNCONNECTED_PONG packet-raknet.c now correctly dissects the following system messages: * ID_CONNECTED_PING * ID_CONNECTED_PONG * ID_CONNECTION_REQUEST * ID_CONNECTION_REQUEST_ACCEPTED * ID_NEW_INCOMING_CONNECTION packet-raknet.h exports the following functions: * raknet_add_udp_dissector() * raknet_delete_udp_dissector() * raknet_conversation_set_dissector() packet-raknet.c now dissects message flags, reliability, reliable message number and so on. It now reassembles fragmented packets, supports heuristics, supports dissecting combined packets, and gives up dissecting messages when they are encrypted. packet-raknet.c now calls subdissectors with a tvbuff buffer only having a message ID and payload. It first tries to locate a subdissector based on the port, and then tries heuristic dissectors if any. packet-mcpe.c is updated so that it uses the new raknet interface, and it now correctly dissects the following game packets: * 0x01 Login * 0x03 Server to Client Handshake * 0x06 Batch packet-mcpe.c now supports heuristics, and gives up dissecting packets in a conversation once it sees a "Server to Client Handshake" packet because everything, including packet ID, are encrypted after that. Change-Id: I92c0b3ff0f18d22d4513bb014aeb4ea6475fb06c Reviewed-on: https://code.wireshark.org/review/18044 Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'epan/dissectors/packet-mcpe.c')
-rw-r--r--epan/dissectors/packet-mcpe.c792
1 files changed, 572 insertions, 220 deletions
diff --git a/epan/dissectors/packet-mcpe.c b/epan/dissectors/packet-mcpe.c
index fba05f802b..1c486c73a6 100644
--- a/epan/dissectors/packet-mcpe.c
+++ b/epan/dissectors/packet-mcpe.c
@@ -28,294 +28,607 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
+
+#include <epan/conversation.h>
+#include <epan/expert.h>
#include <epan/packet.h>
#include <epan/prefs.h>
+#include "packet-raknet.h"
+
+/* Minecraft Pocket Edition Protocol
+ *
+ * See also:
+ * http://wiki.vg/Pocket_Edition_Protocol_Documentation
+ */
+
#define MCPE_UDP_PORT_DEFAULT 19132
static guint mcpe_udp_port_requested = MCPE_UDP_PORT_DEFAULT;
static int proto_mcpe = -1;
static gint ett_mcpe = -1; /* Should this node be expanded */
+static gint ett_mcpe_batch = -1;
+static gint ett_mcpe_batch_record = -1;
+static gint ett_mcpe_login = -1;
+static gint ett_mcpe_string = -1;
+/*
+ * Dissectors
+ */
+static dissector_handle_t mcpe_handle = NULL;
+static dissector_table_t mcpe_packet_dissectors = NULL;
/*
- * First byte gives us the packet id
+ * Expert fields
*/
+static expert_field ei_mcpe_unknown_packet_id = EI_INIT;
+static expert_field ei_mcpe_decompression_failed = EI_INIT;
+static expert_field ei_mcpe_encrypted_packet = EI_INIT;
+
+/*
+ * Common Header fields
+ */
+static int hf_mcpe_message_id = -1;
static int hf_mcpe_packet_id = -1;
+static int hf_mcpe_string_length = -1;
+static int hf_mcpe_UTF8_string = -1;
+static int hf_mcpe_byte_string = -1;
/*
- * Custom payload encoding header.
+ * Fields specific to a packet ID
*/
-static int hf_mcpe_payload_encoding = -1;
-static int hf_mcpe_general_packet_number = -1;
-static int hf_mcpe_general_packet_payload = -1;
-static int hf_mcpe_general_packet_payload_length = -1;
-static int hf_mcpe_general_packet_payload_count = -1;
+static int hf_mcpe_protocol_version = -1;
+static int hf_mcpe_login_data_length = -1;
+static int hf_mcpe_login_data = -1;
+static int hf_mcpe_login = -1;
+static int hf_mcpe_chain_JSON = -1;
+static int hf_mcpe_client_data_JWT = -1;
+static int hf_mcpe_public_key = -1;
+static int hf_mcpe_server_token = -1;
+
+static int hf_mcpe_batch_length = -1;
+static int hf_mcpe_batch_body = -1;
+static int hf_mcpe_batch_records = -1;
+static int hf_mcpe_batch_record_length = -1;
+static int hf_mcpe_batch_record = -1;
/*
- * Fields specific to a packet id type.
+ * RakNet Message ID
*/
-static int hf_mcpe_0xC0_unknown = -1;
-static int hf_mcpe_0xC0_single_packet = -1;
+static const value_string mcpe_message_names[] = {
+ { 0xFE, "Wrapper" },
+ { 0, NULL }
+};
/*
- * Forward declarations.
+ * Forward declarations
*/
void proto_register_mcpe(void);
void proto_reg_handoff_mcpe(void);
-
-static void
-mcpe_dissect_detail_0xA0(tvbuff_t *tvb, proto_tree *raknet_tree, gint offset)
-{
- gboolean single_packet;
- gint item_size;
-
- item_size = 2;
- proto_tree_add_item(raknet_tree, hf_mcpe_0xC0_unknown, tvb, offset,
- item_size, ENC_BIG_ENDIAN);
- offset += item_size;
-
- single_packet = (tvb_get_guint8(tvb, offset) != 0);
- item_size = 1;
- proto_tree_add_item(raknet_tree, hf_mcpe_0xC0_single_packet, tvb, offset,
- item_size, ENC_BIG_ENDIAN);
- offset += item_size;
-
- item_size = 3;
- proto_tree_add_item(raknet_tree, hf_mcpe_general_packet_number, tvb, offset,
- item_size, ENC_BIG_ENDIAN);
- offset += item_size;
-
- if (!single_packet) {
- item_size = 3;
- proto_tree_add_item(raknet_tree, hf_mcpe_general_packet_number, tvb,
- offset, item_size, ENC_BIG_ENDIAN);
- /*offset += item_size;*/
- }
-}
+static int mcpe_dissect_login(tvbuff_t*, packet_info*, proto_tree*, void*);
+static int mcpe_dissect_server_to_client_handshake(tvbuff_t*, packet_info*, proto_tree*, void*);
+static int mcpe_dissect_batch(tvbuff_t*, packet_info*, proto_tree*, void*);
/*
- * offset is updated for use by the caller.
+ * Protocol definition and handlers.
*/
-static void
-mcpe_dissect_detail_payload_0x00(tvbuff_t *tvb, proto_tree *mcpe_tree,
- gint *offset)
-{
- gint item_size;
+struct mcpe_handler_entry {
+ value_string vs;
+ dissector_t dissector_fp;
+};
+
+static const struct mcpe_handler_entry mcpe_packet_handlers[] = {
+ { { 0x01, "Login" },
+ mcpe_dissect_login },
+ { { 0x03, "Server to Client Handshake" },
+ mcpe_dissect_server_to_client_handshake },
+ { { 0x06, "Batch" },
+ mcpe_dissect_batch },
+};
- item_size = 2;
- proto_tree_add_item(mcpe_tree, hf_mcpe_general_packet_payload_length, tvb,
- *offset, item_size, ENC_BIG_ENDIAN);
- *offset += item_size;
-}
+/*
+ * Look up table from packet ID to name
+ */
+static value_string mcpe_packet_names[array_length(mcpe_packet_handlers)+1];
/*
- * offset is updated for use by the caller.
+ * Session state
*/
-static void
-mcpe_dissect_detail_payload_0x40(tvbuff_t *tvb, proto_tree *mcpe_tree,
- gint *offset)
-{
- gint item_size;
+typedef struct mcpe_session_state {
+ gboolean encrypted;
+ guint32 encryption_starts_after; /* Frame number */
+} mcpe_session_state_t;
- item_size = 2;
- proto_tree_add_item(mcpe_tree, hf_mcpe_general_packet_payload_length, tvb,
- *offset, item_size, ENC_BIG_ENDIAN);
- *offset += item_size;
+static mcpe_session_state_t*
+mcpe_get_session_state(packet_info *pinfo) {
+ conversation_t* conversation;
+ mcpe_session_state_t* state;
- item_size = 3;
- proto_tree_add_item(mcpe_tree, hf_mcpe_general_packet_payload_count, tvb,
- *offset, item_size, ENC_BIG_ENDIAN);
- *offset += item_size;
-}
+ conversation = find_or_create_conversation(pinfo);
+ state = (mcpe_session_state_t*)conversation_get_proto_data(conversation, proto_mcpe);
-static void
-mcpe_dissect_detail_payload(tvbuff_t *tvb, proto_tree *mcpe_tree, gint offset)
-{
- gint payload_encoding;
- gint item_size;
-
- item_size = 3;
- proto_tree_add_item(mcpe_tree, hf_mcpe_general_packet_number, tvb, offset,
- item_size, ENC_BIG_ENDIAN);
- offset += item_size;
-
- payload_encoding = tvb_get_guint8(tvb, offset);
-
- item_size = 1;
- proto_tree_add_item(mcpe_tree, hf_mcpe_payload_encoding, tvb, offset,
- item_size, ENC_BIG_ENDIAN);
- offset += item_size;
-
- switch (payload_encoding) {
- case 0x00:
- mcpe_dissect_detail_payload_0x00(tvb, mcpe_tree, &offset);
- break;
- case 0x40:
- case 0x50:
- case 0x60:
- /*
- * 0x50 and 0x60 contain extra fields before the payload. These fields
- * are currently unknown, so just use 0x40 for a correct partial
- * dissection.
- */
- mcpe_dissect_detail_payload_0x40(tvb, mcpe_tree, &offset);
- break;
- default:
- break;
+ if (state == NULL) {
+ state = (mcpe_session_state_t*)wmem_alloc(wmem_file_scope(), sizeof(mcpe_session_state_t));
+ state->encrypted = FALSE;
+ state->encryption_starts_after = 0;
+
+ conversation_add_proto_data(conversation, proto_mcpe, state);
}
- item_size = -1; /* Read to end of buffer */
- proto_tree_add_item(mcpe_tree, hf_mcpe_general_packet_payload, tvb, offset,
- item_size, ENC_NA);
+ return state;
}
/*
- * Common MCPE packet data
+ * Packet dissectors
*/
-static proto_tree *
-mcpe_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint *offset)
-{
- const char *packet_desc;
- guint8 packet_id;
- proto_tree *sub_tree;
+static void
+mcpe_dissect_string(proto_tree *tree, int hf, tvbuff_t *tvb, gint *offset, guint encoding) {
proto_item *ti;
+ proto_tree *string_tree;
+ guint32 length;
+ guint32 length_width;
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "MCPE");
- col_clear(pinfo->cinfo, COL_INFO);
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ /*
+ * Yes it's crazy. Lengths of string come with two flavors:
+ * big-endian uint16 and little-endian uint32.
+ */
+ length = tvb_get_letohl(tvb, *offset);
+ length_width = 4;
+ }
+ else {
+ length = tvb_get_ntohs(tvb, *offset);
+ length_width = 2;
+ }
- /*
- * Take buffer start 0 to end -1 as single mcpe item.
- */
- ti = proto_tree_add_item(tree, proto_mcpe, tvb, 0, -1, ENC_NA);
- sub_tree = proto_item_add_subtree(ti, ett_mcpe);
-
- packet_id = tvb_get_guint8(tvb, *offset);
- proto_tree_add_item(sub_tree, hf_mcpe_packet_id, tvb, *offset,
- 1, ENC_BIG_ENDIAN);
- *offset += 1;
-
- switch (packet_id) {
- case 0xA0:
- packet_desc = " (NACK)";
- break;
- case 0xC0:
- packet_desc = " (ACK)";
- break;
- default:
- packet_desc = "";
- break;
+ if (encoding & ENC_UTF_8) {
+ guint8 *string;
+
+ string = tvb_get_string_enc(wmem_packet_scope(), tvb, *offset + length_width, length, ENC_UTF_8);
+
+ ti = proto_tree_add_string(tree, hf, tvb, *offset, length + length_width, string);
+ string_tree = proto_item_add_subtree(ti, ett_mcpe_string);
+
+ proto_tree_add_item(string_tree, hf_mcpe_string_length, tvb,
+ *offset, length_width, encoding);
+ *offset += length_width;
+
+ proto_tree_add_item(string_tree, hf_mcpe_UTF8_string, tvb,
+ *offset, length, ENC_UTF_8|ENC_NA);
+ *offset += length;
}
- col_add_fstr(pinfo->cinfo, COL_INFO, "Type %#x%s", packet_id, packet_desc);
+ else {
+ guint8 *bytes;
+
+ bytes = (guint8*)tvb_memdup(wmem_packet_scope(), tvb, *offset + length_width, length);
+
+ ti = proto_tree_add_bytes_with_length(tree, hf, tvb, *offset, length + length_width, bytes, length);
+ string_tree = proto_item_add_subtree(ti, ett_mcpe_string);
- proto_item_append_text(ti, ", Packet id %#x", packet_id);
+ proto_tree_add_item(string_tree, hf_mcpe_string_length, tvb,
+ *offset, length_width, encoding);
+ *offset += length_width;
- return sub_tree;
+ proto_tree_add_item(string_tree, hf_mcpe_byte_string, tvb,
+ *offset, length, ENC_NA);
+ *offset += length;
+ }
}
static int
-mcpe_dissect(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
- void *data _U_)
+mcpe_dissect_login(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_)
{
- proto_tree *sub_tree;
- gint offset;
-
- offset = 0;
- sub_tree = mcpe_info(tvb, pinfo, tree, &offset);
- if (sub_tree != NULL) {
- mcpe_dissect_detail_payload(tvb, sub_tree, offset);
+ if (tree) {
+ gint item_size;
+ gint offset = 1;
+ guint32 comp_length;
+ proto_item *ti;
+ tvbuff_t *login_tvb;
+
+ item_size = 4;
+ proto_tree_add_item(tree, hf_mcpe_protocol_version, tvb,
+ offset, item_size, ENC_BIG_ENDIAN);
+ offset += item_size;
+
+ item_size = 4;
+ proto_tree_add_item_ret_uint(tree, hf_mcpe_login_data_length, tvb,
+ offset, item_size, ENC_BIG_ENDIAN, &comp_length);
+ offset += item_size;
+
+ item_size = comp_length;
+ ti = proto_tree_add_item(tree, hf_mcpe_login_data, tvb,
+ offset, item_size, ENC_NA);
+
+ login_tvb = tvb_uncompress(tvb, offset, comp_length);
+ if (login_tvb) {
+ guint32 decomp_length;
+ proto_tree *login_tree;
+
+ add_new_data_source(pinfo, login_tvb, "MCPE Decompressed login data");
+ decomp_length = tvb_captured_length(login_tvb);
+
+ offset = 0;
+ item_size = decomp_length;
+ ti = proto_tree_add_item(tree, hf_mcpe_login, login_tvb,
+ offset, item_size, ENC_NA);
+ login_tree = proto_item_add_subtree(ti, ett_mcpe_login);
+ proto_item_append_text(ti, " (%u octets)", decomp_length);
+ PROTO_ITEM_SET_GENERATED(ti);
+
+ mcpe_dissect_string(login_tree, hf_mcpe_chain_JSON , login_tvb, &offset, ENC_LITTLE_ENDIAN | ENC_UTF_8);
+ mcpe_dissect_string(login_tree, hf_mcpe_client_data_JWT, login_tvb, &offset, ENC_LITTLE_ENDIAN | ENC_UTF_8);
+ }
+ else {
+ expert_add_info(pinfo, ti, &ei_mcpe_decompression_failed);
+ }
}
return tvb_reported_length(tvb);
}
static int
-mcpe_dissect_0xA0(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
- void *data _U_)
+mcpe_dissect_server_to_client_handshake(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
- proto_tree *sub_tree;
- gint offset;
+ if (tree) {
+ gint offset = 1;
+ mcpe_session_state_t *state;
+
+ mcpe_dissect_string(tree, hf_mcpe_public_key, tvb, &offset, ENC_BIG_ENDIAN | ENC_UTF_8);
+ mcpe_dissect_string(tree, hf_mcpe_server_token, tvb, &offset, ENC_BIG_ENDIAN);
- offset = 0;
- sub_tree = mcpe_info(tvb, pinfo, tree, &offset);
- if (sub_tree != NULL) {
- mcpe_dissect_detail_0xA0(tvb, sub_tree, offset);
+ /*
+ * Everything will be encrypted once the server sends this.
+ */
+ state = mcpe_get_session_state(pinfo);
+ state->encrypted = TRUE;
+ state->encryption_starts_after = pinfo->num;
}
return tvb_reported_length(tvb);
}
static int
-mcpe_dissect_0xC0(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
- void *data _U_)
+mcpe_dissect_batch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
{
- proto_tree *sub_tree;
- gint offset;
-
- offset = 0;
- sub_tree = mcpe_info(tvb, pinfo, tree, &offset);
- if (sub_tree != NULL) {
- mcpe_dissect_detail_0xA0(tvb, sub_tree, offset); /* 0xA0 */
+ if (tree) {
+ guint32 item_size;
+ guint32 offset = 1;
+ proto_item *ti;
+ guint32 comp_length;
+ tvbuff_t *batch_tvb;
+
+ item_size = 4;
+ proto_tree_add_item_ret_uint(tree, hf_mcpe_batch_length, tvb,
+ offset, item_size, ENC_BIG_ENDIAN, &comp_length);
+ offset += item_size;
+
+ item_size = comp_length;
+ ti = proto_tree_add_item(tree, hf_mcpe_batch_body, tvb,
+ offset, item_size, ENC_NA);
+
+ batch_tvb = tvb_uncompress(tvb, offset, comp_length);
+ if (batch_tvb) {
+ guint32 decomp_length;
+ proto_tree *batch_tree;
+
+ add_new_data_source(pinfo, batch_tvb, "MCPE Decompressed batch");
+ decomp_length = tvb_captured_length(batch_tvb);
+
+ offset = 0;
+ item_size = decomp_length;
+ ti = proto_tree_add_item(tree, hf_mcpe_batch_records, batch_tvb,
+ offset, item_size, ENC_NA);
+ batch_tree = proto_item_add_subtree(ti, ett_mcpe_batch);
+ proto_item_append_text(ti, " (%u octets)", decomp_length);
+ PROTO_ITEM_SET_GENERATED(ti);
+
+ col_append_str(pinfo->cinfo, COL_INFO, " [");
+
+ while (TRUE) {
+ guint32 record_length;
+ tvbuff_t *record_tvb;
+ proto_tree *record_tree;
+ guint32 packet_id;
+ gint dissected;
+
+ item_size = 4;
+ proto_tree_add_item_ret_uint(batch_tree, hf_mcpe_batch_record_length, batch_tvb,
+ offset, item_size, ENC_BIG_ENDIAN, &record_length);
+ offset += item_size;
+
+ record_tvb = tvb_new_subset_length(batch_tvb, offset, record_length);
+ offset += record_length;
+
+ /*
+ * Take the whole buffer as a single MCPE packet.
+ */
+ ti = proto_tree_add_item(batch_tree, hf_mcpe_batch_record, record_tvb,
+ 0, -1, ENC_NA);
+ record_tree = proto_item_add_subtree(ti, ett_mcpe_batch_record);
+
+ /*
+ * The first octet is the packet ID.
+ */
+ proto_tree_add_item_ret_uint(record_tree, hf_mcpe_packet_id,
+ record_tvb, 0, 1, ENC_NA, &packet_id);
+
+ proto_item_append_text(ti, " (%s)",
+ val_to_str(packet_id, mcpe_packet_names, "Unknown ID: %#x"));
+ col_append_str(pinfo->cinfo, COL_INFO,
+ val_to_str(packet_id, mcpe_packet_names, "Unknown packet ID: %#x"));
+
+ dissected =
+ dissector_try_uint_new(mcpe_packet_dissectors, packet_id,
+ record_tvb, pinfo, record_tree, TRUE, data);
+ if (!dissected) {
+ expert_add_info(pinfo, ti, &ei_mcpe_unknown_packet_id);
+ }
+
+ if (offset < decomp_length) {
+ col_append_str(pinfo->cinfo, COL_INFO, ", ");
+ }
+ else {
+ break;
+ }
+ }
+
+ col_append_str(pinfo->cinfo, COL_INFO, "]");
+ }
+ else {
+ expert_add_info(pinfo, ti, &ei_mcpe_decompression_failed);
+ }
}
return tvb_reported_length(tvb);
}
-void
-proto_register_mcpe(void)
+static void
+mcpe_init_message_names(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < array_length(mcpe_packet_handlers); i++) {
+ mcpe_packet_names[i].value = mcpe_packet_handlers[i].vs.value;
+ mcpe_packet_names[i].strptr = mcpe_packet_handlers[i].vs.strptr;
+ }
+ mcpe_packet_names[array_length(mcpe_packet_handlers)].value = 0;
+ mcpe_packet_names[array_length(mcpe_packet_handlers)].strptr = NULL;
+}
+
+static gboolean
+test_mcpe_heur(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, void* data _U_)
{
/*
- * Arrays for detailed dissection, controls output formatting.
+ * 0xFE "Wrapper" is the only message ID that MCPE uses. The sole
+ * purpose of Wrapper message is to make a RakNet message out of a
+ * game packet.
*/
+ if (tvb_strneql(tvb, 0, "\xFE", 1) == 0) {
+ /*
+ * Does the message have a packet ID?
+ */
+ if (tvb_captured_length(tvb) >= 2) {
+ /*
+ * Inspect the packet ID. If it's known to us the message
+ * can be considered to be an MCPE packet.
+ */
+ gint8 packet_id = tvb_get_guint8(tvb, 1);
+
+ *(dissector_handle_t*)data =
+ dissector_get_uint_handle(mcpe_packet_dissectors, packet_id);
+
+ if (*(dissector_handle_t*)data) {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+static gboolean
+dissect_mcpe_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
+{
+ dissector_handle_t handle;
+
+ if (test_mcpe_heur(tvb, pinfo, tree, &handle)) {
+ proto_item *ti;
+ proto_tree *mcpe_tree;
+ guint32 message_id;
+ guint32 packet_id;
+ tvbuff_t *packet_tvb;
+
+ raknet_conversation_set_dissector(pinfo, mcpe_handle);
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "MCPE");
+ col_clear(pinfo->cinfo, COL_INFO);
+
+ /*
+ * Take the whole buffer as a single MCPE packet.
+ */
+ ti = proto_tree_add_item(tree, proto_mcpe, tvb, 0, -1, ENC_NA);
+ mcpe_tree = proto_item_add_subtree(ti, ett_mcpe);
+
+ /*
+ * The first octet is always 0xFE (Wrapper). We intentionally
+ * use DISSECTOR_ASSERT() here because test_mcpe_heur() has
+ * already tested it.
+ */
+ proto_tree_add_item_ret_uint(mcpe_tree, hf_mcpe_message_id,
+ tvb, 0, 1, ENC_NA, &message_id);
+ DISSECTOR_ASSERT(message_id == 0xFE);
+
+ /*
+ * The next octet is the packet ID.
+ */
+ proto_tree_add_item_ret_uint(mcpe_tree, hf_mcpe_packet_id,
+ tvb, 1, 1, ENC_NA, &packet_id);
+
+ proto_item_append_text(ti, " (%s)",
+ val_to_str(packet_id, mcpe_packet_names, "Unknown ID: %#x"));
+ col_add_str(pinfo->cinfo, COL_INFO,
+ val_to_str(packet_id, mcpe_packet_names, "Unknown packet ID: %#x"));
+
+ packet_tvb = tvb_new_subset_remaining(tvb, 1);
+ return call_dissector_only(handle, packet_tvb, pinfo, mcpe_tree, data) > 0;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+static int
+dissect_mcpe(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
+{
+ mcpe_session_state_t *state;
+
+ state = mcpe_get_session_state(pinfo);
+ if (state->encrypted && pinfo->num > state->encryption_starts_after) {
+ /*
+ * Encrypted packets don't even have any headers indicating
+ * they are encrypted. And we don't support the cipher they
+ * use.
+ */
+ proto_item *ti;
+ gint packet_size;
+
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "MCPE");
+ col_add_str(pinfo->cinfo, COL_INFO, "Encrypted packet");
+
+ packet_size = tvb_reported_length(tvb);
+ ti = proto_tree_add_item(tree, proto_mcpe, tvb, 0, packet_size, ENC_NA);
+ proto_item_append_text(ti, ", Encrypted packet (%d octets)", packet_size);
+ expert_add_info(pinfo, ti, &ei_mcpe_encrypted_packet);
+
+ return tvb_captured_length(tvb);
+ }
+ else {
+ /*
+ * We reuse our heuristic dissector here because we have no reason
+ * to implement almost the same dissector twice.
+ */
+ if (dissect_mcpe_heur(tvb, pinfo, tree, data)) {
+ return tvb_captured_length(tvb);
+ }
+ else {
+ return 0;
+ }
+ }
+}
+
+void
+proto_register_mcpe(void)
+{
static hf_register_info hf[] = {
+ /*
+ * Common Header fields
+ */
+ { &hf_mcpe_message_id,
+ { "MCPE Message ID", "mcpe.message.id",
+ FT_UINT8, BASE_HEX,
+ VALS(mcpe_message_names), 0x0,
+ NULL, HFILL }
+ },
{ &hf_mcpe_packet_id,
- { "MCPE Packet ID", "mcpe.type",
+ { "MCPE Packet ID", "mcpe.packet.id",
FT_UINT8, BASE_HEX,
+ VALS(mcpe_packet_names), 0x0,
+ NULL, HFILL }
+ },
+ { &hf_mcpe_string_length,
+ { "MCPE String length", "mcpe.string.length",
+ FT_UINT32, BASE_DEC,
NULL, 0x0,
NULL, HFILL }
},
- { &hf_mcpe_payload_encoding,
- { "MCPE Payload encoding", "mcpe.payload_encoding",
- FT_UINT8, BASE_HEX,
+ { &hf_mcpe_UTF8_string,
+ { "MCPE UTF-8 String", "mcpe.string.UTF8",
+ FT_STRING, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_mcpe_byte_string,
+ { "MCPE Byte string", "mcpe.string.bytes",
+ FT_BYTES, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ /*
+ * Fields specific to a packet ID
+ */
+ { &hf_mcpe_protocol_version,
+ { "MCPE Protocol version", "mcpe.protocol.version",
+ FT_UINT32, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_mcpe_login_data_length,
+ { "MCPE Compressed login data length", "mcpe.login.data.length",
+ FT_UINT32, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_mcpe_login_data,
+ { "MCPE Compressed login data", "mcpe.login.data",
+ FT_NONE, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_mcpe_login,
+ { "MCPE Decompressed login data", "mcpe.login",
+ FT_NONE, BASE_NONE,
NULL, 0x0,
NULL, HFILL }
},
- { &hf_mcpe_general_packet_number,
- { "MCPE Packet number", "mcpe.packet_number",
- FT_UINT24, BASE_DEC,
+ { &hf_mcpe_chain_JSON,
+ { "MCPE Chain JSON", "mcpe.chain.JSON",
+ FT_STRING, BASE_NONE,
NULL, 0x0,
NULL, HFILL }
},
- { &hf_mcpe_general_packet_payload,
- { "MCPE Packet Payload", "mcpe.packet_payload",
+ { &hf_mcpe_client_data_JWT,
+ { "MCPE Client data JWT", "mcpe.client.data.JWT",
+ FT_STRING, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_mcpe_public_key,
+ { "MCPE Public key", "mcpe.public.key",
+ FT_STRING, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_mcpe_server_token,
+ { "MCPE Server token", "mcpe.server.token",
FT_BYTES, BASE_NONE,
NULL, 0x0,
NULL, HFILL }
},
- { &hf_mcpe_general_packet_payload_length,
- { "MCPE Payload length in *bits*", "mcpe.payload_length_bits",
- FT_UINT16, BASE_DEC,
+ { &hf_mcpe_batch_length,
+ { "MCPE Compressed batch length", "mcpe.batch.length",
+ FT_UINT32, BASE_DEC,
NULL, 0x0,
NULL, HFILL }
},
- { &hf_mcpe_general_packet_payload_count,
- { "MCPE Packet payload count", "mcpe.payload_count",
- FT_UINT24, BASE_DEC,
+ { &hf_mcpe_batch_body,
+ { "MCPE Compressed batch body", "mcpe.batch.body",
+ FT_NONE, BASE_NONE,
NULL, 0x0,
NULL, HFILL }
},
- /*
- * Packet ID 0xC0
- */
- { &hf_mcpe_0xC0_unknown,
- { "MCPE Unknown field", "mcpe.unknown",
- FT_UINT8, BASE_DEC,
+ { &hf_mcpe_batch_records,
+ { "MCPE Decompressed batch records", "mcpe.batch.records",
+ FT_NONE, BASE_NONE,
NULL, 0x0,
NULL, HFILL }
},
- { &hf_mcpe_0xC0_single_packet,
- { "MCPE single packet (boolean)", "mcpe.single_packet",
- FT_UINT8, BASE_DEC,
+ { &hf_mcpe_batch_record_length,
+ { "MCPE Batch record length", "mcpe.batch.record.length",
+ FT_UINT32, BASE_DEC,
NULL, 0x0,
NULL, HFILL }
- }
+ },
+ { &hf_mcpe_batch_record,
+ { "MCPE Batch record", "mcpe.batch.record",
+ FT_NONE, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
};
/*
@@ -323,10 +636,41 @@ proto_register_mcpe(void)
*/
static gint *ett[] = {
&ett_mcpe,
+ &ett_mcpe_batch,
+ &ett_mcpe_batch_record,
+ &ett_mcpe_login,
+ &ett_mcpe_string,
};
module_t *mcpe_module;
/*
+ * Set up expert info.
+ */
+ static ei_register_info ei[] = {
+ { &ei_mcpe_unknown_packet_id,
+ { "mcpe.unknown.id", PI_UNDECODED, PI_WARN,
+ "MCPE unknown packet ID",
+ EXPFILL }
+ },
+ { &ei_mcpe_decompression_failed,
+ { "mcpe.decompression.failed", PI_MALFORMED, PI_ERROR,
+ "MCPE packet decompression failed",
+ EXPFILL }
+ },
+ { &ei_mcpe_encrypted_packet,
+ { "mcpe.encrypted", PI_DECRYPTION, PI_NOTE,
+ "MCPE encrypted packet",
+ EXPFILL }
+ },
+ };
+ expert_module_t *expert_mcpe;
+
+ /*
+ * Init data structs.
+ */
+ mcpe_init_message_names();
+
+ /*
* Register the protocol with wireshark.
*/
proto_mcpe = proto_register_protocol (
@@ -336,14 +680,31 @@ proto_register_mcpe(void)
);
/*
+ * Register expert support.
+ */
+ expert_mcpe = expert_register_protocol(proto_mcpe);
+ expert_register_field_array(expert_mcpe, ei, array_length(ei));
+
+ /*
* Register detailed dissection arrays.
*/
proto_register_field_array(proto_mcpe, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
+ /*
+ * Register dissectors.
+ */
+ mcpe_handle =
+ register_dissector("mcpe", dissect_mcpe, proto_mcpe);
+
+ mcpe_packet_dissectors =
+ register_dissector_table("mcpe.packet.id", "MCPE packets",
+ proto_mcpe, FT_UINT8, BASE_HEX);
+
/* Register a configuration option for UDP port */
- mcpe_module = prefs_register_protocol(proto_mcpe,
- proto_reg_handoff_mcpe);
+ mcpe_module =
+ prefs_register_protocol(proto_mcpe, proto_reg_handoff_mcpe);
+
prefs_register_uint_preference(mcpe_module, "udp.port",
"MCPE Server UDP Port",
"Set the UDP port for the MCPE Server",
@@ -353,41 +714,32 @@ proto_register_mcpe(void)
void
proto_reg_handoff_mcpe(void)
{
- static dissector_handle_t raknet_dissector = NULL;
static guint last_server_port;
static gboolean init_done = FALSE;
if (init_done) {
- /*
- * Just delete the dissector before the new add.
- */
- dissector_delete_uint("udp.port", last_server_port, raknet_dissector);
- } else {
- /*
- * First time, create dissector handle, and find raknet dissector.
- */
- dissector_handle_t mcpe_gen_handle;
-
- init_done = TRUE;
- raknet_dissector = find_dissector("raknet");
-
- mcpe_gen_handle = create_dissector_handle(mcpe_dissect, proto_mcpe);
- dissector_add_uint("raknet.packet_id", 0x80, mcpe_gen_handle);
- dissector_add_uint("raknet.packet_id", 0x84, mcpe_gen_handle);
- dissector_add_uint("raknet.packet_id", 0x88, mcpe_gen_handle);
- dissector_add_uint("raknet.packet_id", 0x8C, mcpe_gen_handle);
- dissector_add_uint("raknet.packet_id", 0xA0,
- create_dissector_handle(mcpe_dissect_0xA0,
- proto_mcpe));
- dissector_add_uint("raknet.packet_id", 0xC0,
- create_dissector_handle(mcpe_dissect_0xC0,
- proto_mcpe));
+ raknet_delete_udp_dissector(last_server_port, mcpe_handle);
+ }
+ else {
+ unsigned int i;
+
+ for (i = 0; i < array_length(mcpe_packet_handlers); i++) {
+ dissector_add_uint(
+ "mcpe.packet.id",
+ mcpe_packet_handlers[i].vs.value,
+ create_dissector_handle(
+ mcpe_packet_handlers[i].dissector_fp, proto_mcpe));
+ }
+
+ heur_dissector_add("raknet", dissect_mcpe_heur,
+ "MCPE over RakNet", "mcpe_raknet", proto_mcpe, HEURISTIC_ENABLE);
}
last_server_port = mcpe_udp_port_requested;
+ init_done = TRUE;
- /* MCPE is the protocol that carries RakNet packets over UDP */
- dissector_add_uint("udp.port", mcpe_udp_port_requested, raknet_dissector);
+ /* MCPE is a protocol that carries RakNet packets over UDP */
+ raknet_add_udp_dissector(mcpe_udp_port_requested, mcpe_handle);
}
/*