forked from osmocom/wireshark
d561afcd12
also remove item_size variable (replace directly by the value) Will be also fix some Clang analyzer warning about used variable Change-Id: I4bf3b118efb2b0846012a92e5bf596ca95043414 Reviewed-on: https://code.wireshark.org/review/18318 Petri-Dish: Michael Mann <mmann78@netscape.net> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Michael Mann <mmann78@netscape.net>
2050 lines
67 KiB
C
2050 lines
67 KiB
C
/*
|
|
* 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
|
|
*
|
|
* 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 <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/wmem.h>
|
|
|
|
#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;
|
|
|
|
/*
|
|
* 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);
|
|
|
|
/*
|
|
* Initialization.
|
|
*/
|
|
static void
|
|
raknet_reassemble_init(void)
|
|
{
|
|
reassembly_table_init(&raknet_reassembly_table,
|
|
&addresses_ports_reassembly_table_functions);
|
|
}
|
|
|
|
static void
|
|
raknet_reassemble_cleanup(void)
|
|
{
|
|
reassembly_table_destroy(&raknet_reassembly_table);
|
|
}
|
|
|
|
|
|
/*
|
|
* 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, 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;
|
|
|
|
ip_version = tvb_get_guint8(tvb, *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 + 1);
|
|
port = tvb_get_ntohs(tvb, *offset + 1 + 4);
|
|
|
|
set_address(&addr, AT_IPv4, sizeof(v4_addr), &v4_addr);
|
|
addr_str = address_to_display(wmem_packet_scope(), &addr);
|
|
|
|
ti = proto_tree_add_string_format_value(tree, hf, tvb, *offset, 1 + 4 + 2,
|
|
"", "%s:%" G_GUINT16_FORMAT, addr_str, port);
|
|
sub_tree = proto_item_add_subtree(ti, ett_raknet_system_address);
|
|
proto_tree_add_item(sub_tree, hf_raknet_ip_version, tvb, *offset, 1, ENC_NA);
|
|
proto_tree_add_ipv4(sub_tree, hf_raknet_ipv4_address, tvb, *offset + 1, 4, v4_addr);
|
|
proto_tree_add_item(sub_tree, hf_raknet_port, tvb, *offset + 1 + 4, 2, ENC_BIG_ENDIAN);
|
|
*offset += 1 + 4 + 2;
|
|
break;
|
|
case 6:
|
|
addr_str = tvb_ip6_to_str(tvb, *offset + 1);
|
|
port = tvb_get_ntohs(tvb, *offset + 1 + 16);
|
|
|
|
ti = proto_tree_add_string_format_value(tree, hf, tvb, *offset, 1 + 16 + 2,
|
|
"", "[%s]:%" G_GUINT16_FORMAT, addr_str, port);
|
|
sub_tree = proto_item_add_subtree(ti, ett_raknet_system_address);
|
|
proto_tree_add_item(sub_tree, hf_raknet_ip_version, tvb, *offset, 1, ENC_NA);
|
|
proto_tree_add_item(sub_tree, hf_raknet_ipv6_address, tvb, *offset + 1, 16, ENC_NA);
|
|
proto_tree_add_item(sub_tree, hf_raknet_port, tvb, *offset + 1 + 16, 2, ENC_BIG_ENDIAN);
|
|
*offset += 1 + 16 + 2;
|
|
break;
|
|
default:
|
|
THROW(ReportedBoundsError);
|
|
}
|
|
}
|
|
|
|
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, 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, 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, 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, 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 _U_,
|
|
proto_tree *tree, void* data _U_)
|
|
{
|
|
|
|
gint offset = 1;
|
|
gint i;
|
|
|
|
raknet_dissect_system_address(tree, hf_raknet_server_address, tvb, &offset);
|
|
|
|
for (i = 0; i < RAKNET_NUMBER_OF_INTERNAL_IDS; i++) {
|
|
raknet_dissect_system_address(tree, hf_raknet_internal_address, 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 }
|
|
},
|
|
};
|
|
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 init/cleanup routines.
|
|
*/
|
|
register_init_routine(raknet_reassemble_init);
|
|
register_cleanup_routine(raknet_reassemble_cleanup);
|
|
|
|
/*
|
|
* 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:
|
|
*/
|