/* * packet-raknet.c * * Routines for RakNet protocol packet disassembly. * * Ref: https://github.com/OculusVR/RakNet * * Nick Carter * Copyright 2014 Nick Carter * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #include #include #include #include "packet-raknet.h" /* * RakNet Protocol Constants. */ 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 = (raknet_session_state_t*)wmem_alloc(wmem_file_scope(), sizeof(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(wmem_packet_scope(), &addr); proto_tree_add_ipv4(sub_tree, hf_raknet_ipv4_address, tvb, *offset + 1, 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:%" G_GUINT16_FORMAT, addr_str, port); break; case 6: addr_str = tvb_ip6_to_str(tvb, *offset); proto_tree_add_item(sub_tree, hf_raknet_ipv6_address, tvb, *offset + 1, 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]:%" G_GUINT16_FORMAT, 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, "#%" G_GUINT32_FORMAT, min); ti = proto_tree_add_string_format_value(tree, hf_raknet_packet_number_range, tvb, offset, 1 + 3, "", "%" G_GUINT32_FORMAT " .. %" G_GUINT32_FORMAT, 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, "#%" G_GUINT32_FORMAT "..%" G_GUINT32_FORMAT, min, max); ti = proto_tree_add_string_format_value(tree, hf_raknet_packet_number_range, tvb, offset, 1 + 3 + 3, "", "%" G_GUINT32_FORMAT " .. %" G_GUINT32_FORMAT, 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; const int* 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 (%" G_GUINT32_FORMAT " 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(wmem_packet_scope(), ""); wmem_strbuf_append_printf(strbuf, "{Message fragment %" G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT "; 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(wmem_packet_scope(), ""); wmem_strbuf_append_printf(strbuf, "{Message fragment %" G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT "}", 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(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 */ const int *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 */ const int* 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; const int* 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 #%" G_GUINT32_FORMAT, packet_number); col_add_fstr(pinfo->cinfo, COL_INFO, "#%" G_GUINT32_FORMAT ": ", 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 - http://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: */