summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-raknet.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/dissectors/packet-raknet.c
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'epan/dissectors/packet-raknet.c')
-rw-r--r--epan/dissectors/packet-raknet.c2036
1 files changed, 2036 insertions, 0 deletions
diff --git a/epan/dissectors/packet-raknet.c b/epan/dissectors/packet-raknet.c
new file mode 100644
index 00000000..9911fc34
--- /dev/null
+++ b/epan/dissectors/packet-raknet.c
@@ -0,0 +1,2036 @@
+/*
+ * packet-raknet.c
+ *
+ * Routines for RakNet protocol packet disassembly.
+ *
+ * Ref: https://github.com/OculusVR/RakNet
+ *
+ * Nick Carter <ncarter100@gmail.com>
+ * Copyright 2014 Nick Carter
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "config.h"
+
+#include <epan/conversation.h>
+#include <epan/exceptions.h>
+#include <epan/expert.h>
+#include <epan/packet.h>
+#include <epan/reassemble.h>
+#include <epan/to_str.h>
+#include <epan/wmem_scopes.h>
+
+#include "packet-raknet.h"
+
+/*
+ * RakNet Protocol Constants.
+ */
+static guint8 RAKNET_OFFLINE_MESSAGE_DATA_ID[16] = {0x00, 0xff, 0xff, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0x12, 0x34, 0x56, 0x78};
+#define RAKNET_CHALLENGE_LENGTH 64
+#define RAKNET_ANSWER_LENGTH 128
+#define RAKNET_PROOF_LENGTH 32
+#define RAKNET_IDENTITY_LENGTH 160
+#define RAKNET_NUMBER_OF_INTERNAL_IDS 10
+
+static int proto_raknet = -1;
+static gint ett_raknet = -1; /* Should this node be expanded */
+static gint ett_raknet_system_address = -1;
+static gint ett_raknet_packet_type = -1;
+static gint ett_raknet_packet_number_range = -1;
+static gint ett_raknet_message = -1;
+static gint ett_raknet_message_flags = -1;
+static gint ett_raknet_system_message = -1;
+
+/*
+ * Dissectors
+ */
+static dissector_handle_t raknet_handle = NULL;
+static dissector_table_t raknet_offline_message_dissectors = NULL;
+static dissector_table_t raknet_system_message_dissectors = NULL;
+static dissector_table_t raknet_port_dissectors = NULL;
+static heur_dissector_list_t raknet_heur_subdissectors = NULL;
+
+/*
+ * Expert fields
+ */
+static expert_field ei_raknet_unknown_message_id = EI_INIT;
+static expert_field ei_raknet_encrypted_message = EI_INIT;
+static expert_field ei_raknet_subdissector_failed = EI_INIT;
+static expert_field ei_raknet_ip_ver_invalid = EI_INIT;
+
+/*
+ * First byte gives us the packet id
+ */
+static int hf_raknet_offline_message_id = -1;
+
+/*
+ * General fields (fields that are in >1 packet types.
+ */
+static int hf_raknet_client_guid = -1;
+static int hf_raknet_timestamp = -1;
+static int hf_raknet_offline_message_data_id = -1;
+static int hf_raknet_mtu_size = -1;
+static int hf_raknet_raknet_proto_ver = -1;
+static int hf_raknet_server_guid = -1;
+static int hf_raknet_ip_version = -1;
+static int hf_raknet_ipv4_address = -1;
+static int hf_raknet_ipv6_address = -1;
+static int hf_raknet_port = -1;
+
+/*
+ * Fields specific to a packet id type
+ */
+static int hf_raknet_null_padding = -1;
+static int hf_raknet_use_encryption = -1;
+static int hf_raknet_server_public_key = -1;
+static int hf_raknet_cookie = -1;
+static int hf_raknet_client_wrote_challenge = -1;
+static int hf_raknet_client_challenge = -1;
+static int hf_raknet_client_address = -1;
+static int hf_raknet_server_address = -1;
+static int hf_raknet_server_answer = -1;
+static int hf_raknet_0x1C_server_id_str_len = -1;
+static int hf_raknet_0x1C_server_id_str = -1;
+static int hf_raknet_packet_type = -1;
+static int hf_raknet_packet_is_for_connected = -1;
+static int hf_raknet_packet_is_ACK = -1;
+static int hf_raknet_packet_has_B_and_AS = -1;
+static int hf_raknet_packet_is_NAK = -1;
+static int hf_raknet_packet_is_pair = -1;
+static int hf_raknet_packet_is_continuous_send = -1;
+static int hf_raknet_packet_needs_B_and_AS = -1;
+static int hf_raknet_AS = -1;
+static int hf_raknet_NACK_record_count = -1;
+static int hf_raknet_packet_number_range = -1;
+static int hf_raknet_range_max_equal_to_min = -1;
+static int hf_raknet_packet_number_min = -1;
+static int hf_raknet_packet_number_max = -1;
+static int hf_raknet_packet_number = -1;
+static int hf_raknet_message = -1;
+static int hf_raknet_message_flags = -1;
+static int hf_raknet_message_reliability = -1;
+static int hf_raknet_message_has_split_packet = -1;
+static int hf_raknet_payload_length = -1;
+static int hf_raknet_reliable_message_number = -1;
+static int hf_raknet_message_sequencing_index = -1;
+static int hf_raknet_message_ordering_index = -1;
+static int hf_raknet_message_ordering_channel = -1;
+static int hf_raknet_split_packet_count = -1;
+static int hf_raknet_split_packet_id = -1;
+static int hf_raknet_split_packet_index = -1;
+static int hf_raknet_split_packet = -1;
+static int hf_raknet_system_message = -1;
+static int hf_raknet_system_message_id = -1;
+static int hf_raknet_client_proof = -1;
+static int hf_raknet_use_client_key = -1;
+static int hf_raknet_client_identity = -1;
+static int hf_raknet_password = -1;
+static int hf_raknet_system_index = -1;
+static int hf_raknet_internal_address = -1;
+
+/*
+ * Frame reassembly
+ */
+static reassembly_table raknet_reassembly_table;
+
+static gint ett_raknet_fragment = -1;
+static gint ett_raknet_fragments = -1;
+static gint hf_raknet_fragment = -1;
+static gint hf_raknet_fragment_count = -1;
+static gint hf_raknet_fragment_error = -1;
+static gint hf_raknet_fragment_multiple_tails = -1;
+static gint hf_raknet_fragment_overlap = -1;
+static gint hf_raknet_fragment_overlap_conflicts = -1;
+static gint hf_raknet_fragment_too_long_fragment = -1;
+static gint hf_raknet_fragments = -1;
+static gint hf_raknet_reassembled_in = -1;
+static gint hf_raknet_reassembled_length = -1;
+
+static const fragment_items raknet_frag_items = {
+ /* Fragment subtrees */
+ &ett_raknet_fragment,
+ &ett_raknet_fragments,
+ /* Fragment fields */
+ &hf_raknet_fragments,
+ &hf_raknet_fragment,
+ &hf_raknet_fragment_overlap,
+ &hf_raknet_fragment_overlap_conflicts,
+ &hf_raknet_fragment_multiple_tails,
+ &hf_raknet_fragment_too_long_fragment,
+ &hf_raknet_fragment_error,
+ &hf_raknet_fragment_count,
+ /* Reassembled in field */
+ &hf_raknet_reassembled_in,
+ /* Reassembled length field */
+ &hf_raknet_reassembled_length,
+ /* Reassembled data field */
+ NULL,
+ /* Tag */
+ "Message fragments"
+};
+
+/*
+ * Session state
+ */
+typedef struct raknet_session_state {
+ gboolean use_encryption;
+ dissector_handle_t subdissector;
+} raknet_session_state_t;
+
+/*
+ * Reliability strings
+ * Ref: ..RakNet/Source/PacketPriority.h
+ *
+ * Note that ACK receipts will not be transmitted to the wire. See
+ * ReliabilityLayer::WriteToBitStreamFromInternalPacket()
+ */
+#define raknet_reliability_VALUE_STRING_LIST(VS) \
+ VS( RAKNET_UNRELIABLE , 0, "unreliable" ) \
+ VS( RAKNET_UNRELIABLE_SEQUENCED, 1, "unreliable sequenced" ) \
+ VS( RAKNET_RELIABLE , 2, "reliable" ) \
+ VS( RAKNET_RELIABLE_ORDERED , 3, "reliable ordered" ) \
+ VS( RAKNET_RELIABLE_SEQUENCED , 4, "reliable sequenced" )
+
+typedef VALUE_STRING_ENUM(raknet_reliability) raknet_reliability_t;
+VALUE_STRING_ARRAY(raknet_reliability);
+
+/*
+ * Forward declarations.
+ */
+void proto_register_raknet(void);
+void proto_reg_handoff_raknet(void);
+static proto_tree *init_raknet_offline_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint *offset);
+
+
+/*
+ * Called by dissectors for protocols that run atop RakNet/UDP.
+ */
+void
+raknet_add_udp_dissector(guint32 port, const dissector_handle_t handle) {
+ /*
+ * Register ourselves as the handler for that port number
+ * over TCP.
+ */
+ dissector_add_uint("udp.port", port, raknet_handle);
+
+ /*
+ * And register them in *our* table for that port.
+ */
+ dissector_add_uint("raknet.port", port, handle);
+}
+
+void
+raknet_delete_udp_dissector(guint32 port, const dissector_handle_t handle) {
+ dissector_delete_uint("udp.port", port, raknet_handle);
+ dissector_delete_uint("raknet.port", port, handle);
+}
+
+static raknet_session_state_t*
+raknet_get_session_state(packet_info *pinfo) {
+ conversation_t* conversation;
+ raknet_session_state_t* state;
+
+ conversation = find_or_create_conversation(pinfo);
+ state = (raknet_session_state_t*)conversation_get_proto_data(conversation, proto_raknet);
+
+ if (state == NULL) {
+ state = wmem_new(wmem_file_scope(), raknet_session_state_t);
+ state->use_encryption = FALSE;
+ state->subdissector = NULL;
+ conversation_add_proto_data(conversation, proto_raknet, state);
+ }
+
+ return state;
+}
+
+void
+raknet_conversation_set_dissector(packet_info *pinfo, const dissector_handle_t handle) {
+ raknet_session_state_t *state;
+
+ state = raknet_get_session_state(pinfo);
+ state->subdissector = handle;
+}
+
+static void
+raknet_dissect_system_address(proto_tree *tree, int hf,
+ packet_info *pinfo, tvbuff_t *tvb, gint *offset) {
+ proto_item *ti;
+ proto_tree *sub_tree;
+ guint8 ip_version;
+ guint32 v4_addr;
+ guint16 port;
+ address addr;
+ gchar *addr_str;
+
+ /* XXX - does it really make sense to have a string hf that's set to
+ an empty string? */
+ ti = proto_tree_add_string(tree, hf, tvb, *offset, -1, "");
+ sub_tree = proto_item_add_subtree(ti, ett_raknet_system_address);
+ ip_version = tvb_get_guint8(tvb, *offset);
+ proto_tree_add_item(sub_tree, hf_raknet_ip_version, tvb, *offset, 1, ENC_NA);
+ (*offset)++;
+ switch (ip_version) {
+ case 4:
+ /*
+ * IPv4 addresses are bit-inverted to prevent routers from
+ * changing them. See ..RakNet/Source/BitStream.h
+ * (BitStream::Write)
+ */
+ v4_addr = ~tvb_get_ipv4(tvb, *offset);
+ set_address(&addr, AT_IPv4, sizeof(v4_addr), &v4_addr);
+ addr_str = address_to_display(pinfo->pool, &addr);
+ proto_tree_add_ipv4(sub_tree, hf_raknet_ipv4_address, tvb, *offset, 4, v4_addr);
+ *offset += 4;
+ port = tvb_get_ntohs(tvb, *offset);
+ proto_tree_add_item(sub_tree, hf_raknet_port, tvb, *offset, 2, ENC_BIG_ENDIAN);
+ *offset += 2;
+ proto_item_set_len(ti, 1 + 4 + 2);
+ proto_item_append_text(ti, "%s:%" PRIu16, addr_str, port);
+ break;
+ case 6:
+ addr_str = tvb_ip6_to_str(pinfo->pool, tvb, *offset);
+ proto_tree_add_item(sub_tree, hf_raknet_ipv6_address, tvb, *offset, 16, ENC_NA);
+ *offset += 16;
+ port = tvb_get_ntohs(tvb, *offset);
+ proto_tree_add_item(sub_tree, hf_raknet_port, tvb, *offset, 2, ENC_BIG_ENDIAN);
+ *offset += 2;
+ proto_item_set_len(ti, 1 + 16 + 2);
+ proto_item_append_text(ti, "[%s]:%" PRIu16, addr_str, port);
+ break;
+ default:
+ proto_item_set_len(ti, 1);
+ expert_add_info(pinfo, sub_tree, &ei_raknet_ip_ver_invalid);
+ }
+}
+
+static int
+raknet_dissect_unconnected_ping(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_)
+{
+ proto_tree *sub_tree;
+ gint offset;
+
+ sub_tree = init_raknet_offline_message(tvb, pinfo, tree, &offset);
+
+ proto_tree_add_item(sub_tree, hf_raknet_timestamp, tvb,
+ offset, 8, ENC_BIG_ENDIAN);
+ offset += 8;
+
+ proto_tree_add_item(sub_tree, hf_raknet_offline_message_data_id, tvb, offset,
+ 16, ENC_NA);
+ offset += 16;
+
+ proto_tree_add_item(sub_tree, hf_raknet_client_guid, tvb,
+ offset, 8, ENC_NA);
+ offset += 8;
+
+ return offset;
+}
+
+static int
+raknet_dissect_open_connection_request_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_)
+{
+ proto_tree *sub_tree;
+ gint offset;
+
+ sub_tree = init_raknet_offline_message(tvb, pinfo, tree, &offset);
+
+ proto_tree_add_item(sub_tree, hf_raknet_offline_message_data_id, tvb, offset,
+ 16, ENC_NA);
+ offset += 16;
+
+ proto_tree_add_item(sub_tree, hf_raknet_raknet_proto_ver, tvb, offset,
+ 1, ENC_BIG_ENDIAN);
+ offset += 1;
+
+ /* -1 read to end of tvb buffer */
+ proto_tree_add_item(sub_tree, hf_raknet_null_padding, tvb, offset,
+ -1, ENC_NA);
+
+ return tvb_reported_length(tvb);
+}
+
+static int
+raknet_dissect_open_connection_reply_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_)
+{
+ proto_tree *sub_tree;
+ gint offset;
+ raknet_session_state_t* state;
+
+ sub_tree = init_raknet_offline_message(tvb, pinfo, tree, &offset);
+
+
+ proto_tree_add_item(sub_tree, hf_raknet_offline_message_data_id, tvb, offset,
+ 16, ENC_NA);
+ offset += 16;
+
+ proto_tree_add_item(sub_tree, hf_raknet_server_guid, tvb, offset,
+ 8, ENC_NA);
+ offset += 8;
+
+ state = raknet_get_session_state(pinfo);
+ state->use_encryption = tvb_get_guint8(tvb, offset) ? TRUE : FALSE;
+
+ proto_tree_add_item(sub_tree, hf_raknet_use_encryption, tvb,
+ offset, 1, ENC_NA);
+ offset += 1;
+
+ if (state->use_encryption) {
+ proto_tree_add_item(sub_tree, hf_raknet_cookie, tvb,
+ offset, 4, ENC_BIG_ENDIAN);
+ offset += 4;
+
+ proto_tree_add_item(sub_tree, hf_raknet_server_public_key, tvb,
+ offset, 64, ENC_NA);
+ offset += 64;
+ }
+
+ proto_tree_add_item(sub_tree, hf_raknet_mtu_size, tvb, offset,
+ 2, ENC_BIG_ENDIAN);
+ offset += 2;
+
+ return offset;
+}
+
+static int
+raknet_dissect_open_connection_request_2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_)
+{
+ proto_tree *sub_tree;
+ gint offset;
+ raknet_session_state_t* state;
+
+ sub_tree = init_raknet_offline_message(tvb, pinfo, tree, &offset);
+
+ proto_tree_add_item(sub_tree, hf_raknet_offline_message_data_id, tvb, offset,
+ 16, ENC_NA);
+ offset += 16;
+
+ state = raknet_get_session_state(pinfo);
+ if (state->use_encryption) {
+ gboolean client_wrote_challenge;
+
+ proto_tree_add_item(sub_tree, hf_raknet_cookie, tvb, offset,
+ 4, ENC_BIG_ENDIAN);
+ offset += 4;
+
+ client_wrote_challenge = tvb_get_guint8(tvb, offset) ? TRUE : FALSE;
+ proto_tree_add_item(sub_tree, hf_raknet_client_wrote_challenge, tvb, offset,
+ 1, ENC_NA);
+ offset += 1;
+
+ if (client_wrote_challenge) {
+ proto_tree_add_item(sub_tree, hf_raknet_client_challenge, tvb,
+ offset, 64, ENC_NA);
+ offset += 64;
+ }
+ }
+
+ raknet_dissect_system_address(
+ sub_tree, hf_raknet_server_address, pinfo, tvb, &offset);
+
+ proto_tree_add_item(sub_tree, hf_raknet_mtu_size, tvb, offset,
+ 2, ENC_BIG_ENDIAN);
+ offset += 2;
+
+ proto_tree_add_item(sub_tree, hf_raknet_client_guid, tvb, offset,
+ 8, ENC_NA);
+ offset += 8;
+
+ return offset;
+}
+
+static int
+raknet_dissect_open_connection_reply_2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_)
+{
+ proto_tree *sub_tree;
+ gint offset;
+
+ sub_tree = init_raknet_offline_message(tvb, pinfo, tree, &offset);
+ gboolean use_encryption;
+
+ proto_tree_add_item(sub_tree, hf_raknet_offline_message_data_id, tvb, offset,
+ 16, ENC_NA);
+ offset += 16;
+
+ proto_tree_add_item(sub_tree, hf_raknet_server_guid, tvb, offset,
+ 8, ENC_NA);
+ offset += 8;
+
+ raknet_dissect_system_address(
+ sub_tree, hf_raknet_client_address, pinfo, tvb, &offset);
+
+ proto_tree_add_item(sub_tree, hf_raknet_mtu_size, tvb, offset,
+ 2, ENC_BIG_ENDIAN);
+ offset += 2;
+
+ use_encryption = tvb_get_guint8(tvb, offset) ? TRUE : FALSE;
+
+ proto_tree_add_item(sub_tree, hf_raknet_use_encryption, tvb, offset,
+ 1, ENC_NA);
+ offset += 1;
+
+ if (use_encryption) {
+ proto_tree_add_item(sub_tree, hf_raknet_server_answer, tvb, offset,
+ 128, ENC_NA);
+ offset += 128;
+ }
+
+ return offset;
+}
+
+static int
+raknet_dissect_incompatible_protocol_version(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_)
+{
+ proto_tree *sub_tree;
+ gint offset;
+
+ sub_tree = init_raknet_offline_message(tvb, pinfo, tree, &offset);
+
+ proto_tree_add_item(sub_tree, hf_raknet_raknet_proto_ver, tvb,
+ offset, 1, ENC_BIG_ENDIAN);
+ offset += 1;
+
+ proto_tree_add_item(sub_tree, hf_raknet_offline_message_data_id, tvb, offset,
+ 16, ENC_NA);
+ offset += 16;
+
+ proto_tree_add_item(sub_tree, hf_raknet_server_guid, tvb, offset,
+ 8, ENC_NA);
+ offset += 8;
+
+ return offset;
+}
+
+static int
+raknet_dissect_connection_failed(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_)
+{
+ proto_tree *sub_tree;
+ gint offset;
+
+ sub_tree = init_raknet_offline_message(tvb, pinfo, tree, &offset);
+
+ proto_tree_add_item(sub_tree, hf_raknet_offline_message_data_id, tvb, offset,
+ 16, ENC_NA);
+ offset += 16;
+
+ proto_tree_add_item(sub_tree, hf_raknet_server_guid, tvb, offset,
+ 8, ENC_NA);
+ offset += 8;
+
+ return offset;
+}
+
+static int
+raknet_dissect_unconnected_pong(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data _U_)
+{
+ proto_tree *sub_tree;
+ guint32 str_size;
+ gint offset;
+
+ sub_tree = init_raknet_offline_message(tvb, pinfo, tree, &offset);
+
+ proto_tree_add_item(sub_tree, hf_raknet_timestamp, tvb,
+ offset, 8, ENC_BIG_ENDIAN);
+ offset += 8;
+
+ proto_tree_add_item(sub_tree, hf_raknet_server_guid, tvb, offset,
+ 8, ENC_NA);
+ offset += 8;
+
+ proto_tree_add_item(sub_tree, hf_raknet_offline_message_data_id, tvb, offset,
+ 16, ENC_NA);
+ offset += 16;
+
+ /* raknet precedes strings with a short (2 bytes) holding string length. */
+ proto_tree_add_item_ret_uint(sub_tree, hf_raknet_0x1C_server_id_str_len, tvb,
+ offset, 2, ENC_BIG_ENDIAN, &str_size);
+ offset += 2;
+
+ proto_tree_add_item(sub_tree, hf_raknet_0x1C_server_id_str, tvb, offset,
+ str_size, ENC_NA|ENC_ASCII);
+ offset += str_size;
+
+ return offset;
+}
+
+static int
+raknet_dissect_connected_ping(tvbuff_t *tvb, packet_info *pinfo _U_,
+ proto_tree *tree, void* data _U_)
+{
+
+ gint offset = 1;
+
+ proto_tree_add_item(tree, hf_raknet_timestamp, tvb,
+ offset, 8, ENC_BIG_ENDIAN);
+ offset += 8;
+
+ return offset;
+}
+
+static int
+raknet_dissect_connected_pong(tvbuff_t *tvb, packet_info *pinfo _U_,
+ proto_tree *tree, void* data _U_)
+{
+ gint offset = 1;
+
+ proto_tree_add_item(tree, hf_raknet_timestamp, tvb,
+ offset, 8, ENC_BIG_ENDIAN);
+ offset += 8;
+
+ proto_tree_add_item(tree, hf_raknet_timestamp, tvb,
+ offset, 8, ENC_BIG_ENDIAN);
+ offset += 8;
+
+ return offset;
+}
+
+static int
+raknet_dissect_connection_request(tvbuff_t *tvb, packet_info *pinfo _U_,
+ proto_tree *tree, void* data _U_)
+{
+ gint offset = 1;
+ gboolean use_encryption;
+
+ proto_tree_add_item(tree, hf_raknet_client_guid, tvb, offset,
+ 8, ENC_NA);
+ offset += 8;
+
+ proto_tree_add_item(tree, hf_raknet_timestamp, tvb,
+ offset, 8, ENC_BIG_ENDIAN);
+ offset += 8;
+
+ use_encryption = tvb_get_guint8(tvb, offset) ? TRUE : FALSE;
+
+ proto_tree_add_item(tree, hf_raknet_use_encryption, tvb, offset,
+ 1, ENC_NA);
+ offset += 1;
+
+ if (use_encryption) {
+ gboolean use_client_key;
+
+ proto_tree_add_item(tree, hf_raknet_client_proof, tvb, offset,
+ 32, ENC_NA);
+ offset += 32;
+
+ use_client_key = tvb_get_guint8(tvb, offset) ? TRUE : FALSE;
+
+ proto_tree_add_item(tree, hf_raknet_use_client_key, tvb, offset,
+ 1, ENC_NA);
+ offset += 1;
+
+ if (use_client_key) {
+ proto_tree_add_item(tree, hf_raknet_client_identity, tvb, offset,
+ 160, ENC_NA);
+ offset += 160;
+ }
+ }
+
+ proto_tree_add_item(tree, hf_raknet_password, tvb, offset,
+ -1, ENC_NA);
+
+ return tvb_reported_length(tvb);
+}
+
+static int
+raknet_dissect_connection_request_accepted(tvbuff_t *tvb, packet_info *pinfo _U_,
+ proto_tree *tree, void* data _U_)
+{
+ gint offset = 1;
+ gint i;
+
+ raknet_dissect_system_address(
+ tree, hf_raknet_client_address, pinfo, tvb, &offset);
+
+ proto_tree_add_item(tree, hf_raknet_system_index, tvb, offset,
+ 2, ENC_BIG_ENDIAN);
+ offset += 2;
+
+ for (i = 0; i < RAKNET_NUMBER_OF_INTERNAL_IDS; i++) {
+ raknet_dissect_system_address(
+ tree, hf_raknet_internal_address, pinfo, tvb, &offset);
+ }
+
+ proto_tree_add_item(tree, hf_raknet_timestamp, tvb,
+ offset, 8, ENC_BIG_ENDIAN);
+ offset += 8;
+
+ proto_tree_add_item(tree, hf_raknet_timestamp, tvb,
+ offset, 8, ENC_BIG_ENDIAN);
+ offset += 8;
+
+ return offset;
+}
+
+static int
+raknet_dissect_new_incoming_connection(tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *tree, void* data _U_)
+{
+
+ gint offset = 1;
+ gint i;
+
+ raknet_dissect_system_address(
+ tree, hf_raknet_server_address, pinfo, tvb, &offset);
+
+ for (i = 0; i < RAKNET_NUMBER_OF_INTERNAL_IDS; i++) {
+ raknet_dissect_system_address(
+ tree, hf_raknet_internal_address, pinfo, tvb, &offset);
+ }
+
+ proto_tree_add_item(tree, hf_raknet_timestamp, tvb,
+ offset, 8, ENC_BIG_ENDIAN);
+ offset += 8;
+
+ proto_tree_add_item(tree, hf_raknet_timestamp, tvb,
+ offset, 8, ENC_BIG_ENDIAN);
+ offset += 8;
+
+
+ return offset;
+}
+
+/*
+ * Protocol definition and handlers.
+ */
+struct raknet_handler_entry {
+ value_string vs;
+ dissector_t dissector_fp;
+};
+
+static const struct raknet_handler_entry raknet_offline_message_handlers[] = {
+ /*
+ * Ref: ..RakNet/Source/MessageIdentifiers.h
+ * Ref: ..RakNet/Source/RakPeer.cpp (ProcessOfflineNetworkPacket)
+ */
+ { { 0x1, "Unconnected Ping" },
+ raknet_dissect_unconnected_ping },
+ { { 0x2, "Unconnected Ping Open Connections" },
+ raknet_dissect_unconnected_ping },
+ { { 0x5, "Open Connection Request 1" },
+ raknet_dissect_open_connection_request_1 },
+ { { 0x6, "Open Connection Reply 1" },
+ raknet_dissect_open_connection_reply_1 },
+ { { 0x7, "Open Connection Request 2" },
+ raknet_dissect_open_connection_request_2 },
+ { { 0x8, "Open Connection Reply 2" },
+ raknet_dissect_open_connection_reply_2 },
+ { { 0xD, "Out Of Band Internal" },
+ raknet_dissect_connection_failed },
+ { { 0x11, "Connection Attempt Failed" },
+ raknet_dissect_connection_failed },
+ { { 0x12, "Already Connected" },
+ raknet_dissect_connection_failed },
+ { { 0x14, "No Free Incoming Connections" },
+ raknet_dissect_connection_failed },
+ { { 0x17, "Connection Banned" },
+ raknet_dissect_connection_failed },
+ { { 0x19, "Incompatible Protocol Version" },
+ raknet_dissect_incompatible_protocol_version },
+ { { 0x1A, "IP Recently Connected" },
+ raknet_dissect_connection_failed },
+ { { 0x1C, "Unconnected Pong" },
+ raknet_dissect_unconnected_pong },
+};
+
+static const struct raknet_handler_entry raknet_system_message_handlers[] = {
+ /*
+ * Ref: ..RakNet/Source/MessageIdentifiers.h
+ * Ref: ..RakNet/Source/RakPeer.cpp (RakPeer::RunUpdateCycle)
+ */
+ { { 0x00, "Connected Ping" },
+ raknet_dissect_connected_ping },
+ { { 0x03, "Connected Pong" },
+ raknet_dissect_connected_pong },
+ { { 0x09, "Connection Request" },
+ raknet_dissect_connection_request },
+ { { 0x10, "Connection Request Accepted" },
+ raknet_dissect_connection_request_accepted },
+ { { 0x13, "New Incoming Connection" },
+ raknet_dissect_new_incoming_connection },
+};
+
+/*
+ * Look up table from message ID to name.
+ */
+static value_string raknet_offline_message_names[array_length(raknet_offline_message_handlers)+1];
+static value_string raknet_system_message_names[array_length(raknet_system_message_handlers)+1];
+
+static void
+raknet_init_message_names(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < array_length(raknet_offline_message_handlers); i++) {
+ raknet_offline_message_names[i].value = raknet_offline_message_handlers[i].vs.value;
+ raknet_offline_message_names[i].strptr = raknet_offline_message_handlers[i].vs.strptr;
+ }
+ raknet_offline_message_names[array_length(raknet_offline_message_handlers)].value = 0;
+ raknet_offline_message_names[array_length(raknet_offline_message_handlers)].strptr = NULL;
+
+ for (i = 0; i < array_length(raknet_system_message_handlers); i++) {
+ raknet_system_message_names[i].value = raknet_system_message_handlers[i].vs.value;
+ raknet_system_message_names[i].strptr = raknet_system_message_handlers[i].vs.strptr;
+ }
+ raknet_system_message_names[array_length(raknet_system_message_handlers)].value = 0;
+ raknet_system_message_names[array_length(raknet_system_message_handlers)].strptr = NULL;
+}
+
+/*
+ * Fill out the Info column and protocol subtree.
+ *
+ * Offset is updated for the caller.
+ */
+static proto_tree *
+init_raknet_offline_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint *offset)
+{
+ proto_tree *sub_tree;
+ proto_item *ti;
+ guint8 message_id;
+
+ *offset = 0;
+
+ /*
+ * Take buffer start 0 to end -1 as single raknet item.
+ */
+ ti = proto_tree_add_item(tree, proto_raknet, tvb, 0, -1, ENC_NA);
+ sub_tree = proto_item_add_subtree(ti, ett_raknet);
+
+ message_id = tvb_get_guint8(tvb, *offset);
+ proto_tree_add_item(sub_tree, hf_raknet_offline_message_id, tvb, *offset,
+ 1, ENC_BIG_ENDIAN);
+ *offset += 1;
+
+ col_add_str(pinfo->cinfo, COL_INFO,
+ val_to_str(message_id, raknet_offline_message_names, "Unknown offline message: %#x"));
+
+ /*
+ * Append description to the raknet item.
+ */
+ proto_item_append_text(ti, ", Offline message ID %#x", message_id);
+
+ return sub_tree;
+}
+
+static int
+raknet_dissect_ACK(tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *tree, void* data)
+{
+ gint offset = 0;
+ proto_tree *sub_tree;
+ guint32 count;
+ guint32 i;
+
+ if (*(gboolean*)data) {
+ col_add_str(pinfo->cinfo, COL_INFO, "ACK");
+ }
+ else {
+ col_add_str(pinfo->cinfo, COL_INFO, "NAK");
+ }
+
+ proto_tree_add_item_ret_uint(tree, hf_raknet_NACK_record_count, tvb,
+ offset, 2, ENC_BIG_ENDIAN, &count);
+ offset += 2;
+
+ for (i = 0; i < count; i++) {
+ proto_item *ti;
+ guint32 max;
+ guint32 min;
+
+ if (i == 0) {
+ col_append_str(pinfo->cinfo, COL_INFO, " ");
+ }
+ else {
+ col_append_str(pinfo->cinfo, COL_INFO, ", ");
+ }
+
+ if (tvb_get_guint8(tvb, offset)) { /* maxEqualToMin */
+ min = tvb_get_guint24(tvb, offset + 1, ENC_LITTLE_ENDIAN);
+
+ col_append_fstr(pinfo->cinfo, COL_INFO, "#%" PRIu32, min);
+
+ ti = proto_tree_add_string_format_value(tree, hf_raknet_packet_number_range, tvb,
+ offset, 1 + 3, "",
+ "%" PRIu32 " .. %" PRIu32,
+ min, min);
+ sub_tree = proto_item_add_subtree(ti, ett_raknet_packet_number_range);
+
+ proto_tree_add_item(sub_tree, hf_raknet_range_max_equal_to_min, tvb,
+ offset, 1, ENC_NA);
+ offset += 1;
+
+ proto_tree_add_item(sub_tree, hf_raknet_packet_number_min, tvb,
+ offset, 3, ENC_LITTLE_ENDIAN);
+ offset += 3;
+ }
+ else {
+ min = tvb_get_guint24(tvb, offset + 1 , ENC_LITTLE_ENDIAN);
+ max = tvb_get_guint24(tvb, offset + 1 + 3, ENC_LITTLE_ENDIAN);
+
+ col_append_fstr(pinfo->cinfo, COL_INFO,
+ "#%" PRIu32 "..%" PRIu32,
+ min, max);
+
+ ti = proto_tree_add_string_format_value(tree, hf_raknet_packet_number_range, tvb,
+ offset, 1 + 3 + 3, "",
+ "%" PRIu32 " .. %" PRIu32, min, max);
+ sub_tree = proto_item_add_subtree(ti, ett_raknet_packet_number_range);
+
+ proto_tree_add_item(sub_tree, hf_raknet_range_max_equal_to_min, tvb,
+ offset, 1, ENC_NA);
+ offset += 1;
+
+ proto_tree_add_item(sub_tree, hf_raknet_packet_number_min, tvb,
+ offset, 3, ENC_LITTLE_ENDIAN);
+ offset += 3;
+
+ proto_tree_add_item(sub_tree, hf_raknet_packet_number_max, tvb,
+ offset, 3, ENC_LITTLE_ENDIAN);
+ offset += 3;
+ }
+ }
+
+ return tvb_captured_length(tvb);
+}
+
+static int
+raknet_dissect_common_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *raknet_tree, void *data)
+{
+ gint offset = 0;
+ gboolean *has_multiple_messages;
+ proto_item *ti;
+ proto_item *raknet_ti;
+ proto_item *msg_ti;
+ proto_tree *msg_tree;
+ guint64 msg_flags;
+ guint32 payload_bits;
+ guint32 payload_octets;
+ raknet_reliability_t reliability;
+ gboolean has_split_packet;
+ guint8 message_id;
+ gint message_size;
+ proto_tree *payload_tree;
+ tvbuff_t* next_tvb;
+ gboolean next_tvb_is_subset;
+ dissector_handle_t next_dissector;
+ gint dissected;
+ heur_dtbl_entry_t *hdtbl_entry;
+ static int * const flag_flds[] = {
+ &hf_raknet_message_reliability,
+ &hf_raknet_message_has_split_packet,
+ NULL
+ };
+
+ has_multiple_messages = (gboolean*)data;
+ raknet_ti = proto_tree_get_parent(raknet_tree);
+
+ msg_ti = proto_tree_add_item(raknet_tree, hf_raknet_message, tvb, offset, -1, ENC_NA);
+ msg_tree = proto_item_add_subtree(msg_ti, ett_raknet_message);
+ proto_item_append_text(msg_ti, ", ");
+
+ proto_tree_add_bitmask_ret_uint64(msg_tree, tvb, offset, hf_raknet_message_flags,
+ ett_raknet_message_flags, flag_flds, ENC_NA, &msg_flags);
+ offset += 1;
+
+ ti = proto_tree_add_item_ret_uint(msg_tree, hf_raknet_payload_length, tvb,
+ offset, 2, ENC_BIG_ENDIAN, &payload_bits);
+ offset += 2;
+ payload_octets = payload_bits / 8 + (payload_bits % 8 > 0); /* ceil(bits / 8) */
+ proto_item_append_text(ti, " bits (%" PRIu32 " octets)", payload_octets);
+
+ reliability = (raknet_reliability_t)((msg_flags >> 5) & 0x07);
+ has_split_packet = (msg_flags >> 4) & 0x01 ? TRUE : FALSE;
+
+ if (reliability == RAKNET_RELIABLE ||
+ reliability == RAKNET_RELIABLE_SEQUENCED ||
+ reliability == RAKNET_RELIABLE_ORDERED ) {
+
+ proto_tree_add_item(msg_tree, hf_raknet_reliable_message_number, tvb,
+ offset, 3, ENC_LITTLE_ENDIAN);
+ offset += 3;
+ }
+
+ if (reliability == RAKNET_UNRELIABLE_SEQUENCED ||
+ reliability == RAKNET_RELIABLE_SEQUENCED) {
+
+ proto_tree_add_item(msg_tree, hf_raknet_message_sequencing_index, tvb,
+ offset, 3, ENC_LITTLE_ENDIAN);
+ offset += 3;
+ }
+
+ if (reliability == RAKNET_UNRELIABLE_SEQUENCED ||
+ reliability == RAKNET_RELIABLE_SEQUENCED ||
+ reliability == RAKNET_RELIABLE_ORDERED) {
+
+ proto_tree_add_item(msg_tree, hf_raknet_message_ordering_index, tvb,
+ offset, 3, ENC_LITTLE_ENDIAN);
+ offset += 3;
+
+ proto_tree_add_item(msg_tree, hf_raknet_message_ordering_channel, tvb,
+ offset, 1, ENC_NA);
+ offset += 1;
+ }
+
+ if (has_split_packet) {
+ gboolean save_fragmented;
+ guint32 split_packet_count;
+ guint32 split_packet_id;
+ guint32 split_packet_index;
+ fragment_head *frag_msg;
+
+
+ proto_tree_add_item_ret_uint(msg_tree, hf_raknet_split_packet_count, tvb,
+ offset, 4, ENC_BIG_ENDIAN, &split_packet_count);
+ offset += 4;
+
+ proto_tree_add_item_ret_uint(msg_tree, hf_raknet_split_packet_id, tvb,
+ offset, 2, ENC_BIG_ENDIAN, &split_packet_id);
+ offset += 2;
+
+ proto_tree_add_item_ret_uint(msg_tree, hf_raknet_split_packet_index, tvb,
+ offset, 4, ENC_BIG_ENDIAN, &split_packet_index);
+ offset += 4;
+
+ /*
+ * Reassemble the fragmented packet.
+ */
+ save_fragmented = pinfo->fragmented;
+ pinfo->fragmented = TRUE;
+
+ frag_msg =
+ fragment_add_seq_check(&raknet_reassembly_table,
+ tvb, offset, pinfo,
+ split_packet_id,
+ NULL,
+ split_packet_index,
+ payload_octets,
+ split_packet_index != split_packet_count - 1);
+
+ next_tvb =
+ process_reassembled_data(tvb, offset, pinfo, "Reassembled packet",
+ frag_msg, &raknet_frag_items, NULL, msg_tree);
+
+ pinfo->fragmented = save_fragmented;
+
+ if (next_tvb) {
+ /*
+ * Reassembly done. Dissect the message as normal.
+ */
+ wmem_strbuf_t *strbuf;
+
+ strbuf = wmem_strbuf_new(pinfo->pool, "");
+ wmem_strbuf_append_printf(strbuf,
+ "{Message fragment %" PRIu32 "/%" PRIu32 "; Reassembled} ",
+ split_packet_index + 1, split_packet_count);
+
+ proto_item_append_text(msg_ti, "%s", wmem_strbuf_get_str(strbuf));
+ col_add_str(pinfo->cinfo, COL_INFO, wmem_strbuf_get_str(strbuf));
+ col_set_fence(pinfo->cinfo, COL_INFO);
+
+ next_tvb_is_subset = FALSE;
+ }
+ else {
+ wmem_strbuf_t *strbuf;
+
+ strbuf = wmem_strbuf_new(pinfo->pool, "");
+ wmem_strbuf_append_printf(strbuf,
+ "{Message fragment %" PRIu32 "/%" PRIu32 "}",
+ split_packet_index + 1, split_packet_count);
+
+ proto_item_append_text(msg_ti, "%s", wmem_strbuf_get_str(strbuf));
+ col_add_str(pinfo->cinfo, COL_INFO, wmem_strbuf_get_str(strbuf));
+
+ ti = proto_tree_add_item(msg_tree, hf_raknet_split_packet, tvb, offset,
+ payload_octets, ENC_NA);
+ proto_item_append_text(ti, " (%u octets)", payload_octets);
+ }
+ }
+ else {
+ next_tvb = tvb_new_subset_length(tvb, offset, payload_octets);
+ next_tvb_is_subset = TRUE;
+ }
+
+ /*
+ * At this point we can finally check if the packet has multiple
+ * messages.
+ */
+ if (! *has_multiple_messages) {
+ *has_multiple_messages =
+ tvb_reported_length_remaining(tvb, offset) > (gint)payload_octets
+ ? TRUE : FALSE;
+ }
+
+ /*
+ * And we finally have the actual size of message.
+ */
+ message_size = offset + payload_octets;
+
+ if (!next_tvb) {
+ /*
+ * It was an incomplete message fragment.
+ */
+ proto_item_set_len(msg_ti, message_size);
+ if (raknet_ti) {
+ proto_item_set_len(raknet_ti, proto_item_get_len(raknet_ti) + message_size);
+ }
+ return message_size;
+ }
+
+ message_id = tvb_get_guint8(next_tvb, 0);
+
+ /*
+ * Now we want to dissect this message. First we search for a
+ * dissector from our system message dissector table.
+ */
+ next_dissector =
+ dissector_get_uint_handle(raknet_system_message_dissectors, message_id);
+
+ if (next_dissector) {
+ /*
+ * We have a subdissector. The protocol of the message is
+ * still RakNet (e.g. 0x09 ID_CONNECTION_REQUEST) so we always
+ * insert it into our tree.
+ */
+ ti = proto_tree_add_item(msg_tree, hf_raknet_system_message, next_tvb, 0, -1, ENC_NA);
+ payload_tree = proto_item_add_subtree(ti, ett_raknet_system_message);
+
+ proto_item_append_text(ti, " (%s)",
+ val_to_str(message_id, raknet_system_message_names, "Unknown ID: %#x"));
+
+ proto_item_append_text(msg_ti, "ID %#x (%s)", message_id,
+ val_to_str_const(message_id, raknet_system_message_names, "Unknown"));
+
+ col_add_str(pinfo->cinfo, COL_INFO,
+ val_to_str(message_id, raknet_system_message_names, "Unknown system message ID: %#x"));
+
+ proto_tree_add_item(payload_tree, hf_raknet_system_message_id,
+ next_tvb, 0, 1, ENC_NA);
+
+ dissected =
+ call_dissector_only(next_dissector, next_tvb, pinfo, payload_tree, data);
+
+ proto_item_set_len(msg_ti, message_size);
+ if (raknet_ti) {
+ proto_item_set_len(raknet_ti, proto_item_get_len(raknet_ti) + message_size);
+ }
+
+ if (dissected >= 0) {
+ return message_size;
+ }
+ else {
+ return dissected;
+ }
+ }
+
+ /*
+ * It seems not to be a system message so use a dissector set for
+ * this conversation if any.
+ */
+ next_dissector =
+ raknet_get_session_state(pinfo)->subdissector;
+
+ /*
+ * And of course we don't know the name of message.
+ */
+ proto_item_append_text(msg_ti, "ID %#x", message_id);
+
+ /*
+ * The message belongs to a sub-protocol of RakNet so let it place
+ * its own protocol layer, but only if the packet has only one
+ * message.
+ */
+ if (*has_multiple_messages) {
+ payload_tree = msg_tree;
+ }
+ else {
+ payload_tree = proto_tree_get_root(raknet_tree);
+ }
+
+ if (next_dissector) {
+ dissected =
+ call_dissector_only(next_dissector, next_tvb, pinfo, payload_tree, data);
+
+ if (dissected > 0) {
+ goto FIX_UP_AND_RETURN;
+ }
+ else {
+ expert_add_info(pinfo, msg_tree, &ei_raknet_subdissector_failed);
+ }
+ }
+
+ /*
+ * No dissectors set for this conversation. Look up a dissector
+ * from the port table.
+ */
+ next_dissector =
+ dissector_get_uint_handle(raknet_port_dissectors, pinfo->match_uint);
+
+ if (next_dissector) {
+ dissected =
+ call_dissector_only(next_dissector, next_tvb, pinfo, payload_tree, data);
+
+ if (dissected > 0) {
+ goto FIX_UP_AND_RETURN;
+ }
+ else {
+ expert_add_info(pinfo, msg_tree, &ei_raknet_subdissector_failed);
+ }
+ }
+
+ /*
+ * We don't have a subdissector or we have one but id didn't
+ * dissect the message. Try heuristic subdissectors.
+ */
+ dissected =
+ dissector_try_heuristic(raknet_heur_subdissectors, next_tvb, pinfo, payload_tree,
+ &hdtbl_entry, data);
+ if (!dissected) {
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown message ID: %#x", message_id);
+
+ ti = proto_tree_add_expert(msg_tree, pinfo, &ei_raknet_unknown_message_id, next_tvb,
+ 0, 1);
+ proto_item_append_text(ti, " %#x", message_id);
+ }
+
+ FIX_UP_AND_RETURN:
+ /*
+ * Fix up the top-level item so that it doesn't include stuff for
+ * sub-protocols. In order to do this there must not be multiple
+ * messages in the packet, and the message must have been
+ * reassembled from fragments.
+ */
+ if (!*has_multiple_messages && next_tvb_is_subset) {
+ proto_item_set_len(msg_ti, message_size - payload_octets);
+ if (raknet_ti) {
+ proto_item_set_len(raknet_ti, proto_item_get_len(raknet_ti) + message_size - payload_octets);
+ }
+ }
+ else {
+ proto_item_set_len(msg_ti, message_size);
+ if (raknet_ti) {
+ proto_item_set_len(raknet_ti, proto_item_get_len(raknet_ti) + message_size);
+ }
+ }
+ return message_size;
+}
+
+static int
+raknet_dissect_connected_message(tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *root_tree, void* data _U_)
+{
+ raknet_session_state_t* state;
+ proto_item *ti;
+ proto_tree *raknet_tree;
+ gint item_size;
+ gint offset = 0;
+ guint8 msg_type;
+
+ state = raknet_get_session_state(pinfo);
+ if (state->use_encryption) {
+ /*
+ * RakNet uses ChaCha stream cipher to encrypt messages, which
+ * is currently not supported by this dissector.
+ */
+ col_add_str(pinfo->cinfo, COL_INFO, "Encrypted message");
+
+ item_size = tvb_reported_length_remaining(tvb, offset);
+ ti = proto_tree_add_expert(root_tree, pinfo, &ei_raknet_encrypted_message, tvb,
+ offset, item_size);
+ proto_item_append_text(ti, " (%d octets)", item_size);
+ return tvb_captured_length(tvb);
+ }
+
+ msg_type = tvb_get_guint8(tvb, offset);
+
+ if (!(msg_type & (1 << 7))) { /* !isValid */
+ /*
+ * No suitable dissector was registered for this offline
+ * message.
+ */
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown offline message ID: %#x", msg_type);
+ ti = proto_tree_add_expert(root_tree, pinfo, &ei_raknet_unknown_message_id, tvb,
+ 0, 1);
+ proto_item_append_text(ti, " %#x", msg_type);
+ return tvb_captured_length(tvb);
+ }
+ else if (msg_type & (1 << 6)) { /* isACK */
+ static int * const ack_flds[] = {
+ &hf_raknet_packet_is_for_connected,
+ &hf_raknet_packet_is_ACK,
+ &hf_raknet_packet_has_B_and_AS,
+ NULL
+ };
+
+ ti = proto_tree_add_item(root_tree, proto_raknet, tvb, 0, -1, ENC_NA);
+ proto_item_append_text(ti, ", ACK");
+ raknet_tree = proto_item_add_subtree(ti, ett_raknet);
+
+ proto_tree_add_bitmask(raknet_tree, tvb, offset, hf_raknet_packet_type,
+ ett_raknet_packet_type, ack_flds, ENC_NA);
+ offset += 1;
+
+ if (msg_type & (1 << 5)) { /* hasBAndAS */
+ proto_tree_add_item(raknet_tree, hf_raknet_AS, tvb, offset,
+ 4, ENC_BIG_ENDIAN);
+ offset += 4;
+ }
+
+ if (raknet_tree) {
+ gboolean is_ACK = TRUE;
+ return raknet_dissect_ACK(tvb_new_subset_remaining(tvb, offset),
+ pinfo, raknet_tree, &is_ACK);
+ }
+ else {
+ return tvb_captured_length(tvb);
+ }
+ }
+ else if (msg_type & (1 << 5)) { /* isNAK */
+ static int * const nak_flds[] = {
+ &hf_raknet_packet_is_for_connected,
+ &hf_raknet_packet_is_ACK,
+ &hf_raknet_packet_is_NAK,
+ NULL
+ };
+
+ ti = proto_tree_add_item(root_tree, proto_raknet, tvb, 0, -1, ENC_NA);
+ proto_item_append_text(ti, ", NAK");
+ raknet_tree = proto_item_add_subtree(ti, ett_raknet);
+
+ proto_tree_add_bitmask(raknet_tree, tvb, offset, hf_raknet_packet_type,
+ ett_raknet_packet_type, nak_flds, ENC_NA);
+ offset += 1;
+
+ if (raknet_tree) {
+ gboolean is_ACK = FALSE;
+ return raknet_dissect_ACK(tvb_new_subset_remaining(tvb, offset),
+ pinfo, raknet_tree, &is_ACK);
+ }
+ else {
+ return tvb_captured_length(tvb);
+ }
+ }
+ else {
+ /*
+ * This is the trickiest part as it's neither ACK nor NAK. The
+ * length of its RakNet header varies, and its payload can
+ * even be fragmented so we might have to reassemble them.
+ */
+ guint32 packet_number;
+ gboolean has_multiple_messages = FALSE;
+ static int * const common_flds[] = {
+ &hf_raknet_packet_is_for_connected,
+ &hf_raknet_packet_is_ACK,
+ &hf_raknet_packet_is_NAK,
+ &hf_raknet_packet_is_pair,
+ &hf_raknet_packet_is_continuous_send,
+ &hf_raknet_packet_needs_B_and_AS,
+ NULL
+ };
+
+ ti = proto_tree_add_item(root_tree, proto_raknet, tvb, 0, 0, ENC_NA);
+ raknet_tree = proto_item_add_subtree(ti, ett_raknet);
+
+ proto_tree_add_bitmask(raknet_tree, tvb, offset, hf_raknet_packet_type,
+ ett_raknet_packet_type, common_flds, ENC_NA);
+ offset += 1;
+
+ proto_tree_add_item_ret_uint(raknet_tree, hf_raknet_packet_number, tvb,
+ offset, 3, ENC_LITTLE_ENDIAN, &packet_number);
+ offset += 3;
+
+ proto_item_append_text(ti, ", Message #%" PRIu32, packet_number);
+ col_add_fstr(pinfo->cinfo, COL_INFO, "#%" PRIu32 ": ", packet_number);
+ col_set_fence(pinfo->cinfo, COL_INFO);
+
+ /*
+ * Set the length of the top-level item to the size of packet
+ * header as we don't know the correct size yet. The common
+ * message dissector will later resize it.
+ */
+ proto_item_set_len(ti, offset);
+
+ while (TRUE) {
+ int dissected;
+
+ dissected = raknet_dissect_common_message(tvb_new_subset_remaining(tvb, offset), pinfo,
+ raknet_tree, &has_multiple_messages);
+ if (dissected >= 0) {
+ offset += dissected;
+
+ if (tvb_reported_length_remaining(tvb, offset) > 0) {
+ /*
+ * More messages are in the packet.
+ */
+ col_append_fstr(pinfo->cinfo, COL_INFO, ", ");
+ col_set_fence(pinfo->cinfo, COL_INFO);
+ continue;
+ }
+ else {
+ /*
+ * It's the end of packet.
+ */
+ break;
+ }
+ }
+ else {
+ return dissected;
+ }
+ }
+
+ return tvb_captured_length(tvb);
+ }
+}
+
+/*
+ * Decode the tvb buffer.
+ *
+ * RakNet is just a dissector. It is invoked by protocols whose applications
+ * are built using the RakNet libs.
+ */
+static int
+dissect_raknet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
+{
+ guint8 message_id;
+ gint dissected;
+
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "RakNet");
+ col_clear(pinfo->cinfo, COL_INFO);
+
+ message_id = tvb_get_guint8(tvb, 0);
+
+ dissected = dissector_try_uint_new(raknet_offline_message_dissectors, message_id, tvb,
+ pinfo, tree, TRUE, data);
+ if (!dissected) {
+ raknet_dissect_connected_message(tvb, pinfo, tree, data);
+ }
+
+ return tvb_captured_length(tvb);
+}
+
+/*
+ * Applications using RakNet do not always use a fixed port, but since
+ * every RakNet sessions start with offline messages we can do
+ * heuristics to detect such sessions.
+ */
+static gboolean
+test_raknet_heur(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, void* data _U_)
+{
+ if (tvb_memeql(tvb, 1 + 8, RAKNET_OFFLINE_MESSAGE_DATA_ID, sizeof(RAKNET_OFFLINE_MESSAGE_DATA_ID)) == 0) {
+ /* ID_UNCONNECTED_PING */
+ return TRUE;
+ }
+ else if (tvb_memeql(tvb, 1, RAKNET_OFFLINE_MESSAGE_DATA_ID, sizeof(RAKNET_OFFLINE_MESSAGE_DATA_ID)) == 0) {
+ /* ID_OPEN_CONNECTION_REQUEST_1 */
+ return TRUE;
+ }
+ else if (tvb_memeql(tvb, 1 + 8 + 8, RAKNET_OFFLINE_MESSAGE_DATA_ID, sizeof(RAKNET_OFFLINE_MESSAGE_DATA_ID)) == 0) {
+ /* ID_UNCONNECTED_PONG */
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+static gboolean
+dissect_raknet_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
+{
+ if (test_raknet_heur(tvb, pinfo, tree, data)) {
+ conversation_t* conversation;
+
+ conversation = find_or_create_conversation(pinfo);
+ conversation_set_dissector(conversation, raknet_handle);
+
+ return call_dissector_only(raknet_handle, tvb, pinfo, tree, data) > 0;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+void
+proto_register_raknet(void)
+{
+ static hf_register_info hf[] = {
+ /*
+ * Offline Message ID field.
+ */
+ { &hf_raknet_offline_message_id,
+ { "RakNet Offline Message ID", "raknet.offline.message.id",
+ FT_UINT8, BASE_HEX,
+ VALS(raknet_offline_message_names), 0x0,
+ NULL, HFILL }
+ },
+ /*
+ * General fields (fields in >1 packet).
+ */
+ { &hf_raknet_client_guid,
+ { "RakNet Client GUID", "raknet.client.guid",
+ FT_BYTES, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_timestamp,
+ { "RakNet Time since start (ms)", "raknet.timestamp",
+ FT_UINT64, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_offline_message_data_id,
+ { "RakNet Offline message data ID", "raknet.offline_message.data_id",
+ FT_BYTES, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_mtu_size,
+ { "RakNet MTU size", "raknet.MTU",
+ FT_UINT16, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_raknet_proto_ver,
+ { "RakNet RakNet protocol version", "raknet.proto_ver",
+ FT_UINT8, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_server_guid,
+ { "RakNet Server GUID", "raknet.server_id",
+ FT_BYTES, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_ip_version,
+ { "RakNet IP Version", "raknet.ip.version",
+ FT_UINT8, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_ipv4_address,
+ { "RakNet IPv4 Address", "raknet.ip.v4_address",
+ FT_IPv4, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_ipv6_address,
+ { "RakNet IPv6 Address", "raknet.ip.v6_address",
+ FT_IPv6, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_port,
+ { "RakNet Port", "raknet.port",
+ FT_UINT16, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ /*
+ * Packet ID 0x05
+ */
+ { &hf_raknet_null_padding,
+ { "RakNet Null padding", "raknet.null_padding",
+ FT_NONE, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ /*
+ * Packet ID 0x06
+ */
+ { &hf_raknet_use_encryption,
+ { "RakNet Use encryption", "raknet.use_encryption",
+ FT_BOOLEAN, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_server_public_key,
+ { "RakNet Server public key", "raknet.server.public_key",
+ FT_BYTES, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ /*
+ * Packet ID 0x07
+ */
+ { &hf_raknet_cookie,
+ { "RakNet cookie", "raknet.cookie",
+ FT_UINT32, BASE_HEX,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_client_wrote_challenge,
+ { "RakNet Client wrote challenge", "raknet.client.wrote_challenge",
+ FT_BOOLEAN, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_client_challenge,
+ { "RakNet Client challenge", "raknet.client.challenge",
+ FT_BYTES, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_client_address,
+ { "RakNet Client address", "raknet.client.address",
+ FT_STRING, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_server_address,
+ { "RakNet Server address", "raknet.server.address",
+ FT_STRING, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_server_answer,
+ { "RakNet Server answer", "raknet.server.answer",
+ FT_BYTES, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ /*
+ * Packet ID 0x1C
+ */
+ { &hf_raknet_0x1C_server_id_str_len,
+ { "RakNet Server ID string len", "raknet.server_id_str_len",
+ FT_UINT16, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_0x1C_server_id_str,
+ { "RakNet Server ID string", "raknet.server_id_str",
+ FT_STRING, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_packet_type,
+ { "RakNet Packet type", "raknet.packet.type",
+ FT_UINT8, BASE_HEX,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_packet_is_for_connected,
+ { "is for connected peer", "raknet.packet.is_for_connected",
+ FT_BOOLEAN, 8,
+ NULL, 0x80,
+ NULL, HFILL }
+ },
+ { &hf_raknet_packet_is_ACK,
+ { "is ACK", "raknet.packet.is_ACK",
+ FT_BOOLEAN, 8,
+ NULL, 0x40,
+ NULL, HFILL }
+ },
+ { &hf_raknet_packet_has_B_and_AS,
+ { "has B and AS", "raknet.packet.has_B_and_AS",
+ FT_BOOLEAN, 8,
+ NULL, 0x20,
+ NULL, HFILL }
+ },
+ { &hf_raknet_packet_is_NAK,
+ { "is NAK", "raknet.packet.is_NAK",
+ FT_BOOLEAN, 8,
+ NULL, 0x20,
+ NULL, HFILL }
+ },
+ { &hf_raknet_packet_is_pair,
+ { "is pair", "raknet.packet.is_pair",
+ FT_BOOLEAN, 8,
+ NULL, 0x10,
+ NULL, HFILL }
+ },
+ { &hf_raknet_packet_is_continuous_send,
+ { "is continuous send", "raknet.packet.is_continuous_send",
+ FT_BOOLEAN, 8,
+ NULL, 0x8,
+ NULL, HFILL }
+ },
+ { &hf_raknet_packet_needs_B_and_AS,
+ { "needs B and AS", "raknet.packet.needs_B_and_AS",
+ FT_BOOLEAN, 8,
+ NULL, 0x4,
+ NULL, HFILL }
+ },
+ { &hf_raknet_AS,
+ { "RakNet Data arrival rate", "raknet.AS",
+ FT_FLOAT, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_NACK_record_count,
+ { "RakNet ACK/NAK record count", "raknet.NACK.record_count",
+ FT_UINT16, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_packet_number_range,
+ { "RakNet Packet sequence number range", "raknet.range.packet_number",
+ FT_STRING, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_range_max_equal_to_min,
+ { "RakNet Range max equals to min", "raknet.range.max_equals_to_min",
+ FT_BOOLEAN, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_packet_number_min,
+ { "RakNet Packet sequence number min", "raknet.range.packet_number.min",
+ FT_UINT24, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_packet_number_max,
+ { "RakNet Packet sequence number max", "raknet.range.packet_number.max",
+ FT_UINT24, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_packet_number,
+ { "RakNet Packet sequence number", "raknet.packet_number",
+ FT_UINT24, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_message,
+ { "RakNet Message", "raknet.message",
+ FT_NONE, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_message_flags,
+ { "RakNet Message flags", "raknet.message.flags",
+ FT_UINT8, BASE_HEX,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_message_reliability,
+ { "reliability", "raknet.message.reliability",
+ FT_UINT8, BASE_DEC,
+ VALS(raknet_reliability), 0xE0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_message_has_split_packet,
+ { "has split packet", "raknet.message.has_split_packet",
+ FT_BOOLEAN, 8,
+ NULL, 0x10,
+ NULL, HFILL }
+ },
+ { &hf_raknet_payload_length,
+ { "RakNet Payload length", "raknet.payload.length",
+ FT_UINT16, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_reliable_message_number,
+ { "RakNet Reliable message number", "raknet.reliable.number",
+ FT_UINT24, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_message_sequencing_index,
+ { "RakNet Message sequencing index", "raknet.sequencing.index",
+ FT_UINT24, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_message_ordering_index,
+ { "RakNet Message ordering index", "raknet.ordering.index",
+ FT_UINT24, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_message_ordering_channel,
+ { "RakNet Message ordering channel", "raknet.ordering.channel",
+ FT_UINT8, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_split_packet_count,
+ { "RakNet Split packet count", "raknet.split.count",
+ FT_UINT32, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_split_packet_id,
+ { "RakNet Split packet ID", "raknet.split.id",
+ FT_UINT16, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_split_packet_index,
+ { "RakNet Split packet index", "raknet.split.index",
+ FT_UINT32, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_split_packet,
+ { "RakNet Split packet", "raknet.split.packet",
+ FT_NONE, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_system_message,
+ { "RakNet System message", "raknet.system.message",
+ FT_NONE, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_system_message_id,
+ { "RakNet System Message ID", "raknet.system.message.id",
+ FT_UINT8, BASE_HEX,
+ VALS(raknet_system_message_names), 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_client_proof,
+ { "RakNet Client proof of key", "raknet.client.proof",
+ FT_BYTES, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_use_client_key,
+ { "RakNet Use client key", "raknet.use_client_key",
+ FT_BOOLEAN, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_client_identity,
+ { "RakNet Client identity", "raknet.client.identity",
+ FT_BYTES, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_password,
+ { "RakNet Password", "raknet.password",
+ FT_BYTES, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_system_index,
+ { "RakNet System index", "raknet.system.index",
+ FT_UINT16, BASE_DEC,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_raknet_internal_address,
+ { "RakNet Internal address", "raknet.internal.address",
+ FT_STRING, BASE_NONE,
+ NULL, 0x0,
+ NULL, HFILL }
+ },
+ /*
+ * Fragmented packets
+ */
+ { &hf_raknet_fragment,
+ { "Message fragment", "raknet.fragment",
+ FT_FRAMENUM, BASE_NONE,
+ NULL, 0x00,
+ NULL, HFILL }
+ },
+ { &hf_raknet_fragment_count,
+ { "Message fragment count", "raknet.fragment.count",
+ FT_UINT32, BASE_DEC,
+ NULL, 0x00,
+ NULL, HFILL }
+ },
+ { &hf_raknet_fragment_error,
+ { "Message defragmentation error", "raknet.fragment.error",
+ FT_FRAMENUM, BASE_NONE,
+ NULL, 0x00,
+ NULL, HFILL }
+ },
+ { &hf_raknet_fragment_multiple_tails,
+ { "Message has multiple tail fragments", "raknet.fragment.multiple_tails",
+ FT_BOOLEAN, BASE_NONE,
+ NULL, 0x00,
+ NULL, HFILL }
+ },
+ { &hf_raknet_fragment_overlap,
+ { "Message fragment overlap", "raknet.fragment.overlap",
+ FT_BOOLEAN, BASE_NONE,
+ NULL, 0x00,
+ NULL, HFILL }
+ },
+ { &hf_raknet_fragment_overlap_conflicts,
+ { "Message fragment overlapping with conflicting data", "raknet.fragment.overlap.conflicts",
+ FT_BOOLEAN, BASE_NONE,
+ NULL, 0x00,
+ NULL, HFILL }
+ },
+ { &hf_raknet_fragment_too_long_fragment,
+ { "Message fragment too long", "raknet.fragment.too_long",
+ FT_BOOLEAN, BASE_NONE,
+ NULL, 0x00,
+ NULL, HFILL }
+ },
+ { &hf_raknet_fragments,
+ { "Message fragments", "raknet.fragments",
+ FT_NONE, BASE_NONE,
+ NULL, 0x00,
+ NULL, HFILL }
+ },
+ { &hf_raknet_reassembled_in,
+ { "Reassembled message in frame", "raknet.reassembled.in",
+ FT_FRAMENUM, BASE_NONE,
+ NULL, 0x00,
+ NULL, HFILL }
+ },
+ { &hf_raknet_reassembled_length,
+ { "Reassembled message length", "raknet.reassembled.length",
+ FT_UINT32, BASE_DEC,
+ NULL, 0x00,
+ NULL, HFILL }
+ },
+ };
+
+ /*
+ * Setup protocol subtree array
+ */
+ static gint *ett[] = {
+ &ett_raknet,
+ &ett_raknet_system_address,
+ &ett_raknet_packet_type,
+ &ett_raknet_packet_number_range,
+ &ett_raknet_message,
+ &ett_raknet_message_flags,
+ &ett_raknet_system_message,
+ &ett_raknet_fragment,
+ &ett_raknet_fragments,
+ };
+
+ /*
+ * Set up expert info.
+ */
+ static ei_register_info ei[] = {
+ { &ei_raknet_unknown_message_id,
+ { "raknet.unknown.id", PI_UNDECODED, PI_WARN,
+ "RakNet unknown message ID",
+ EXPFILL }
+ },
+ { &ei_raknet_encrypted_message,
+ { "raknet.encrypted", PI_DECRYPTION, PI_NOTE,
+ "RakNet encrypted message",
+ EXPFILL }
+ },
+ { &ei_raknet_subdissector_failed,
+ { "raknet.subdissector.failed", PI_MALFORMED, PI_NOTE,
+ "RakNet message subdissector failed, trying the next candidate or heuristics",
+ EXPFILL }
+ },
+ { &ei_raknet_ip_ver_invalid,
+ { "raknet.ip_version.invalid", PI_PROTOCOL, PI_WARN,
+ "Invalid IP version",
+ EXPFILL }
+ }
+ };
+ expert_module_t *expert_raknet;
+
+ /*
+ * Init data structs.
+ */
+ raknet_init_message_names();
+
+ /*
+ * Register the protocol with Wireshark.
+ */
+ proto_raknet = proto_register_protocol (
+ "RakNet game networking protocol", /* name */
+ "RakNet", /* short name */
+ "raknet" /* abbrev */
+ );
+
+ /*
+ * Register expert support.
+ */
+ expert_raknet = expert_register_protocol(proto_raknet);
+ expert_register_field_array(expert_raknet, ei, array_length(ei));
+
+ /*
+ * Register detailed dissection arrays.
+ */
+ proto_register_field_array(proto_raknet, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ /*
+ * Register reassembly table.
+ */
+ reassembly_table_register(&raknet_reassembly_table,
+ &addresses_ports_reassembly_table_functions);
+
+ /*
+ * For internal use only
+ */
+ raknet_handle =
+ register_dissector("raknet", dissect_raknet, proto_raknet);
+
+ raknet_offline_message_dissectors =
+ register_dissector_table("raknet.offline.message.id", "RakNet offline messages",
+ proto_raknet, FT_UINT8, BASE_HEX);
+
+ raknet_system_message_dissectors =
+ register_dissector_table("raknet.system.message.id", "RakNet system messages",
+ proto_raknet, FT_UINT8, BASE_HEX);
+
+ /*
+ * External protocols may register their port to this table via
+ * "raknet_add_udp_dissector()".
+ */
+ raknet_port_dissectors =
+ register_dissector_table("raknet.port", "Port for protocols on top of RakNet",
+ proto_raknet, FT_UINT16, BASE_DEC);
+
+ /*
+ * ...and their heuristic dissector to this table.
+ */
+ raknet_heur_subdissectors =
+ register_heur_dissector_list("raknet", proto_raknet);
+}
+
+void
+proto_reg_handoff_raknet(void)
+{
+ dissector_handle_t raknet_handle_tmp;
+ unsigned int i;
+
+ for (i = 0; i < array_length(raknet_offline_message_handlers); i++) {
+ raknet_handle_tmp =
+ create_dissector_handle(raknet_offline_message_handlers[i].dissector_fp,
+ proto_raknet);
+ dissector_add_uint("raknet.offline.message.id", raknet_offline_message_handlers[i].vs.value,
+ raknet_handle_tmp);
+ }
+
+ for (i = 0; i < array_length(raknet_system_message_handlers); i++) {
+ raknet_handle_tmp =
+ create_dissector_handle(raknet_system_message_handlers[i].dissector_fp,
+ proto_raknet);
+ dissector_add_uint("raknet.system.message.id", raknet_system_message_handlers[i].vs.value,
+ raknet_handle_tmp);
+ }
+
+ heur_dissector_add("udp", dissect_raknet_heur,
+ "RakNet over UDP", "raknet_udp", proto_raknet, HEURISTIC_ENABLE);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */