From 032d90b9f6c9c83a4e1aaf87026ae72167d9ed3e Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 14 Apr 2020 21:22:19 +0200 Subject: [PATCH] Update wireshark dissector (#9621) --- util/wireshark/minetest.lua | 911 +++++++++++++++++++----------------- 1 file changed, 490 insertions(+), 421 deletions(-) diff --git a/util/wireshark/minetest.lua b/util/wireshark/minetest.lua index 1346adbc3..13cd6d482 100644 --- a/util/wireshark/minetest.lua +++ b/util/wireshark/minetest.lua @@ -23,18 +23,39 @@ -- +-- Wireshark documentation: +-- https://web.archive.org/web/20170711121726/https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Proto.html +-- https://web.archive.org/web/20170711121844/https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Tree.html +-- https://web.archive.org/web/20170711121917/https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Tvb.html + + -- Table of Contents: --- Part 1: Client command dissectors (TOSERVER_*) --- Part 2: Server command dissectors (TOCLIENT_*) --- Part 3: Wrapper protocol subdissectors --- Part 4: Wrapper protocol main dissector --- Part 5: Utility functions +-- Part 1: Utility functions +-- Part 2: Client command dissectors (TOSERVER_*) +-- Part 3: Server command dissectors (TOCLIENT_*) +-- Part 4: Wrapper protocol subdissectors +-- Part 5: Wrapper protocol main dissector +-- Part 6: Utility functions part 2 + + +----------------------- +-- Part 1 -- +-- Utility functions -- +----------------------- + +-- Creates two ProtoFields to hold a length and variable-length text content +-- lentype must be either "uint16" or "uint32" +function minetest_field_helper(lentype, name, abbr) + local f_textlen = ProtoField[lentype](name .. "len", abbr .. " (length)", base.DEC) + local f_text = ProtoField.string(name, abbr) + return f_textlen, f_text +end -------------------------------------------- --- Part 1 -- +-- Part 2 -- -- Client command dissectors (TOSERVER_*) -- -------------------------------------------- @@ -42,34 +63,71 @@ minetest_client_commands = {} minetest_client_obsolete = {} -- TOSERVER_INIT -minetest_client_commands[0x02] = { "INIT", 2 } do - local f_ser_fmt = ProtoField.uint8("minetest.client.init_ser_version", - "Maximum serialization format version", base.DEC) - local f_player_name = ProtoField.stringz("minetest.client.init_player_name", "Player Name") - local f_password = ProtoField.stringz("minetest.client.init_password", "Password") - local f_version = ProtoField.uint16("minetest.client.init_version", "Version", base.DEC) + local abbr = "minetest.client.init_" - minetest_client_commands[0x10] = { - "INIT_LEGACY", -- Command name - 53, -- Minimum message length including code - { f_ser_fmt, -- List of fields [optional] - f_player_name, - f_password, - f_version }, - function(buffer, pinfo, tree, t) -- Dissector function [optional] + local f_ser_fmt = ProtoField.uint8(abbr.."ser_version", + "Maximum serialization format version", base.DEC) + local f_comp_modes = ProtoField.uint16(abbr.."compression", + "Supported compression modes", base.DEC, { [0] = "No compression" }) + local f_proto_min = ProtoField.uint16(abbr.."proto_min", "Minimum protocol version", base.DEC) + local f_proto_max = ProtoField.uint16(abbr.."_proto_max", "Maximum protocol version", base.DEC) + local f_player_namelen, f_player_name = + minetest_field_helper("uint16", abbr.."player_name", "Player Name") + + minetest_client_commands[0x02] = { + "INIT", -- Command name + 11, -- Minimum message length including code + { f_ser_fmt, -- List of fields [optional] + f_comp_modes, + f_proto_min, + f_proto_max, + f_player_namelen, + f_player_name }, + function(buffer, pinfo, tree, t) -- Dissector function [optional] t:add(f_ser_fmt, buffer(2,1)) - t:add(f_player_name, buffer(3,20)) - t:add(f_password, buffer(23,28)) - t:add(f_version, buffer(51,2)) + t:add(f_comp_modes, buffer(3,2)) + t:add(f_proto_min, buffer(5,2)) + t:add(f_proto_max, buffer(7,2)) + minetest_decode_helper_ascii(buffer, t, "uint16", 9, f_player_namelen, f_player_name) end } end +-- TOSERVER_INIT_LEGACY (obsolete) + +minetest_client_commands[0x10] = { "INIT_LEGACY", 53 } +minetest_client_obsolete[0x10] = true + -- TOSERVER_INIT2 -minetest_client_commands[0x11] = { "INIT2", 2 } +do + local f_langlen, f_lang = + minetest_field_helper("uint16", "minetest.client.init2_language", "Language Code") + + minetest_client_commands[0x11] = { + "INIT2", + 2, + { f_langlen, + f_lang }, + function(buffer, pinfo, tree, t) + minetest_decode_helper_ascii(buffer, t, "uint16", 2, f_langlen, f_lang) + end + } +end + +-- TOSERVER_MODCHANNEL_JOIN + +minetest_client_commands[0x17] = { "MODCHANNEL_JOIN", 2 } + +-- TOSERVER_MODCHANNEL_LEAVE + +minetest_client_commands[0x18] = { "MODCHANNEL_LEAVE", 2 } + +-- TOSERVER_MODCHANNEL_MSG + +minetest_client_commands[0x19] = { "MODCHANNEL_MSG", 2 } -- TOSERVER_GETBLOCK (obsolete) @@ -89,18 +147,24 @@ minetest_client_obsolete[0x22] = true -- TOSERVER_PLAYERPOS do - local f_x = ProtoField.int32("minetest.client.playerpos_x", "Position X", base.DEC) - local f_y = ProtoField.int32("minetest.client.playerpos_y", "Position Y", base.DEC) - local f_z = ProtoField.int32("minetest.client.playerpos_z", "Position Z", base.DEC) - local f_speed_x = ProtoField.int32("minetest.client.playerpos_speed_x", "Speed X", base.DEC) - local f_speed_y = ProtoField.int32("minetest.client.playerpos_speed_y", "Speed Y", base.DEC) - local f_speed_z = ProtoField.int32("minetest.client.playerpos_speed_z", "Speed Z", base.DEC) - local f_pitch = ProtoField.int32("minetest.client.playerpos_pitch", "Pitch", base.DEC) - local f_yaw = ProtoField.int32("minetest.client.playerpos_yaw", "Yaw", base.DEC) + local abbr = "minetest.client.playerpos_" + + local f_x = ProtoField.int32(abbr.."x", "Position X", base.DEC) + local f_y = ProtoField.int32(abbr.."y", "Position Y", base.DEC) + local f_z = ProtoField.int32(abbr.."z", "Position Z", base.DEC) + local f_speed_x = ProtoField.int32(abbr.."speed_x", "Speed X", base.DEC) + local f_speed_y = ProtoField.int32(abbr.."speed_y", "Speed Y", base.DEC) + local f_speed_z = ProtoField.int32(abbr.."speed_z", "Speed Z", base.DEC) + local f_pitch = ProtoField.int32(abbr.."pitch", "Pitch", base.DEC) + local f_yaw = ProtoField.int32(abbr.."yaw", "Yaw", base.DEC) + local f_key_pressed = ProtoField.bytes(abbr.."key_pressed", "Pressed keys") + local f_fov = ProtoField.uint8(abbr.."fov", "FOV", base.DEC) + local f_wanted_range = ProtoField.uint8(abbr.."wanted_range", "Requested view range", base.DEC) minetest_client_commands[0x23] = { "PLAYERPOS", 34, - { f_x, f_y, f_z, f_speed_x, f_speed_y, f_speed_z, f_pitch, f_yaw }, + { f_x, f_y, f_z, f_speed_x, f_speed_y, f_speed_z, f_pitch, f_yaw, + f_key_pressed, f_fov, f_wanted_range }, function(buffer, pinfo, tree, t) t:add(f_x, buffer(2,4)) t:add(f_y, buffer(6,4)) @@ -110,6 +174,9 @@ do t:add(f_speed_z, buffer(22,4)) t:add(f_pitch, buffer(26,4)) t:add(f_yaw, buffer(30,4)) + t:add(f_key_pressed, buffer(34,4)) + t:add(f_fov, buffer(38,1)) + t:add(f_wanted_range, buffer(39,1)) end } end @@ -148,7 +215,6 @@ do end -- TOSERVER_DELETEDBLOCKS --- TODO: Test this do local f_count = ProtoField.uint8("minetest.client.deletedblocks_count", "Count", base.DEC) @@ -186,122 +252,25 @@ end minetest_client_commands[0x26] = { "ADDNODE_FROM_INVENTORY", 2 } minetest_client_obsolete[0x26] = true --- TOSERVER_CLICK_OBJECT --- TODO: Test this +-- TOSERVER_CLICK_OBJECT (obsolete) -do - local vs_button = { - [0] = "left", - [1] = "right" - } +minetest_client_commands[0x27] = { "CLICK_OBJECT", 2 } +minetest_client_obsolete[0x27] = true - local f_button = ProtoField.uint8("minetest.client.click_object_button", "Button", base.DEC, vs_button) - local f_blockpos_x = ProtoField.int16("minetest.client.click_object_blockpos_x", "Block position X", base.DEC) - local f_blockpos_y = ProtoField.int16("minetest.client.click_object_blockpos_y", "Block position Y", base.DEC) - local f_blockpos_z = ProtoField.int16("minetest.client.click_object_blockpos_z", "Block position Z", base.DEC) - local f_id = ProtoField.int16("minetest.client.click_object_id", "ID", base.DEC) - local f_item = ProtoField.uint16("minetest.client.click_object_item", "Item", base.DEC) +-- TOSERVER_GROUND_ACTION (obsolete) - minetest_client_commands[0x27] = { - "CLICK_OBJECT", 13, - { f_button, f_blockpos_x, f_blockpos_y, f_blockpos_z, f_id, f_item }, - function(buffer, pinfo, tree, t) - t:add(f_button, buffer(2,1)) - t:add(f_blockpos_x, buffer(3,2)) - t:add(f_blockpos_y, buffer(5,2)) - t:add(f_blockpos_z, buffer(7,2)) - t:add(f_id, buffer(9,2)) - t:add(f_item, buffer(11,2)) - end - } -end - --- TOSERVER_GROUND_ACTION - -do - local vs_action = { - [0] = "Start digging", - [1] = "Place block", - [2] = "Stop digging", - [3] = "Digging completed" - } - - local f_action = ProtoField.uint8("minetest.client.ground_action", "Action", base.DEC, vs_action) - local f_nodepos_undersurface_x = ProtoField.int16( - "minetest.client.ground_action_nodepos_undersurface_x", - "Node position (under surface) X") - local f_nodepos_undersurface_y = ProtoField.int16( - "minetest.client.ground_action_nodepos_undersurface_y", - "Node position (under surface) Y") - local f_nodepos_undersurface_z = ProtoField.int16( - "minetest.client.ground_action_nodepos_undersurface_z", - "Node position (under surface) Z") - local f_nodepos_abovesurface_x = ProtoField.int16( - "minetest.client.ground_action_nodepos_abovesurface_x", - "Node position (above surface) X") - local f_nodepos_abovesurface_y = ProtoField.int16( - "minetest.client.ground_action_nodepos_abovesurface_y", - "Node position (above surface) Y") - local f_nodepos_abovesurface_z = ProtoField.int16( - "minetest.client.ground_action_nodepos_abovesurface_z", - "Node position (above surface) Z") - local f_item = ProtoField.uint16("minetest.client.ground_action_item", "Item") - - minetest_client_commands[0x28] = { - "GROUND_ACTION", 17, - { f_action, - f_nodepos_undersurface_x, - f_nodepos_undersurface_y, - f_nodepos_undersurface_z, - f_nodepos_abovesurface_x, - f_nodepos_abovesurface_y, - f_nodepos_abovesurface_z, - f_item }, - function(buffer, pinfo, tree, t) - t:add(f_action, buffer(2,1)) - t:add(f_nodepos_undersurface_x, buffer(3,2)) - t:add(f_nodepos_undersurface_y, buffer(5,2)) - t:add(f_nodepos_undersurface_z, buffer(7,2)) - t:add(f_nodepos_abovesurface_x, buffer(9,2)) - t:add(f_nodepos_abovesurface_y, buffer(11,2)) - t:add(f_nodepos_abovesurface_z, buffer(13,2)) - t:add(f_item, buffer(15,2)) - end - } -end +minetest_client_commands[0x28] = { "GROUND_ACTION", 2 } +minetest_client_obsolete[0x28] = true -- TOSERVER_RELEASE (obsolete) minetest_client_commands[0x29] = { "RELEASE", 2 } minetest_client_obsolete[0x29] = true --- TOSERVER_SIGNTEXT (old signs) --- TODO: Test this or mark obsolete +-- TOSERVER_SIGNTEXT (obsolete) -do - local f_blockpos_x = ProtoField.int16("minetest.client.signtext_blockpos_x", "Block position X", base.DEC) - local f_blockpos_y = ProtoField.int16("minetest.client.signtext_blockpos_y", "Block position Y", base.DEC) - local f_blockpos_z = ProtoField.int16("minetest.client.signtext_blockpos_z", "Block position Z", base.DEC) - local f_id = ProtoField.int16("minetest.client.signtext_id", "ID", base.DEC) - local f_textlen = ProtoField.uint16("minetest.client.signtext_textlen", "Text length", base.DEC) - local f_text = ProtoField.string("minetest.client.signtext_text", "Text") - - minetest_client_commands[0x30] = { - "SIGNTEXT", 12, - { f_blockpos_x, f_blockpos_y, f_blockpos_z, f_id, f_textlen, f_text }, - function(buffer, pinfo, tree, t) - t:add(f_blockpos_x, buffer(2,2)) - t:add(f_blockpos_y, buffer(4,2)) - t:add(f_blockpos_z, buffer(6,2)) - t:add(f_id, buffer(8,2)) - t:add(f_textlen, buffer(10,2)) - local textlen = buffer(10,2):uint() - if minetest_check_length(buffer, 12 + textlen, t) then - t:add(f_text, buffer, buffer(12,textlen)) - end - end - } -end +minetest_client_commands[0x30] = { "SIGNTEXT", 2 } +minetest_client_obsolete[0x30] = true -- TOSERVER_INVENTORY_ACTION @@ -336,53 +305,16 @@ do } end --- TOSERVER_SIGNNODETEXT +-- TOSERVER_SIGNNODETEXT (obsolete) -do - local f_pos_x = ProtoField.int16("minetest.client.signnodetext_pos_x", "Block position X", base.DEC) - local f_pos_y = ProtoField.int16("minetest.client.signnodetext_pos_y", "Block position Y", base.DEC) - local f_pos_z = ProtoField.int16("minetest.client.signnodetext_pos_z", "Block position Z", base.DEC) - local f_textlen = ProtoField.uint16("minetest.client.signnodetext_textlen", "Text length", base.DEC) - local f_text = ProtoField.string("minetest.client.signnodetext_text", "Text") +minetest_client_commands[0x33] = { "SIGNNODETEXT", 2 } +minetest_client_obsolete[0x33] = true - minetest_client_commands[0x33] = { - "SIGNNODETEXT", 10, - { f_pos_x, f_pos_y, f_pos_z, f_textlen, f_text }, - function(buffer, pinfo, tree, t) - t:add(f_pos_x, buffer(2,2)) - t:add(f_pos_y, buffer(4,2)) - t:add(f_pos_z, buffer(6,2)) - t:add(f_textlen, buffer(8,2)) - local textlen = buffer(8,2):uint() - if minetest_check_length(buffer, 10 + textlen, t) then - t:add(f_text, buffer(10, textlen)) - end - end - } -end --- TOSERVER_CLICK_ACTIVEOBJECT +-- TOSERVER_CLICK_ACTIVEOBJECT (obsolete) -do - local vs_button = { - [0] = "left", - [1] = "right" - } - - local f_button = ProtoField.uint8("minetest.client.click_activeobject_button", "Button", base.DEC, vs_button) - local f_id = ProtoField.uint16("minetest.client.click_activeobject_id", "ID", base.DEC) - local f_item = ProtoField.uint16("minetest.client.click_activeobject_item", "Item", base.DEC) - - minetest_client_commands[0x34] = { - "CLICK_ACTIVEOBJECT", 7, - { f_button, f_id, f_item }, - function(buffer, pinfo, tree, t) - t:add(f_button, buffer(2,1)) - t:add(f_id, buffer(3,2)) - t:add(f_item, buffer(5,2)) - end - } -end +minetest_client_commands[0x34] = { "CLICK_ACTIVEOBJECT", 2 } +minetest_client_obsolete[0x34] = true -- TOSERVER_DAMAGE @@ -398,21 +330,10 @@ do } end --- TOSERVER_PASSWORD +-- TOSERVER_PASSWORD (obsolete) -do - local f_old_password = ProtoField.string("minetest.client.password_old", "Old password") - local f_new_password = ProtoField.string("minetest.client.password_new", "New password") - - minetest_client_commands[0x36] = { - "PASSWORD", 58, - { f_old_password, f_new_password }, - function(buffer, pinfo, tree, t) - t:add(f_old_password, buffer(2,28)) - t:add(f_new_password, buffer(30,28)) - end - } -end +minetest_client_commands[0x36] = { "CLICK_ACTIVEOBJECT", 2 } +minetest_client_obsolete[0x36] = true -- TOSERVER_PLAYERITEM @@ -429,16 +350,135 @@ do end -- TOSERVER_RESPAWN + minetest_client_commands[0x38] = { "RESPAWN", 2 } -minetest_client_commands[0x39] = { "INTERACT", 2 } +-- TOSERVER_INTERACT + +do + local abbr = "minetest.client.interact_" + local vs_action = { + [0] = "Start digging", + [1] = "Stop digging", + [2] = "Digging completed", + [3] = "Place block or item", + [4] = "Use item", + [5] = "Activate held item", + } + local vs_pointed_type = { + [0] = "Nothing", + [1] = "Node", + [2] = "Object", + } + + local f_action = ProtoField.uint8(abbr.."action", "Action", base.DEC, vs_action) + local f_item = ProtoField.uint16(abbr.."item", "Item Index", base.DEC) + local f_plen = ProtoField.uint32(abbr.."plen", "Length of pointed thing", base.DEC) + local f_pointed_version = ProtoField.uint8(abbr.."pointed_version", + "Pointed Thing Version", base.DEC) + local f_pointed_type = ProtoField.uint8(abbr.."pointed_version", + "Pointed Thing Type", base.DEC, vs_pointed_type) + local f_pointed_under_x = ProtoField.int16(abbr.."pointed_under_x", + "Node position (under surface) X") + local f_pointed_under_y = ProtoField.int16(abbr.."pointed_under_y", + "Node position (under surface) Y") + local f_pointed_under_z = ProtoField.int16(abbr.."pointed_under_z", + "Node position (under surface) Z") + local f_pointed_above_x = ProtoField.int16(abbr.."pointed_above_x", + "Node position (above surface) X") + local f_pointed_above_y = ProtoField.int16(abbr.."pointed_above_y", + "Node position (above surface) Y") + local f_pointed_above_z = ProtoField.int16(abbr.."pointed_above_z", + "Node position (above surface) Z") + local f_pointed_object_id = ProtoField.int16(abbr.."pointed_object_id", + "Object ID") + -- mising: additional playerpos data just like in TOSERVER_PLAYERPOS + + minetest_client_commands[0x39] = { + "INTERACT", 11, + { f_action, + f_item, + f_plen, + f_pointed_version, + f_pointed_type, + f_pointed_under_x, + f_pointed_under_y, + f_pointed_under_z, + f_pointed_above_x, + f_pointed_above_y, + f_pointed_above_z, + f_pointed_object_id }, + function(buffer, pinfo, tree, t) + t:add(f_action, buffer(2,1)) + t:add(f_item, buffer(3,2)) + t:add(f_plen, buffer(5,4)) + local plen = buffer(5,4):uint() + if minetest_check_length(buffer, 9 + plen, t) then + t:add(f_pointed_version, buffer(9,1)) + t:add(f_pointed_type, buffer(10,1)) + local ptype = buffer(10,1):uint() + if ptype == 1 then -- Node + t:add(f_pointed_under_x, buffer(11,2)) + t:add(f_pointed_under_y, buffer(13,2)) + t:add(f_pointed_under_z, buffer(15,2)) + t:add(f_pointed_above_x, buffer(17,2)) + t:add(f_pointed_above_x, buffer(19,2)) + t:add(f_pointed_above_x, buffer(21,2)) + elseif ptype == 2 then -- Object + t:add(f_pointed_object_id, buffer(11,2)) + end + end + end + } +end + +-- ... + minetest_client_commands[0x3a] = { "REMOVED_SOUNDS", 2 } minetest_client_commands[0x3b] = { "NODEMETA_FIELDS", 2 } minetest_client_commands[0x3c] = { "INVENTORY_FIELDS", 2 } minetest_client_commands[0x40] = { "REQUEST_MEDIA", 2 } minetest_client_commands[0x41] = { "RECEIVED_MEDIA", 2 } + +-- TOSERVER_BREATH (obsolete) + minetest_client_commands[0x42] = { "BREATH", 2 } -minetest_client_commands[0x43] = { "CLIENT_READY", 2 } +minetest_client_obsolete[0x42] = true + +-- TOSERVER_CLIENT_READY + +do + local abbr = "minetest.client.client_ready_" + local f_major = ProtoField.uint8(abbr.."major","Version Major") + local f_minor = ProtoField.uint8(abbr.."minor","Version Minor") + local f_patch = ProtoField.uint8(abbr.."patch","Version Patch") + local f_reserved = ProtoField.uint8(abbr.."reserved","Reserved") + local f_versionlen, f_version = + minetest_field_helper("uint16", abbr.."version", "Full Version String") + local f_formspec_ver = ProtoField.uint16(abbr.."formspec_version", + "Formspec API version") + + minetest_client_commands[0x43] = { + "CLIENT_READY", + 8, + { f_major, f_minor, f_patch, f_reserved, f_versionlen, + f_version, f_formspec_ver }, + function(buffer, pinfo, tree, t) + t:add(f_major, buffer(2,1)) + t:add(f_minor, buffer(3,1)) + t:add(f_patch, buffer(4,1)) + t:add(f_reserved, buffer(5,1)) + local off = minetest_decode_helper_ascii(buffer, t, "uint16", 6, + f_versionlen, f_version) + if off and minetest_check_length(buffer, off + 2, t) then + t:add(f_formspec_ver, buffer(off,2)) + end + end + } +end + +-- ... + minetest_client_commands[0x50] = { "FIRST_SRP", 2 } minetest_client_commands[0x51] = { "SRP_BYTES_A", 2 } minetest_client_commands[0x52] = { "SRP_BYTES_M", 2 } @@ -446,40 +486,84 @@ minetest_client_commands[0x52] = { "SRP_BYTES_M", 2 } -------------------------------------------- --- Part 2 -- +-- Part 3 -- -- Server command dissectors (TOCLIENT_*) -- -------------------------------------------- minetest_server_commands = {} minetest_server_obsolete = {} --- TOCLIENT_INIT +-- TOCLIENT_HELLO + +do + local abbr = "minetest.server.hello_" + + local f_ser_fmt = ProtoField.uint8(abbr.."ser_version", + "Deployed serialization format version", base.DEC) + local f_comp_mode = ProtoField.uint16(abbr.."compression", + "Deployed compression mode", base.DEC, { [0] = "No compression" }) + local f_proto = ProtoField.uint16(abbr.."proto", + "Deployed protocol version", base.DEC) + local f_auth_methods = ProtoField.bytes(abbr.."auth_modes", + "Supported authentication modes") + local f_legacy_namelen, f_legacy_name = minetest_field_helper("uint16", + abbr.."legacy_name", "Legacy player name for hashing") + + minetest_server_commands[0x02] = { + "HELLO", + 13, + { f_ser_fmt, f_comp_mode, f_proto, f_auth_methods, + f_legacy_namelen, f_legacy_name }, + function(buffer, pinfo, tree, t) + t:add(f_ser_fmt, buffer(2,1)) + t:add(f_comp_mode, buffer(3,2)) + t:add(f_proto, buffer(5,2)) + t:add(f_auth_methods, buffer(7,4)) + minetest_decode_helper_ascii(buffer, t, "uint16", 11, f_legacy_namelen, f_legacy_name) + end + } +end + +-- TOCLIENT_AUTH_ACCEPT + +do + local abbr = "minetest.server.auth_accept_" + + local f_player_x = ProtoField.float(abbr.."player_x", "Player position X") + local f_player_y = ProtoField.float(abbr.."player_y", "Player position Y") + local f_player_z = ProtoField.float(abbr.."player_z", "Player position Z") + local f_map_seed = ProtoField.uint64(abbr.."map_seed", "Map seed") + local f_send_interval = ProtoField.float(abbr.."send_interval", + "Recommended send interval") + local f_sudo_auth_methods = ProtoField.bytes(abbr.."sudo_auth_methods", + "Supported auth methods for sudo mode") + + minetest_server_commands[0x03] = { + "AUTH_ACCEPT", + 30, + { f_player_x, f_player_y, f_player_z, f_map_seed, + f_send_interval, f_sudo_auth_methods }, + function(buffer, pinfo, tree, t) + t:add(f_player_x, buffer(2,4)) + t:add(f_player_y, buffer(6,4)) + t:add(f_player_z, buffer(10,4)) + t:add(f_map_seed, buffer(14,8)) + t:add(f_send_interval, buffer(22,4)) + t:add(f_sudo_auth_methods, buffer(26,4)) + end + } +end + +-- ... -minetest_server_commands[0x02] = {"HELLO", 2} -minetest_server_commands[0x03] = {"AUTH_ACCEPT", 2} minetest_server_commands[0x04] = {"ACCEPT_SUDO_MODE", 2} minetest_server_commands[0x05] = {"DENY_SUDO_MODE", 2} minetest_server_commands[0x0A] = {"ACCESS_DENIED", 2} -do - local f_version = ProtoField.uint8("minetest.server.init_version", "Deployed version", base.DEC) - local f_pos_x = ProtoField.int16("minetest.server.init_pos_x", "Position X", base.DEC) - local f_pos_y = ProtoField.int16("minetest.server.init_pos_y", "Position Y", base.DEC) - local f_pos_z = ProtoField.int16("minetest.server.init_pos_x", "Position Z", base.DEC) - local f_map_seed = ProtoField.uint64("minetest.server.init_map_seed", "Map seed", base.DEC) +-- TOCLIENT_INIT (obsolete) - minetest_server_commands[0x10] = { - "INIT", 17, - { f_version, f_pos_x, f_pos_y, f_pos_z, f_map_seed }, - function(buffer, pinfo, tree, t) - t:add(f_version, buffer(2,1)) - t:add(f_pos_x, buffer(3,2)) - t:add(f_pos_y, buffer(5,2)) - t:add(f_pos_z, buffer(7,2)) - t:add(f_map_seed, buffer(9,8)) - end - } -end +minetest_server_commands[0x10] = { "INIT", 2 } +minetest_server_obsolete[0x10] = true -- TOCLIENT_BLOCKDATA @@ -544,37 +628,10 @@ end minetest_server_commands[0x23] = { "PLAYERPOS", 2 } minetest_server_obsolete[0x23] = true --- TOCLIENT_PLAYERINFO +-- TOCLIENT_PLAYERINFO (obsolete) -do - local f_count = ProtoField.uint16("minetest.server.playerinfo_count", "Count", base.DEC) - local f_player = ProtoField.bytes("minetest.server.playerinfo_player", "Player", base.NONE) - local f_peer_id = ProtoField.uint16("minetest.server.playerinfo_peer_id", "Peer ID", base.DEC) - local f_name = ProtoField.string("minetest.server.playerinfo_name", "Name") - - minetest_server_commands[0x24] = { - "PLAYERINFO", 2, - { f_count, f_player, f_peer_id, f_name }, - function(buffer, pinfo, tree, t) - local count = 0 - local pos, index - for pos = 2, buffer:len() - 22, 22 do -- does lua have integer division? - count = count + 1 - end - t:add(f_count, count):set_generated() - t:set_len(2 + 22 * count) - pinfo.cols.info:append(" * " .. count) - for index = 0, count - 1 do - local pos = 2 + 22 * index - local t2 = t:add(f_player, buffer(pos, 22)) - t2:set_text("Player, ID: " .. buffer(pos, 2):uint() - .. ", Name: " .. buffer(pos + 2, 20):string()) - t2:add(f_peer_id, buffer(pos, 2)) - t2:add(f_name, buffer(pos + 2, 20)) - end - end - } -end +minetest_server_commands[0x24] = { "PLAYERINFO", 2 } +minetest_server_obsolete[0x24] = true -- TOCLIENT_OPT_BLOCK_NOT_FOUND (obsolete) @@ -600,100 +657,74 @@ do } end --- TOCLIENT_OBJECTDATA +-- TOCLIENT_OBJECTDATA (obsolete) -do - local f_player_count = ProtoField.uint16("minetest.server.objectdata_player_count", - "Count of player positions", base.DEC) - local f_player = ProtoField.bytes("minetest.server.objectdata_player", "Player position") - local f_peer_id = ProtoField.uint16("minetest.server.objectdata_player_peer_id", "Peer ID") - local f_x = ProtoField.int32("minetest.server.objectdata_player_x", "Position X", base.DEC) - local f_y = ProtoField.int32("minetest.server.objectdata_player_y", "Position Y", base.DEC) - local f_z = ProtoField.int32("minetest.server.objectdata_player_z", "Position Z", base.DEC) - local f_speed_x = ProtoField.int32("minetest.server.objectdata_player_speed_x", "Speed X", base.DEC) - local f_speed_y = ProtoField.int32("minetest.server.objectdata_player_speed_y", "Speed Y", base.DEC) - local f_speed_z = ProtoField.int32("minetest.server.objectdata_player_speed_z", "Speed Z", base.DEC) - local f_pitch = ProtoField.int32("minetest.server.objectdata_player_pitch", "Pitch", base.DEC) - local f_yaw = ProtoField.int32("minetest.server.objectdata_player_yaw", "Yaw", base.DEC) - local f_block_count = ProtoField.uint16("minetest.server.objectdata_block_count", - "Count of blocks", base.DEC) - - minetest_server_commands[0x28] = { - "OBJECTDATA", 6, - { f_player_count, f_player, f_peer_id, f_x, f_y, f_z, - f_speed_x, f_speed_y, f_speed_z,f_pitch, f_yaw, - f_block_count }, - function(buffer, pinfo, tree, t) - local t2, index, pos - - local player_count_pos = 2 - local player_count = buffer(player_count_pos, 2):uint() - t:add(f_player_count, buffer(player_count_pos, 2)) - - local block_count_pos = player_count_pos + 2 + 34 * player_count - if not minetest_check_length(buffer, block_count_pos + 2, t) then - return - end - - for index = 0, player_count - 1 do - pos = player_count_pos + 2 + 34 * index - t2 = t:add(f_player, buffer(pos, 34)) - t2:set_text("Player position, ID: " .. buffer(pos, 2):uint()) - t2:add(f_peer_id, buffer(pos, 2)) - t2:add(f_x, buffer(pos + 2, 4)) - t2:add(f_y, buffer(pos + 6, 4)) - t2:add(f_z, buffer(pos + 10, 4)) - t2:add(f_speed_x, buffer(pos + 14, 4)) - t2:add(f_speed_y, buffer(pos + 18, 4)) - t2:add(f_speed_z, buffer(pos + 22, 4)) - t2:add(f_pitch, buffer(pos + 26, 4)) - t2:add(f_yaw, buffer(pos + 30, 4)) - end - - local block_count = buffer(block_count_pos, 2):uint() - t:add(f_block_count, buffer(block_count_pos, 2)) - - -- TODO: dissect blocks. - -- NOTE: block_count > 0 is obsolete. (?) - - pinfo.cols.info:append(" * " .. (player_count + block_count)) - end - } -end +minetest_server_commands[0x28] = { "OBJECTDATA", 2 } +minetest_server_obsolete[0x28] = true -- TOCLIENT_TIME_OF_DAY do local f_time = ProtoField.uint16("minetest.server.time_of_day", "Time", base.DEC) + local f_time_speed = ProtoField.float("minetest.server.time_speed", "Time Speed", base.DEC) minetest_server_commands[0x29] = { "TIME_OF_DAY", 4, - { f_time }, + { f_time, f_time_speed }, function(buffer, pinfo, tree, t) t:add(f_time, buffer(2,2)) + t:add(f_time_speed, buffer(4,4)) end } end +-- TOCLIENT_CSM_RESTRICTION_FLAGS + +minetest_server_commands[0x2a] = { "CSM_RESTRICTION_FLAGS", 2 } + +-- TOCLIENT_PLAYER_SPEED + +minetest_server_commands[0x2b] = { "PLAYER_SPEED", 2 } + -- TOCLIENT_CHAT_MESSAGE do - local f_length = ProtoField.uint16("minetest.server.chat_message_length", "Length", base.DEC) - local f_message = ProtoField.string("minetest.server.chat_message", "Message") + local abbr = "minetest.server.chat_message_" + local vs_type = { + [0] = "Raw", + [1] = "Normal", + [2] = "Announce", + [3] = "System", + } - minetest_server_commands[0x30] = { - "CHAT_MESSAGE", 4, - { f_length, f_message }, + local f_version = ProtoField.uint8(abbr.."version", "Version") + local f_type = ProtoField.uint8(abbr.."type", "Message Type", base.DEC, vs_type) + local f_senderlen, f_sender = minetest_field_helper("uint16", abbr.."sender", + "Message sender") + local f_messagelen, f_message = minetest_field_helper("uint16", abbr:sub(1,-2), + "Message") + + minetest_server_commands[0x2f] = { + "CHAT_MESSAGE", 8, + { f_version, f_type, f_senderlen, f_sender, + f_messagelen, f_message }, function(buffer, pinfo, tree, t) - t:add(f_length, buffer(2,2)) - local textlen = buffer(2,2):uint() - if minetest_check_length(buffer, 4 + textlen*2, t) then - t:add(f_message, minetest_convert_utf16(buffer(4, textlen*2), "Converted chat message")) + t:add(f_version, buffer(2,1)) + t:add(f_type, buffer(3,1)) + local off = 4 + off = minetest_decode_helper_utf16(buffer, t, "uint16", off, f_senderlen, f_sender) + if off then + off = minetest_decode_helper_utf16(buffer, t, "uint16", off, f_messagelen, f_message) end end } end +-- TOCLIENT_CHAT_MESSAGE_OLD (obsolete) + +minetest_server_commands[0x30] = { "CHAT_MESSAGE_OLD", 2 } +minetest_server_obsolete[0x30] = true + -- TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD do @@ -842,13 +873,13 @@ end -- TOCLIENT_HP do - local f_hp = ProtoField.uint8("minetest.server.hp", "Hitpoints", base.DEC) + local f_hp = ProtoField.uint16("minetest.server.hp", "Hitpoints", base.DEC) minetest_server_commands[0x33] = { - "HP", 3, + "HP", 4, { f_hp }, function(buffer, pinfo, tree, t) - t:add(f_hp, buffer(2,1)) + t:add(f_hp, buffer(2,2)) end } end @@ -856,45 +887,35 @@ end -- TOCLIENT_MOVE_PLAYER do - local f_x = ProtoField.int32("minetest.server.move_player_x", "Position X", base.DEC) - local f_y = ProtoField.int32("minetest.server.move_player_y", "Position Y", base.DEC) - local f_z = ProtoField.int32("minetest.server.move_player_z", "Position Z", base.DEC) - local f_pitch = ProtoField.int32("minetest.server.move_player_pitch", "Pitch", base.DEC) - local f_yaw = ProtoField.int32("minetest.server.move_player_yaw", "Yaw", base.DEC) - local f_garbage = ProtoField.bytes("minetest.server.move_player_garbage", "Garbage") + local abbr = "minetest.server.move_player_" + + local f_x = ProtoField.float(abbr.."x", "Position X") + local f_y = ProtoField.float(abbr.."y", "Position Y") + local f_z = ProtoField.float(abbr.."z", "Position Z") + local f_pitch = ProtoField.float(abbr.."_pitch", "Pitch") + local f_yaw = ProtoField.float(abbr.."yaw", "Yaw") minetest_server_commands[0x34] = { - "MOVE_PLAYER", 18, -- actually 22, but see below + "MOVE_PLAYER", 22, { f_x, f_y, f_z, f_pitch, f_yaw, f_garbage }, function(buffer, pinfo, tree, t) t:add(f_x, buffer(2, 4)) t:add(f_y, buffer(6, 4)) t:add(f_z, buffer(10, 4)) - - -- Compatibility note: - -- Up to 2011-08-23, there was a bug in Minetest that - -- caused the server to serialize the pitch and yaw - -- with 2 bytes each instead of 4, creating a - -- malformed message. - if buffer:len() >= 22 then - t:add(f_pitch, buffer(14, 4)) - t:add(f_yaw, buffer(18, 4)) - else - t:add(f_garbage, buffer(14, 4)) - t:add_expert_info(PI_MALFORMED, PI_WARN, "Malformed pitch and yaw, possibly caused by a serialization bug in Minetest") - end + t:add(f_pitch, buffer(14, 4)) + t:add(f_yaw, buffer(18, 4)) end } end --- TOCLIENT_ACCESS_DENIED +-- TOCLIENT_ACCESS_DENIED_LEGACY do local f_reason_length = ProtoField.uint16("minetest.server.access_denied_reason_length", "Reason length", base.DEC) local f_reason = ProtoField.string("minetest.server.access_denied_reason", "Reason") minetest_server_commands[0x35] = { - "ACCESS_DENIED", 4, + "ACCESS_DENIED_LEGACY", 4, { f_reason_length, f_reason }, function(buffer, pinfo, tree, t) t:add(f_reason_length, buffer(2,2)) @@ -906,68 +927,16 @@ do } end --- TOCLIENT_PLAYERITEM +-- TOCLIENT_FOV -do - local f_count = ProtoField.uint16( - "minetest.server.playeritem_count", - "Count of players", base.DEC) - local f_player = ProtoField.bytes( - "minetest.server.playeritem_player", - "Player") - local f_peer_id = ProtoField.uint16( - "minetest.server.playeritem_peer_id", - "Peer ID", base.DEC) - local f_item_length = ProtoField.uint16( - "minetest.server.playeritem_item_length", - "Item information length", base.DEC) - local f_item = ProtoField.string( - "minetest.server.playeritem_item", - "Item information") - - minetest_server_commands[0x36] = { - "PLAYERITEM", 4, - { f_count, f_player, f_peer_id, f_item_length, f_item }, - function(buffer, pinfo, tree, t) - local count, index, pos, item_length - - count = buffer(2,2):uint() - pinfo.cols.info:append(" * " .. count) - t:add(f_count, buffer(2,2)) - - pos = 4 - for index = 0, count - 1 do - if not minetest_check_length(buffer, pos + 4, t) then - return - end - item_length = buffer(pos + 2, 2):uint() - if not minetest_check_length(buffer, pos + 4 + item_length, t) then - return - end - - local t2 = t:add(f_player, buffer(pos, 4 + item_length)) - t2:set_text("Player, ID: " .. buffer(pos, 2):uint()) - t2:add(f_peer_id, buffer(pos, 2)) - t2:add(f_item_length, buffer(pos + 2, 2)) - t2:add(f_item, buffer(pos + 4, item_length)) - - pos = pos + 4 + item_length - end - end - } -end +minetest_server_commands[0x36] = { "FOV", 2 } -- TOCLIENT_DEATHSCREEN do - local vs_set_camera_point_target = { - [0] = "False", - [1] = "True" - } - - local f_set_camera_point_target = ProtoField.uint8( + local f_set_camera_point_target = ProtoField.bool( "minetest.server.deathscreen_set_camera_point_target", - "Set camera point target", base.DEC, vs_set_camera_point_target) + "Set camera point target") local f_camera_point_target_x = ProtoField.int32( "minetest.server.deathscreen_camera_point_target_x", "Camera point target X", base.DEC) @@ -991,10 +960,26 @@ do } end +-- TOCLIENT_MEDIA + minetest_server_commands[0x38] = {"MEDIA", 2} + +-- TOCLIENT_TOOLDEF (obsolete) + minetest_server_commands[0x39] = {"TOOLDEF", 2} +minetest_server_obsolete[0x39] = true + +-- TOCLIENT_NODEDEF + minetest_server_commands[0x3a] = {"NODEDEF", 2} + +-- TOCLIENT_CRAFTITEMDEF (obsolete) + minetest_server_commands[0x3b] = {"CRAFTITEMDEF", 2} +minetest_server_obsolete[0x3b] = true + +-- ... + minetest_server_commands[0x3c] = {"ANNOUNCE_MEDIA", 2} minetest_server_commands[0x3d] = {"ITEMDEF", 2} minetest_server_commands[0x3f] = {"PLAY_SOUND", 2} @@ -1006,7 +991,14 @@ minetest_server_commands[0x44] = {"SHOW_FORMSPEC", 2} minetest_server_commands[0x45] = {"MOVEMENT", 2} minetest_server_commands[0x46] = {"SPAWN_PARTICLE", 2} minetest_server_commands[0x47] = {"ADD_PARTICLE_SPAWNER", 2} + +-- TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY (obsolete) + minetest_server_commands[0x48] = {"DELETE_PARTICLESPAWNER_LEGACY", 2} +minetest_server_obsolete[0x48] = true + +-- ... + minetest_server_commands[0x49] = {"HUDADD", 2} minetest_server_commands[0x4a] = {"HUDRM", 2} minetest_server_commands[0x4b] = {"HUDCHANGE", 2} @@ -1020,11 +1012,57 @@ minetest_server_commands[0x52] = {"EYE_OFFSET", 2} minetest_server_commands[0x53] = {"DELETE_PARTICLESPAWNER", 2} minetest_server_commands[0x54] = {"CLOUD_PARAMS", 2} minetest_server_commands[0x55] = {"FADE_SOUND", 2} -minetest_server_commands[0x61] = {"SRP_BYTES_S_B", 2} + +-- TOCLIENT_UPDATE_PLAYER_LIST + +do + local abbr = "minetest.server.update_player_list_" + local vs_type = { + [0] = "Init", + [1] = "Add", + [2] = "Remove", + } + + local f_type = ProtoField.uint8(abbr.."type", "Type", base.DEC, vs_type) + local f_count = ProtoField.uint16(abbr.."count", "Number of players", base.DEC) + local f_name = ProtoField.string(abbr.."name", "Name") + + minetest_server_commands[0x56] = { + "UPDATE_PLAYER_LIST", + 5, + { f_type, f_count, f_name }, + function(buffer, pinfo, tree, t) + t:add(f_type, buffer(2,1)) + t:add(f_count, buffer(3,2)) + local count = buffer(3,2):uint() + local off = 5 + for i = 1, count do + if not minetest_check_length(buffer, off + 2, t) then + return + end + off = minetest_decode_helper_ascii(buffer, t, "uint16", off, nil, f_name) + if not off then + return + end + end + end + } +end + +-- ... + +minetest_server_commands[0x57] = {"MODCHANNEL_MSG", 2} +minetest_server_commands[0x58] = {"MODCHANNEL_SIGNAL", 2} +minetest_server_commands[0x59] = {"NODEMETA_CHANGED", 2} +minetest_server_commands[0x5a] = {"SET_SUN", 2} +minetest_server_commands[0x5b] = {"SET_MOON", 2} +minetest_server_commands[0x5c] = {"SET_STARS", 2} +minetest_server_commands[0x60] = {"SRP_BYTES_S_B", 2} +minetest_server_commands[0x61] = {"FORMSPEC_PREPEND", 2} ------------------------------------ --- Part 3 -- +-- Part 4 -- -- Wrapper protocol subdissectors -- ------------------------------------ @@ -1093,15 +1131,15 @@ function minetest_define_client_or_server_proto(is_client) this_peer = "Client" other_peer = "Server" empty_message_info = "Empty message / Connect" - commands = minetest_client_commands -- defined in Part 1 - obsolete = minetest_client_obsolete -- defined in Part 1 + commands = minetest_client_commands -- defined in Part 2 + obsolete = minetest_client_obsolete -- defined in Part 2 else proto_name = "minetest.server" this_peer = "Server" other_peer = "Client" empty_message_info = "Empty message" - commands = minetest_server_commands -- defined in Part 2 - obsolete = minetest_server_obsolete -- defined in Part 2 + commands = minetest_server_commands -- defined in Part 3 + obsolete = minetest_server_obsolete -- defined in Part 3 end -- Create the protocol object. @@ -1124,8 +1162,8 @@ function minetest_define_client_or_server_proto(is_client) for code, command_info in pairs(commands) do local command_fields = command_info[3] if command_fields ~= nil then - local index, field for index, field in ipairs(command_fields) do + assert(field ~= nil) table.insert(proto.fields, field) end end @@ -1199,7 +1237,7 @@ end ------------------------------------- --- Part 4 -- +-- Part 5 -- -- Wrapper protocol main dissector -- ------------------------------------- @@ -1311,10 +1349,10 @@ end ------------------------ --- Part 5 -- --- Utility functions -- ------------------------ +------------------------------ +-- Part 6 -- +-- Utility functions part 2 -- +------------------------------ -- Checks if a (sub-)Tvb is long enough to be further dissected. -- If it is long enough, sets the dissector tree item length to min_len @@ -1329,14 +1367,11 @@ function minetest_check_length(tvb, min_len, t) t:set_len(min_len) return true - -- Tvb:reported_length_remaining() has been added in August 2011 - -- and is not yet widely available, disable for the time being - -- TODO: uncomment at a later date - -- TODO: when uncommenting this, also re-check if other parts of + -- TODO: check if other parts of -- the dissector could benefit from reported_length_remaining - --elseif tvb:reported_length_remaining() >= min_len then - -- t:add_expert_info(PI_UNDECODED, PI_INFO, "Only part of this packet was captured, unable to decode.") - -- return false + elseif tvb:reported_length_remaining() >= min_len then + t:add_expert_info(PI_UNDECODED, PI_INFO, "Only part of this packet was captured, unable to decode.") + return false else t:add_expert_info(PI_MALFORMED, PI_ERROR, "Message is too short") @@ -1358,7 +1393,7 @@ function minetest_convert_utf16(tvb, name) hex = "" for pos = 0, tvb:len() - 2, 2 do char = tvb(pos, 2):uint() - if (char >= 0x20) and (char <= 0x7e) then + if (char >= 0x20 and char <= 0x7e) or char == 0x0a then hex = hex .. string.format(" %02x", char) else hex = hex .. " 3F" @@ -1373,3 +1408,37 @@ function minetest_convert_utf16(tvb, name) end end +-- Decodes a variable-length string as ASCII text +-- t_textlen, t_text should be the ProtoFields created by minetest_field_helper +-- alternatively t_text can be a ProtoField.string and t_textlen can be nil +-- lentype must be the type of the length field (as passed to minetest_field_helper) +-- returns nil if length check failed +function minetest_decode_helper_ascii(tvb, t, lentype, offset, f_textlen, f_text) + local n = ({uint16 = 2, uint32 = 4})[lentype] + assert(n) + + if f_textlen then + t:add(f_textlen, tvb(offset, n)) + end + local textlen = tvb(offset, n):uint() + if minetest_check_length(tvb, offset + n + textlen, t) then + t:add(f_text, tvb(offset + n, textlen)) + return offset + n + textlen + end +end + +-- Decodes a variable-length string as UTF-16 text +-- (see minetest_decode_helper_ascii) +function minetest_decode_helper_utf16(tvb, t, lentype, offset, f_textlen, f_text) + local n = ({uint16 = 2, uint32 = 4})[lentype] + assert(n) + + if f_textlen then + t:add(f_textlen, tvb(offset, n)) + end + local textlen = tvb(offset, n):uint() * 2 + if minetest_check_length(tvb, offset + n + textlen, t) then + t:add(f_text, minetest_convert_utf16(tvb(offset + n, textlen), "UTF-16 text")) + return offset + n + textlen + end +end