minetest.lua 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375
  1. -- minetest.lua
  2. -- Packet dissector for the UDP-based Minetest protocol
  3. -- Copy this to $HOME/.wireshark/plugins/
  4. --
  5. -- Minetest
  6. -- Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
  7. --
  8. -- This program is free software; you can redistribute it and/or modify
  9. -- it under the terms of the GNU General Public License as published by
  10. -- the Free Software Foundation; either version 2 of the License, or
  11. -- (at your option) any later version.
  12. --
  13. -- This program is distributed in the hope that it will be useful,
  14. -- but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. -- GNU General Public License for more details.
  17. --
  18. -- You should have received a copy of the GNU General Public License along
  19. -- with this program; if not, write to the Free Software Foundation, Inc.,
  20. -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21. --
  22. -- Table of Contents:
  23. -- Part 1: Client command dissectors (TOSERVER_*)
  24. -- Part 2: Server command dissectors (TOCLIENT_*)
  25. -- Part 3: Wrapper protocol subdissectors
  26. -- Part 4: Wrapper protocol main dissector
  27. -- Part 5: Utility functions
  28. --------------------------------------------
  29. -- Part 1 --
  30. -- Client command dissectors (TOSERVER_*) --
  31. --------------------------------------------
  32. minetest_client_commands = {}
  33. minetest_client_obsolete = {}
  34. -- TOSERVER_INIT
  35. minetest_client_commands[0x02] = { "INIT", 2 }
  36. do
  37. local f_ser_fmt = ProtoField.uint8("minetest.client.init_ser_version",
  38. "Maximum serialization format version", base.DEC)
  39. local f_player_name = ProtoField.stringz("minetest.client.init_player_name", "Player Name")
  40. local f_password = ProtoField.stringz("minetest.client.init_password", "Password")
  41. local f_version = ProtoField.uint16("minetest.client.init_version", "Version", base.DEC)
  42. minetest_client_commands[0x10] = {
  43. "INIT_LEGACY", -- Command name
  44. 53, -- Minimum message length including code
  45. { f_ser_fmt, -- List of fields [optional]
  46. f_player_name,
  47. f_password,
  48. f_version },
  49. function(buffer, pinfo, tree, t) -- Dissector function [optional]
  50. t:add(f_ser_fmt, buffer(2,1))
  51. t:add(f_player_name, buffer(3,20))
  52. t:add(f_password, buffer(23,28))
  53. t:add(f_version, buffer(51,2))
  54. end
  55. }
  56. end
  57. -- TOSERVER_INIT2
  58. minetest_client_commands[0x11] = { "INIT2", 2 }
  59. -- TOSERVER_GETBLOCK (obsolete)
  60. minetest_client_commands[0x20] = { "GETBLOCK", 2 }
  61. minetest_client_obsolete[0x20] = true
  62. -- TOSERVER_ADDNODE (obsolete)
  63. minetest_client_commands[0x21] = { "ADDNODE", 2 }
  64. minetest_client_obsolete[0x21] = true
  65. -- TOSERVER_REMOVENODE (obsolete)
  66. minetest_client_commands[0x22] = { "REMOVENODE", 2 }
  67. minetest_client_obsolete[0x22] = true
  68. -- TOSERVER_PLAYERPOS
  69. do
  70. local f_x = ProtoField.int32("minetest.client.playerpos_x", "Position X", base.DEC)
  71. local f_y = ProtoField.int32("minetest.client.playerpos_y", "Position Y", base.DEC)
  72. local f_z = ProtoField.int32("minetest.client.playerpos_z", "Position Z", base.DEC)
  73. local f_speed_x = ProtoField.int32("minetest.client.playerpos_speed_x", "Speed X", base.DEC)
  74. local f_speed_y = ProtoField.int32("minetest.client.playerpos_speed_y", "Speed Y", base.DEC)
  75. local f_speed_z = ProtoField.int32("minetest.client.playerpos_speed_z", "Speed Z", base.DEC)
  76. local f_pitch = ProtoField.int32("minetest.client.playerpos_pitch", "Pitch", base.DEC)
  77. local f_yaw = ProtoField.int32("minetest.client.playerpos_yaw", "Yaw", base.DEC)
  78. minetest_client_commands[0x23] = {
  79. "PLAYERPOS", 34,
  80. { f_x, f_y, f_z, f_speed_x, f_speed_y, f_speed_z, f_pitch, f_yaw },
  81. function(buffer, pinfo, tree, t)
  82. t:add(f_x, buffer(2,4))
  83. t:add(f_y, buffer(6,4))
  84. t:add(f_z, buffer(10,4))
  85. t:add(f_speed_x, buffer(14,4))
  86. t:add(f_speed_y, buffer(18,4))
  87. t:add(f_speed_z, buffer(22,4))
  88. t:add(f_pitch, buffer(26,4))
  89. t:add(f_yaw, buffer(30,4))
  90. end
  91. }
  92. end
  93. -- TOSERVER_GOTBLOCKS
  94. do
  95. local f_count = ProtoField.uint8("minetest.client.gotblocks_count", "Count", base.DEC)
  96. local f_block = ProtoField.bytes("minetest.client.gotblocks_block", "Block", base.NONE)
  97. local f_x = ProtoField.int16("minetest.client.gotblocks_x", "Block position X", base.DEC)
  98. local f_y = ProtoField.int16("minetest.client.gotblocks_y", "Block position Y", base.DEC)
  99. local f_z = ProtoField.int16("minetest.client.gotblocks_z", "Block position Z", base.DEC)
  100. minetest_client_commands[0x24] = {
  101. "GOTBLOCKS", 3,
  102. { f_count, f_block, f_x, f_y, f_z },
  103. function(buffer, pinfo, tree, t)
  104. t:add(f_count, buffer(2,1))
  105. local count = buffer(2,1):uint()
  106. if minetest_check_length(buffer, 3 + 6*count, t) then
  107. pinfo.cols.info:append(" * " .. count)
  108. local index
  109. for index = 0, count - 1 do
  110. local pos = 3 + 6*index
  111. local t2 = t:add(f_block, buffer(pos, 6))
  112. t2:set_text("Block, X: " .. buffer(pos, 2):int()
  113. .. ", Y: " .. buffer(pos + 2, 2):int()
  114. .. ", Z: " .. buffer(pos + 4, 2):int())
  115. t2:add(f_x, buffer(pos, 2))
  116. t2:add(f_y, buffer(pos + 2, 2))
  117. t2:add(f_z, buffer(pos + 4, 2))
  118. end
  119. end
  120. end
  121. }
  122. end
  123. -- TOSERVER_DELETEDBLOCKS
  124. -- TODO: Test this
  125. do
  126. local f_count = ProtoField.uint8("minetest.client.deletedblocks_count", "Count", base.DEC)
  127. local f_block = ProtoField.bytes("minetest.client.deletedblocks_block", "Block", base.NONE)
  128. local f_x = ProtoField.int16("minetest.client.deletedblocks_x", "Block position X", base.DEC)
  129. local f_y = ProtoField.int16("minetest.client.deletedblocks_y", "Block position Y", base.DEC)
  130. local f_z = ProtoField.int16("minetest.client.deletedblocks_z", "Block position Z", base.DEC)
  131. minetest_client_commands[0x25] = {
  132. "DELETEDBLOCKS", 3,
  133. { f_count, f_block, f_x, f_y, f_z },
  134. function(buffer, pinfo, tree, t)
  135. t:add(f_count, buffer(2,1))
  136. local count = buffer(2,1):uint()
  137. if minetest_check_length(buffer, 3 + 6*count, t) then
  138. pinfo.cols.info:append(" * " .. count)
  139. local index
  140. for index = 0, count - 1 do
  141. local pos = 3 + 6*index
  142. local t2 = t:add(f_block, buffer(pos, 6))
  143. t2:set_text("Block, X: " .. buffer(pos, 2):int()
  144. .. ", Y: " .. buffer(pos + 2, 2):int()
  145. .. ", Z: " .. buffer(pos + 4, 2):int())
  146. t2:add(f_x, buffer(pos, 2))
  147. t2:add(f_y, buffer(pos + 2, 2))
  148. t2:add(f_z, buffer(pos + 4, 2))
  149. end
  150. end
  151. end
  152. }
  153. end
  154. -- TOSERVER_ADDNODE_FROM_INVENTORY (obsolete)
  155. minetest_client_commands[0x26] = { "ADDNODE_FROM_INVENTORY", 2 }
  156. minetest_client_obsolete[0x26] = true
  157. -- TOSERVER_CLICK_OBJECT
  158. -- TODO: Test this
  159. do
  160. local vs_button = {
  161. [0] = "left",
  162. [1] = "right"
  163. }
  164. local f_button = ProtoField.uint8("minetest.client.click_object_button", "Button", base.DEC, vs_button)
  165. local f_blockpos_x = ProtoField.int16("minetest.client.click_object_blockpos_x", "Block position X", base.DEC)
  166. local f_blockpos_y = ProtoField.int16("minetest.client.click_object_blockpos_y", "Block position Y", base.DEC)
  167. local f_blockpos_z = ProtoField.int16("minetest.client.click_object_blockpos_z", "Block position Z", base.DEC)
  168. local f_id = ProtoField.int16("minetest.client.click_object_id", "ID", base.DEC)
  169. local f_item = ProtoField.uint16("minetest.client.click_object_item", "Item", base.DEC)
  170. minetest_client_commands[0x27] = {
  171. "CLICK_OBJECT", 13,
  172. { f_button, f_blockpos_x, f_blockpos_y, f_blockpos_z, f_id, f_item },
  173. function(buffer, pinfo, tree, t)
  174. t:add(f_button, buffer(2,1))
  175. t:add(f_blockpos_x, buffer(3,2))
  176. t:add(f_blockpos_y, buffer(5,2))
  177. t:add(f_blockpos_z, buffer(7,2))
  178. t:add(f_id, buffer(9,2))
  179. t:add(f_item, buffer(11,2))
  180. end
  181. }
  182. end
  183. -- TOSERVER_GROUND_ACTION
  184. do
  185. local vs_action = {
  186. [0] = "Start digging",
  187. [1] = "Place block",
  188. [2] = "Stop digging",
  189. [3] = "Digging completed"
  190. }
  191. local f_action = ProtoField.uint8("minetest.client.ground_action", "Action", base.DEC, vs_action)
  192. local f_nodepos_undersurface_x = ProtoField.int16(
  193. "minetest.client.ground_action_nodepos_undersurface_x",
  194. "Node position (under surface) X")
  195. local f_nodepos_undersurface_y = ProtoField.int16(
  196. "minetest.client.ground_action_nodepos_undersurface_y",
  197. "Node position (under surface) Y")
  198. local f_nodepos_undersurface_z = ProtoField.int16(
  199. "minetest.client.ground_action_nodepos_undersurface_z",
  200. "Node position (under surface) Z")
  201. local f_nodepos_abovesurface_x = ProtoField.int16(
  202. "minetest.client.ground_action_nodepos_abovesurface_x",
  203. "Node position (above surface) X")
  204. local f_nodepos_abovesurface_y = ProtoField.int16(
  205. "minetest.client.ground_action_nodepos_abovesurface_y",
  206. "Node position (above surface) Y")
  207. local f_nodepos_abovesurface_z = ProtoField.int16(
  208. "minetest.client.ground_action_nodepos_abovesurface_z",
  209. "Node position (above surface) Z")
  210. local f_item = ProtoField.uint16("minetest.client.ground_action_item", "Item")
  211. minetest_client_commands[0x28] = {
  212. "GROUND_ACTION", 17,
  213. { f_action,
  214. f_nodepos_undersurface_x,
  215. f_nodepos_undersurface_y,
  216. f_nodepos_undersurface_z,
  217. f_nodepos_abovesurface_x,
  218. f_nodepos_abovesurface_y,
  219. f_nodepos_abovesurface_z,
  220. f_item },
  221. function(buffer, pinfo, tree, t)
  222. t:add(f_action, buffer(2,1))
  223. t:add(f_nodepos_undersurface_x, buffer(3,2))
  224. t:add(f_nodepos_undersurface_y, buffer(5,2))
  225. t:add(f_nodepos_undersurface_z, buffer(7,2))
  226. t:add(f_nodepos_abovesurface_x, buffer(9,2))
  227. t:add(f_nodepos_abovesurface_y, buffer(11,2))
  228. t:add(f_nodepos_abovesurface_z, buffer(13,2))
  229. t:add(f_item, buffer(15,2))
  230. end
  231. }
  232. end
  233. -- TOSERVER_RELEASE (obsolete)
  234. minetest_client_commands[0x29] = { "RELEASE", 2 }
  235. minetest_client_obsolete[0x29] = true
  236. -- TOSERVER_SIGNTEXT (old signs)
  237. -- TODO: Test this or mark obsolete
  238. do
  239. local f_blockpos_x = ProtoField.int16("minetest.client.signtext_blockpos_x", "Block position X", base.DEC)
  240. local f_blockpos_y = ProtoField.int16("minetest.client.signtext_blockpos_y", "Block position Y", base.DEC)
  241. local f_blockpos_z = ProtoField.int16("minetest.client.signtext_blockpos_z", "Block position Z", base.DEC)
  242. local f_id = ProtoField.int16("minetest.client.signtext_id", "ID", base.DEC)
  243. local f_textlen = ProtoField.uint16("minetest.client.signtext_textlen", "Text length", base.DEC)
  244. local f_text = ProtoField.string("minetest.client.signtext_text", "Text")
  245. minetest_client_commands[0x30] = {
  246. "SIGNTEXT", 12,
  247. { f_blockpos_x, f_blockpos_y, f_blockpos_z, f_id, f_textlen, f_text },
  248. function(buffer, pinfo, tree, t)
  249. t:add(f_blockpos_x, buffer(2,2))
  250. t:add(f_blockpos_y, buffer(4,2))
  251. t:add(f_blockpos_z, buffer(6,2))
  252. t:add(f_id, buffer(8,2))
  253. t:add(f_textlen, buffer(10,2))
  254. local textlen = buffer(10,2):uint()
  255. if minetest_check_length(buffer, 12 + textlen, t) then
  256. t:add(f_text, buffer, buffer(12,textlen))
  257. end
  258. end
  259. }
  260. end
  261. -- TOSERVER_INVENTORY_ACTION
  262. do
  263. local f_action = ProtoField.string("minetest.client.inventory_action", "Action")
  264. minetest_client_commands[0x31] = {
  265. "INVENTORY_ACTION", 2,
  266. { f_action },
  267. function(buffer, pinfo, tree, t)
  268. t:add(f_action, buffer(2, buffer:len() - 2))
  269. end
  270. }
  271. end
  272. -- TOSERVER_CHAT_MESSAGE
  273. do
  274. local f_length = ProtoField.uint16("minetest.client.chat_message_length", "Length", base.DEC)
  275. local f_message = ProtoField.string("minetest.client.chat_message", "Message")
  276. minetest_client_commands[0x32] = {
  277. "CHAT_MESSAGE", 4,
  278. { f_length, f_message },
  279. function(buffer, pinfo, tree, t)
  280. t:add(f_length, buffer(2,2))
  281. local textlen = buffer(2,2):uint()
  282. if minetest_check_length(buffer, 4 + textlen*2, t) then
  283. t:add(f_message, minetest_convert_utf16(buffer(4, textlen*2), "Converted chat message"))
  284. end
  285. end
  286. }
  287. end
  288. -- TOSERVER_SIGNNODETEXT
  289. do
  290. local f_pos_x = ProtoField.int16("minetest.client.signnodetext_pos_x", "Block position X", base.DEC)
  291. local f_pos_y = ProtoField.int16("minetest.client.signnodetext_pos_y", "Block position Y", base.DEC)
  292. local f_pos_z = ProtoField.int16("minetest.client.signnodetext_pos_z", "Block position Z", base.DEC)
  293. local f_textlen = ProtoField.uint16("minetest.client.signnodetext_textlen", "Text length", base.DEC)
  294. local f_text = ProtoField.string("minetest.client.signnodetext_text", "Text")
  295. minetest_client_commands[0x33] = {
  296. "SIGNNODETEXT", 10,
  297. { f_pos_x, f_pos_y, f_pos_z, f_textlen, f_text },
  298. function(buffer, pinfo, tree, t)
  299. t:add(f_pos_x, buffer(2,2))
  300. t:add(f_pos_y, buffer(4,2))
  301. t:add(f_pos_z, buffer(6,2))
  302. t:add(f_textlen, buffer(8,2))
  303. local textlen = buffer(8,2):uint()
  304. if minetest_check_length(buffer, 10 + textlen, t) then
  305. t:add(f_text, buffer(10, textlen))
  306. end
  307. end
  308. }
  309. end
  310. -- TOSERVER_CLICK_ACTIVEOBJECT
  311. do
  312. local vs_button = {
  313. [0] = "left",
  314. [1] = "right"
  315. }
  316. local f_button = ProtoField.uint8("minetest.client.click_activeobject_button", "Button", base.DEC, vs_button)
  317. local f_id = ProtoField.uint16("minetest.client.click_activeobject_id", "ID", base.DEC)
  318. local f_item = ProtoField.uint16("minetest.client.click_activeobject_item", "Item", base.DEC)
  319. minetest_client_commands[0x34] = {
  320. "CLICK_ACTIVEOBJECT", 7,
  321. { f_button, f_id, f_item },
  322. function(buffer, pinfo, tree, t)
  323. t:add(f_button, buffer(2,1))
  324. t:add(f_id, buffer(3,2))
  325. t:add(f_item, buffer(5,2))
  326. end
  327. }
  328. end
  329. -- TOSERVER_DAMAGE
  330. do
  331. local f_amount = ProtoField.uint8("minetest.client.damage_amount", "Amount", base.DEC)
  332. minetest_client_commands[0x35] = {
  333. "DAMAGE", 3,
  334. { f_amount },
  335. function(buffer, pinfo, tree, t)
  336. t:add(f_amount, buffer(2,1))
  337. end
  338. }
  339. end
  340. -- TOSERVER_PASSWORD
  341. do
  342. local f_old_password = ProtoField.string("minetest.client.password_old", "Old password")
  343. local f_new_password = ProtoField.string("minetest.client.password_new", "New password")
  344. minetest_client_commands[0x36] = {
  345. "PASSWORD", 58,
  346. { f_old_password, f_new_password },
  347. function(buffer, pinfo, tree, t)
  348. t:add(f_old_password, buffer(2,28))
  349. t:add(f_new_password, buffer(30,28))
  350. end
  351. }
  352. end
  353. -- TOSERVER_PLAYERITEM
  354. do
  355. local f_item = ProtoField.uint16("minetest.client.playeritem_item", "Wielded item")
  356. minetest_client_commands[0x37] = {
  357. "PLAYERITEM", 4,
  358. { f_item },
  359. function(buffer, pinfo, tree, t)
  360. t:add(f_item, buffer(2,2))
  361. end
  362. }
  363. end
  364. -- TOSERVER_RESPAWN
  365. minetest_client_commands[0x38] = { "RESPAWN", 2 }
  366. minetest_client_commands[0x39] = { "INTERACT", 2 }
  367. minetest_client_commands[0x3a] = { "REMOVED_SOUNDS", 2 }
  368. minetest_client_commands[0x3b] = { "NODEMETA_FIELDS", 2 }
  369. minetest_client_commands[0x3c] = { "INVENTORY_FIELDS", 2 }
  370. minetest_client_commands[0x40] = { "REQUEST_MEDIA", 2 }
  371. minetest_client_commands[0x41] = { "RECEIVED_MEDIA", 2 }
  372. minetest_client_commands[0x42] = { "BREATH", 2 }
  373. minetest_client_commands[0x43] = { "CLIENT_READY", 2 }
  374. minetest_client_commands[0x50] = { "FIRST_SRP", 2 }
  375. minetest_client_commands[0x51] = { "SRP_BYTES_A", 2 }
  376. minetest_client_commands[0x52] = { "SRP_BYTES_M", 2 }
  377. --------------------------------------------
  378. -- Part 2 --
  379. -- Server command dissectors (TOCLIENT_*) --
  380. --------------------------------------------
  381. minetest_server_commands = {}
  382. minetest_server_obsolete = {}
  383. -- TOCLIENT_INIT
  384. minetest_server_commands[0x02] = {"HELLO", 2}
  385. minetest_server_commands[0x03] = {"AUTH_ACCEPT", 2}
  386. minetest_server_commands[0x04] = {"ACCEPT_SUDO_MODE", 2}
  387. minetest_server_commands[0x05] = {"DENY_SUDO_MODE", 2}
  388. minetest_server_commands[0x0A] = {"ACCESS_DENIED", 2}
  389. do
  390. local f_version = ProtoField.uint8("minetest.server.init_version", "Deployed version", base.DEC)
  391. local f_pos_x = ProtoField.int16("minetest.server.init_pos_x", "Position X", base.DEC)
  392. local f_pos_y = ProtoField.int16("minetest.server.init_pos_y", "Position Y", base.DEC)
  393. local f_pos_z = ProtoField.int16("minetest.server.init_pos_x", "Position Z", base.DEC)
  394. local f_map_seed = ProtoField.uint64("minetest.server.init_map_seed", "Map seed", base.DEC)
  395. minetest_server_commands[0x10] = {
  396. "INIT", 17,
  397. { f_version, f_pos_x, f_pos_y, f_pos_z, f_map_seed },
  398. function(buffer, pinfo, tree, t)
  399. t:add(f_version, buffer(2,1))
  400. t:add(f_pos_x, buffer(3,2))
  401. t:add(f_pos_y, buffer(5,2))
  402. t:add(f_pos_z, buffer(7,2))
  403. t:add(f_map_seed, buffer(9,8))
  404. end
  405. }
  406. end
  407. -- TOCLIENT_BLOCKDATA
  408. do
  409. local f_x = ProtoField.int16("minetest.server.blockdata_x", "Block position X", base.DEC)
  410. local f_y = ProtoField.int16("minetest.server.blockdata_y", "Block position Y", base.DEC)
  411. local f_z = ProtoField.int16("minetest.server.blockdata_z", "Block position Z", base.DEC)
  412. local f_data = ProtoField.bytes("minetest.server.blockdata_block", "Serialized MapBlock")
  413. minetest_server_commands[0x20] = {
  414. "BLOCKDATA", 8,
  415. { f_x, f_y, f_z, f_data },
  416. function(buffer, pinfo, tree, t)
  417. t:add(f_x, buffer(2,2))
  418. t:add(f_y, buffer(4,2))
  419. t:add(f_z, buffer(6,2))
  420. t:add(f_data, buffer(8, buffer:len() - 8))
  421. end
  422. }
  423. end
  424. -- TOCLIENT_ADDNODE
  425. do
  426. local f_x = ProtoField.int16("minetest.server.addnode_x", "Position X", base.DEC)
  427. local f_y = ProtoField.int16("minetest.server.addnode_y", "Position Y", base.DEC)
  428. local f_z = ProtoField.int16("minetest.server.addnode_z", "Position Z", base.DEC)
  429. local f_data = ProtoField.bytes("minetest.server.addnode_node", "Serialized MapNode")
  430. minetest_server_commands[0x21] = {
  431. "ADDNODE", 8,
  432. { f_x, f_y, f_z, f_data },
  433. function(buffer, pinfo, tree, t)
  434. t:add(f_x, buffer(2,2))
  435. t:add(f_y, buffer(4,2))
  436. t:add(f_z, buffer(6,2))
  437. t:add(f_data, buffer(8, buffer:len() - 8))
  438. end
  439. }
  440. end
  441. -- TOCLIENT_REMOVENODE
  442. do
  443. local f_x = ProtoField.int16("minetest.server.removenode_x", "Position X", base.DEC)
  444. local f_y = ProtoField.int16("minetest.server.removenode_y", "Position Y", base.DEC)
  445. local f_z = ProtoField.int16("minetest.server.removenode_z", "Position Z", base.DEC)
  446. minetest_server_commands[0x22] = {
  447. "REMOVENODE", 8,
  448. { f_x, f_y, f_z },
  449. function(buffer, pinfo, tree, t)
  450. t:add(f_x, buffer(2,2))
  451. t:add(f_y, buffer(4,2))
  452. t:add(f_z, buffer(6,2))
  453. end
  454. }
  455. end
  456. -- TOCLIENT_PLAYERPOS (obsolete)
  457. minetest_server_commands[0x23] = { "PLAYERPOS", 2 }
  458. minetest_server_obsolete[0x23] = true
  459. -- TOCLIENT_PLAYERINFO
  460. do
  461. local f_count = ProtoField.uint16("minetest.server.playerinfo_count", "Count", base.DEC)
  462. local f_player = ProtoField.bytes("minetest.server.playerinfo_player", "Player", base.NONE)
  463. local f_peer_id = ProtoField.uint16("minetest.server.playerinfo_peer_id", "Peer ID", base.DEC)
  464. local f_name = ProtoField.string("minetest.server.playerinfo_name", "Name")
  465. minetest_server_commands[0x24] = {
  466. "PLAYERINFO", 2,
  467. { f_count, f_player, f_peer_id, f_name },
  468. function(buffer, pinfo, tree, t)
  469. local count = 0
  470. local pos, index
  471. for pos = 2, buffer:len() - 22, 22 do -- does lua have integer division?
  472. count = count + 1
  473. end
  474. t:add(f_count, count):set_generated()
  475. t:set_len(2 + 22 * count)
  476. pinfo.cols.info:append(" * " .. count)
  477. for index = 0, count - 1 do
  478. local pos = 2 + 22 * index
  479. local t2 = t:add(f_player, buffer(pos, 22))
  480. t2:set_text("Player, ID: " .. buffer(pos, 2):uint()
  481. .. ", Name: " .. buffer(pos + 2, 20):string())
  482. t2:add(f_peer_id, buffer(pos, 2))
  483. t2:add(f_name, buffer(pos + 2, 20))
  484. end
  485. end
  486. }
  487. end
  488. -- TOCLIENT_OPT_BLOCK_NOT_FOUND (obsolete)
  489. minetest_server_commands[0x25] = { "OPT_BLOCK_NOT_FOUND", 2 }
  490. minetest_server_obsolete[0x25] = true
  491. -- TOCLIENT_SECTORMETA (obsolete)
  492. minetest_server_commands[0x26] = { "SECTORMETA", 2 }
  493. minetest_server_obsolete[0x26] = true
  494. -- TOCLIENT_INVENTORY
  495. do
  496. local f_inventory = ProtoField.string("minetest.server.inventory", "Inventory")
  497. minetest_server_commands[0x27] = {
  498. "INVENTORY", 2,
  499. { f_inventory },
  500. function(buffer, pinfo, tree, t)
  501. t:add(f_inventory, buffer(2, buffer:len() - 2))
  502. end
  503. }
  504. end
  505. -- TOCLIENT_OBJECTDATA
  506. do
  507. local f_player_count = ProtoField.uint16("minetest.server.objectdata_player_count",
  508. "Count of player positions", base.DEC)
  509. local f_player = ProtoField.bytes("minetest.server.objectdata_player", "Player position")
  510. local f_peer_id = ProtoField.uint16("minetest.server.objectdata_player_peer_id", "Peer ID")
  511. local f_x = ProtoField.int32("minetest.server.objectdata_player_x", "Position X", base.DEC)
  512. local f_y = ProtoField.int32("minetest.server.objectdata_player_y", "Position Y", base.DEC)
  513. local f_z = ProtoField.int32("minetest.server.objectdata_player_z", "Position Z", base.DEC)
  514. local f_speed_x = ProtoField.int32("minetest.server.objectdata_player_speed_x", "Speed X", base.DEC)
  515. local f_speed_y = ProtoField.int32("minetest.server.objectdata_player_speed_y", "Speed Y", base.DEC)
  516. local f_speed_z = ProtoField.int32("minetest.server.objectdata_player_speed_z", "Speed Z", base.DEC)
  517. local f_pitch = ProtoField.int32("minetest.server.objectdata_player_pitch", "Pitch", base.DEC)
  518. local f_yaw = ProtoField.int32("minetest.server.objectdata_player_yaw", "Yaw", base.DEC)
  519. local f_block_count = ProtoField.uint16("minetest.server.objectdata_block_count",
  520. "Count of blocks", base.DEC)
  521. minetest_server_commands[0x28] = {
  522. "OBJECTDATA", 6,
  523. { f_player_count, f_player, f_peer_id, f_x, f_y, f_z,
  524. f_speed_x, f_speed_y, f_speed_z,f_pitch, f_yaw,
  525. f_block_count },
  526. function(buffer, pinfo, tree, t)
  527. local t2, index, pos
  528. local player_count_pos = 2
  529. local player_count = buffer(player_count_pos, 2):uint()
  530. t:add(f_player_count, buffer(player_count_pos, 2))
  531. local block_count_pos = player_count_pos + 2 + 34 * player_count
  532. if not minetest_check_length(buffer, block_count_pos + 2, t) then
  533. return
  534. end
  535. for index = 0, player_count - 1 do
  536. pos = player_count_pos + 2 + 34 * index
  537. t2 = t:add(f_player, buffer(pos, 34))
  538. t2:set_text("Player position, ID: " .. buffer(pos, 2):uint())
  539. t2:add(f_peer_id, buffer(pos, 2))
  540. t2:add(f_x, buffer(pos + 2, 4))
  541. t2:add(f_y, buffer(pos + 6, 4))
  542. t2:add(f_z, buffer(pos + 10, 4))
  543. t2:add(f_speed_x, buffer(pos + 14, 4))
  544. t2:add(f_speed_y, buffer(pos + 18, 4))
  545. t2:add(f_speed_z, buffer(pos + 22, 4))
  546. t2:add(f_pitch, buffer(pos + 26, 4))
  547. t2:add(f_yaw, buffer(pos + 30, 4))
  548. end
  549. local block_count = buffer(block_count_pos, 2):uint()
  550. t:add(f_block_count, buffer(block_count_pos, 2))
  551. -- TODO: dissect blocks.
  552. -- NOTE: block_count > 0 is obsolete. (?)
  553. pinfo.cols.info:append(" * " .. (player_count + block_count))
  554. end
  555. }
  556. end
  557. -- TOCLIENT_TIME_OF_DAY
  558. do
  559. local f_time = ProtoField.uint16("minetest.server.time_of_day", "Time", base.DEC)
  560. minetest_server_commands[0x29] = {
  561. "TIME_OF_DAY", 4,
  562. { f_time },
  563. function(buffer, pinfo, tree, t)
  564. t:add(f_time, buffer(2,2))
  565. end
  566. }
  567. end
  568. -- TOCLIENT_CHAT_MESSAGE
  569. do
  570. local f_length = ProtoField.uint16("minetest.server.chat_message_length", "Length", base.DEC)
  571. local f_message = ProtoField.string("minetest.server.chat_message", "Message")
  572. minetest_server_commands[0x30] = {
  573. "CHAT_MESSAGE", 4,
  574. { f_length, f_message },
  575. function(buffer, pinfo, tree, t)
  576. t:add(f_length, buffer(2,2))
  577. local textlen = buffer(2,2):uint()
  578. if minetest_check_length(buffer, 4 + textlen*2, t) then
  579. t:add(f_message, minetest_convert_utf16(buffer(4, textlen*2), "Converted chat message"))
  580. end
  581. end
  582. }
  583. end
  584. -- TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD
  585. do
  586. local f_removed_count = ProtoField.uint16(
  587. "minetest.server.active_object_remove_add_removed_count",
  588. "Count of removed objects", base.DEC)
  589. local f_removed = ProtoField.bytes(
  590. "minetest.server.active_object_remove_add_removed",
  591. "Removed object")
  592. local f_removed_id = ProtoField.uint16(
  593. "minetest.server.active_object_remove_add_removed_id",
  594. "ID", base.DEC)
  595. local f_added_count = ProtoField.uint16(
  596. "minetest.server.active_object_remove_add_added_count",
  597. "Count of added objects", base.DEC)
  598. local f_added = ProtoField.bytes(
  599. "minetest.server.active_object_remove_add_added",
  600. "Added object")
  601. local f_added_id = ProtoField.uint16(
  602. "minetest.server.active_object_remove_add_added_id",
  603. "ID", base.DEC)
  604. local f_added_type = ProtoField.uint8(
  605. "minetest.server.active_object_remove_add_added_type",
  606. "Type", base.DEC)
  607. local f_added_init_length = ProtoField.uint32(
  608. "minetest.server.active_object_remove_add_added_init_length",
  609. "Initialization data length", base.DEC)
  610. local f_added_init_data = ProtoField.bytes(
  611. "minetest.server.active_object_remove_add_added_init_data",
  612. "Initialization data")
  613. minetest_server_commands[0x31] = {
  614. "ACTIVE_OBJECT_REMOVE_ADD", 6,
  615. { f_removed_count, f_removed, f_removed_id,
  616. f_added_count, f_added, f_added_id,
  617. f_added_type, f_added_init_length, f_added_init_data },
  618. function(buffer, pinfo, tree, t)
  619. local t2, index, pos
  620. local removed_count_pos = 2
  621. local removed_count = buffer(removed_count_pos, 2):uint()
  622. t:add(f_removed_count, buffer(removed_count_pos, 2))
  623. local added_count_pos = removed_count_pos + 2 + 2 * removed_count
  624. if not minetest_check_length(buffer, added_count_pos + 2, t) then
  625. return
  626. end
  627. -- Loop through removed active objects
  628. for index = 0, removed_count - 1 do
  629. pos = removed_count_pos + 2 + 2 * index
  630. t2 = t:add(f_removed, buffer(pos, 2))
  631. t2:set_text("Removed object, ID = " .. buffer(pos, 2):uint())
  632. t2:add(f_removed_id, buffer(pos, 2))
  633. end
  634. local added_count = buffer(added_count_pos, 2):uint()
  635. t:add(f_added_count, buffer(added_count_pos, 2))
  636. -- Loop through added active objects
  637. pos = added_count_pos + 2
  638. for index = 0, added_count - 1 do
  639. if not minetest_check_length(buffer, pos + 7, t) then
  640. return
  641. end
  642. local init_length = buffer(pos + 3, 4):uint()
  643. if not minetest_check_length(buffer, pos + 7 + init_length, t) then
  644. return
  645. end
  646. t2 = t:add(f_added, buffer(pos, 7 + init_length))
  647. t2:set_text("Added object, ID = " .. buffer(pos, 2):uint())
  648. t2:add(f_added_id, buffer(pos, 2))
  649. t2:add(f_added_type, buffer(pos + 2, 1))
  650. t2:add(f_added_init_length, buffer(pos + 3, 4))
  651. t2:add(f_added_init_data, buffer(pos + 7, init_length))
  652. pos = pos + 7 + init_length
  653. end
  654. pinfo.cols.info:append(" * " .. (removed_count + added_count))
  655. end
  656. }
  657. end
  658. -- TOCLIENT_ACTIVE_OBJECT_MESSAGES
  659. do
  660. local f_object_count = ProtoField.uint16(
  661. "minetest.server.active_object_messages_object_count",
  662. "Count of objects", base.DEC)
  663. local f_object = ProtoField.bytes(
  664. "minetest.server.active_object_messages_object",
  665. "Object")
  666. local f_object_id = ProtoField.uint16(
  667. "minetest.server.active_object_messages_id",
  668. "ID", base.DEC)
  669. local f_message_length = ProtoField.uint16(
  670. "minetest.server.active_object_messages_message_length",
  671. "Message length", base.DEC)
  672. local f_message = ProtoField.bytes(
  673. "minetest.server.active_object_messages_message",
  674. "Message")
  675. minetest_server_commands[0x32] = {
  676. "ACTIVE_OBJECT_MESSAGES", 2,
  677. { f_object_count, f_object, f_object_id, f_message_length, f_message },
  678. function(buffer, pinfo, tree, t)
  679. local t2, count, pos, message_length
  680. count = 0
  681. pos = 2
  682. while pos < buffer:len() do
  683. if not minetest_check_length(buffer, pos + 4, t) then
  684. return
  685. end
  686. message_length = buffer(pos + 2, 2):uint()
  687. if not minetest_check_length(buffer, pos + 4 + message_length, t) then
  688. return
  689. end
  690. count = count + 1
  691. pos = pos + 4 + message_length
  692. end
  693. pinfo.cols.info:append(" * " .. count)
  694. t:add(f_object_count, count):set_generated()
  695. pos = 2
  696. while pos < buffer:len() do
  697. message_length = buffer(pos + 2, 2):uint()
  698. t2 = t:add(f_object, buffer(pos, 4 + message_length))
  699. t2:set_text("Object, ID = " .. buffer(pos, 2):uint())
  700. t2:add(f_object_id, buffer(pos, 2))
  701. t2:add(f_message_length, buffer(pos + 2, 2))
  702. t2:add(f_message, buffer(pos + 4, message_length))
  703. pos = pos + 4 + message_length
  704. end
  705. end
  706. }
  707. end
  708. -- TOCLIENT_HP
  709. do
  710. local f_hp = ProtoField.uint8("minetest.server.hp", "Hitpoints", base.DEC)
  711. minetest_server_commands[0x33] = {
  712. "HP", 3,
  713. { f_hp },
  714. function(buffer, pinfo, tree, t)
  715. t:add(f_hp, buffer(2,1))
  716. end
  717. }
  718. end
  719. -- TOCLIENT_MOVE_PLAYER
  720. do
  721. local f_x = ProtoField.int32("minetest.server.move_player_x", "Position X", base.DEC)
  722. local f_y = ProtoField.int32("minetest.server.move_player_y", "Position Y", base.DEC)
  723. local f_z = ProtoField.int32("minetest.server.move_player_z", "Position Z", base.DEC)
  724. local f_pitch = ProtoField.int32("minetest.server.move_player_pitch", "Pitch", base.DEC)
  725. local f_yaw = ProtoField.int32("minetest.server.move_player_yaw", "Yaw", base.DEC)
  726. local f_garbage = ProtoField.bytes("minetest.server.move_player_garbage", "Garbage")
  727. minetest_server_commands[0x34] = {
  728. "MOVE_PLAYER", 18, -- actually 22, but see below
  729. { f_x, f_y, f_z, f_pitch, f_yaw, f_garbage },
  730. function(buffer, pinfo, tree, t)
  731. t:add(f_x, buffer(2, 4))
  732. t:add(f_y, buffer(6, 4))
  733. t:add(f_z, buffer(10, 4))
  734. -- Compatibility note:
  735. -- Up to 2011-08-23, there was a bug in Minetest that
  736. -- caused the server to serialize the pitch and yaw
  737. -- with 2 bytes each instead of 4, creating a
  738. -- malformed message.
  739. if buffer:len() >= 22 then
  740. t:add(f_pitch, buffer(14, 4))
  741. t:add(f_yaw, buffer(18, 4))
  742. else
  743. t:add(f_garbage, buffer(14, 4))
  744. t:add_expert_info(PI_MALFORMED, PI_WARN, "Malformed pitch and yaw, possibly caused by a serialization bug in Minetest")
  745. end
  746. end
  747. }
  748. end
  749. -- TOCLIENT_ACCESS_DENIED
  750. do
  751. local f_reason_length = ProtoField.uint16("minetest.server.access_denied_reason_length", "Reason length", base.DEC)
  752. local f_reason = ProtoField.string("minetest.server.access_denied_reason", "Reason")
  753. minetest_server_commands[0x35] = {
  754. "ACCESS_DENIED", 4,
  755. { f_reason_length, f_reason },
  756. function(buffer, pinfo, tree, t)
  757. t:add(f_reason_length, buffer(2,2))
  758. local reason_length = buffer(2,2):uint()
  759. if minetest_check_length(buffer, 4 + reason_length * 2, t) then
  760. t:add(f_reason, minetest_convert_utf16(buffer(4, reason_length * 2), "Converted reason message"))
  761. end
  762. end
  763. }
  764. end
  765. -- TOCLIENT_PLAYERITEM
  766. do
  767. local f_count = ProtoField.uint16(
  768. "minetest.server.playeritem_count",
  769. "Count of players", base.DEC)
  770. local f_player = ProtoField.bytes(
  771. "minetest.server.playeritem_player",
  772. "Player")
  773. local f_peer_id = ProtoField.uint16(
  774. "minetest.server.playeritem_peer_id",
  775. "Peer ID", base.DEC)
  776. local f_item_length = ProtoField.uint16(
  777. "minetest.server.playeritem_item_length",
  778. "Item information length", base.DEC)
  779. local f_item = ProtoField.string(
  780. "minetest.server.playeritem_item",
  781. "Item information")
  782. minetest_server_commands[0x36] = {
  783. "PLAYERITEM", 4,
  784. { f_count, f_player, f_peer_id, f_item_length, f_item },
  785. function(buffer, pinfo, tree, t)
  786. local count, index, pos, item_length
  787. count = buffer(2,2):uint()
  788. pinfo.cols.info:append(" * " .. count)
  789. t:add(f_count, buffer(2,2))
  790. pos = 4
  791. for index = 0, count - 1 do
  792. if not minetest_check_length(buffer, pos + 4, t) then
  793. return
  794. end
  795. item_length = buffer(pos + 2, 2):uint()
  796. if not minetest_check_length(buffer, pos + 4 + item_length, t) then
  797. return
  798. end
  799. local t2 = t:add(f_player, buffer(pos, 4 + item_length))
  800. t2:set_text("Player, ID: " .. buffer(pos, 2):uint())
  801. t2:add(f_peer_id, buffer(pos, 2))
  802. t2:add(f_item_length, buffer(pos + 2, 2))
  803. t2:add(f_item, buffer(pos + 4, item_length))
  804. pos = pos + 4 + item_length
  805. end
  806. end
  807. }
  808. end
  809. -- TOCLIENT_DEATHSCREEN
  810. do
  811. local vs_set_camera_point_target = {
  812. [0] = "False",
  813. [1] = "True"
  814. }
  815. local f_set_camera_point_target = ProtoField.uint8(
  816. "minetest.server.deathscreen_set_camera_point_target",
  817. "Set camera point target", base.DEC, vs_set_camera_point_target)
  818. local f_camera_point_target_x = ProtoField.int32(
  819. "minetest.server.deathscreen_camera_point_target_x",
  820. "Camera point target X", base.DEC)
  821. local f_camera_point_target_y = ProtoField.int32(
  822. "minetest.server.deathscreen_camera_point_target_y",
  823. "Camera point target Y", base.DEC)
  824. local f_camera_point_target_z = ProtoField.int32(
  825. "minetest.server.deathscreen_camera_point_target_z",
  826. "Camera point target Z", base.DEC)
  827. minetest_server_commands[0x37] = {
  828. "DEATHSCREEN", 15,
  829. { f_set_camera_point_target, f_camera_point_target_x,
  830. f_camera_point_target_y, f_camera_point_target_z},
  831. function(buffer, pinfo, tree, t)
  832. t:add(f_set_camera_point_target, buffer(2,1))
  833. t:add(f_camera_point_target_x, buffer(3,4))
  834. t:add(f_camera_point_target_y, buffer(7,4))
  835. t:add(f_camera_point_target_z, buffer(11,4))
  836. end
  837. }
  838. end
  839. minetest_server_commands[0x38] = {"MEDIA", 2}
  840. minetest_server_commands[0x39] = {"TOOLDEF", 2}
  841. minetest_server_commands[0x3a] = {"NODEDEF", 2}
  842. minetest_server_commands[0x3b] = {"CRAFTITEMDEF", 2}
  843. minetest_server_commands[0x3c] = {"ANNOUNCE_MEDIA", 2}
  844. minetest_server_commands[0x3d] = {"ITEMDEF", 2}
  845. minetest_server_commands[0x3f] = {"PLAY_SOUND", 2}
  846. minetest_server_commands[0x40] = {"STOP_SOUND", 2}
  847. minetest_server_commands[0x41] = {"PRIVILEGES", 2}
  848. minetest_server_commands[0x42] = {"INVENTORY_FORMSPEC", 2}
  849. minetest_server_commands[0x43] = {"DETACHED_INVENTORY", 2}
  850. minetest_server_commands[0x44] = {"SHOW_FORMSPEC", 2}
  851. minetest_server_commands[0x45] = {"MOVEMENT", 2}
  852. minetest_server_commands[0x46] = {"SPAWN_PARTICLE", 2}
  853. minetest_server_commands[0x47] = {"ADD_PARTICLE_SPAWNER", 2}
  854. minetest_server_commands[0x48] = {"DELETE_PARTICLESPAWNER_LEGACY", 2}
  855. minetest_server_commands[0x49] = {"HUDADD", 2}
  856. minetest_server_commands[0x4a] = {"HUDRM", 2}
  857. minetest_server_commands[0x4b] = {"HUDCHANGE", 2}
  858. minetest_server_commands[0x4c] = {"HUD_SET_FLAGS", 2}
  859. minetest_server_commands[0x4d] = {"HUD_SET_PARAM", 2}
  860. minetest_server_commands[0x4e] = {"BREATH", 2}
  861. minetest_server_commands[0x4f] = {"SET_SKY", 2}
  862. minetest_server_commands[0x50] = {"OVERRIDE_DAY_NIGHT_RATIO", 2}
  863. minetest_server_commands[0x51] = {"LOCAL_PLAYER_ANIMATIONS", 2}
  864. minetest_server_commands[0x52] = {"EYE_OFFSET", 2}
  865. minetest_server_commands[0x53] = {"DELETE_PARTICLESPAWNER", 2}
  866. minetest_server_commands[0x54] = {"CLOUD_PARAMS", 2}
  867. minetest_server_commands[0x55] = {"FADE_SOUND", 2}
  868. minetest_server_commands[0x61] = {"SRP_BYTES_S_B", 2}
  869. ------------------------------------
  870. -- Part 3 --
  871. -- Wrapper protocol subdissectors --
  872. ------------------------------------
  873. -- minetest.control dissector
  874. do
  875. local p_control = Proto("minetest.control", "Minetest Control")
  876. local vs_control_type = {
  877. [0] = "Ack",
  878. [1] = "Set Peer ID",
  879. [2] = "Ping",
  880. [3] = "Disco"
  881. }
  882. local f_control_type = ProtoField.uint8("minetest.control.type", "Control Type", base.DEC, vs_control_type)
  883. local f_control_ack = ProtoField.uint16("minetest.control.ack", "ACK sequence number", base.DEC)
  884. local f_control_peerid = ProtoField.uint8("minetest.control.peerid", "New peer ID", base.DEC)
  885. p_control.fields = { f_control_type, f_control_ack, f_control_peerid }
  886. local data_dissector = Dissector.get("data")
  887. function p_control.dissector(buffer, pinfo, tree)
  888. local t = tree:add(p_control, buffer(0,1))
  889. t:add(f_control_type, buffer(0,1))
  890. pinfo.cols.info = "Control message"
  891. local pos = 1
  892. if buffer(0,1):uint() == 0 then
  893. pos = 3
  894. t:set_len(3)
  895. t:add(f_control_ack, buffer(1,2))
  896. pinfo.cols.info = "Ack " .. buffer(1,2):uint()
  897. elseif buffer(0,1):uint() == 1 then
  898. pos = 3
  899. t:set_len(3)
  900. t:add(f_control_peerid, buffer(1,2))
  901. pinfo.cols.info = "Set peer ID " .. buffer(1,2):uint()
  902. elseif buffer(0,1):uint() == 2 then
  903. pinfo.cols.info = "Ping"
  904. elseif buffer(0,1):uint() == 3 then
  905. pinfo.cols.info = "Disco"
  906. end
  907. data_dissector:call(buffer(pos):tvb(), pinfo, tree)
  908. end
  909. end
  910. -- minetest.client dissector
  911. -- minetest.server dissector
  912. -- Defines the minetest.client or minetest.server Proto. These two protocols
  913. -- are created by the same function because they are so similar.
  914. -- Parameter: proto: the Proto object
  915. -- Parameter: this_peer: "Client" or "Server"
  916. -- Parameter: other_peer: "Server" or "Client"
  917. -- Parameter: commands: table of command information, built above
  918. -- Parameter: obsolete: table of obsolete commands, built above
  919. function minetest_define_client_or_server_proto(is_client)
  920. -- Differences between minetest.client and minetest.server
  921. local proto_name, this_peer, other_peer, empty_message_info
  922. local commands, obsolete
  923. if is_client then
  924. proto_name = "minetest.client"
  925. this_peer = "Client"
  926. other_peer = "Server"
  927. empty_message_info = "Empty message / Connect"
  928. commands = minetest_client_commands -- defined in Part 1
  929. obsolete = minetest_client_obsolete -- defined in Part 1
  930. else
  931. proto_name = "minetest.server"
  932. this_peer = "Server"
  933. other_peer = "Client"
  934. empty_message_info = "Empty message"
  935. commands = minetest_server_commands -- defined in Part 2
  936. obsolete = minetest_server_obsolete -- defined in Part 2
  937. end
  938. -- Create the protocol object.
  939. local proto = Proto(proto_name, "Minetest " .. this_peer .. " to " .. other_peer)
  940. -- Create a table vs_command that maps command codes to command names.
  941. local vs_command = {}
  942. local code, command_info
  943. for code, command_info in pairs(commands) do
  944. local command_name = command_info[1]
  945. vs_command[code] = "TO" .. other_peer:upper() .. "_" .. command_name
  946. end
  947. -- Field definitions
  948. local f_command = ProtoField.uint16(proto_name .. ".command", "Command", base.HEX, vs_command)
  949. local f_empty = ProtoField.bool(proto_name .. ".empty", "Is empty", BASE_NONE)
  950. proto.fields = { f_command, f_empty }
  951. -- Add command-specific fields to the protocol
  952. for code, command_info in pairs(commands) do
  953. local command_fields = command_info[3]
  954. if command_fields ~= nil then
  955. local index, field
  956. for index, field in ipairs(command_fields) do
  957. table.insert(proto.fields, field)
  958. end
  959. end
  960. end
  961. -- minetest.client or minetest.server dissector function
  962. function proto.dissector(buffer, pinfo, tree)
  963. local t = tree:add(proto, buffer)
  964. pinfo.cols.info = this_peer
  965. if buffer:len() == 0 then
  966. -- Empty message.
  967. t:add(f_empty, 1):set_generated()
  968. pinfo.cols.info:append(": " .. empty_message_info)
  969. elseif minetest_check_length(buffer, 2, t) then
  970. -- Get the command code.
  971. t:add(f_command, buffer(0,2))
  972. local code = buffer(0,2):uint()
  973. local command_info = commands[code]
  974. if command_info == nil then
  975. -- Error: Unknown command.
  976. pinfo.cols.info:append(": Unknown command")
  977. t:add_expert_info(PI_UNDECODED, PI_WARN, "Unknown " .. this_peer .. " to " .. other_peer .. " command")
  978. else
  979. -- Process a known command
  980. local command_name = command_info[1]
  981. local command_min_length = command_info[2]
  982. local command_fields = command_info[3]
  983. local command_dissector = command_info[4]
  984. if minetest_check_length(buffer, command_min_length, t) then
  985. pinfo.cols.info:append(": " .. command_name)
  986. if command_dissector ~= nil then
  987. command_dissector(buffer, pinfo, tree, t)
  988. end
  989. end
  990. if obsolete[code] then
  991. t:add_expert_info(PI_REQUEST_CODE, PI_WARN, "Obsolete command.")
  992. end
  993. end
  994. end
  995. end
  996. end
  997. minetest_define_client_or_server_proto(true) -- minetest.client
  998. minetest_define_client_or_server_proto(false) -- minetest.server
  999. -- minetest.split dissector
  1000. do
  1001. local p_split = Proto("minetest.split", "Minetest Split Message")
  1002. local f_split_seq = ProtoField.uint16("minetest.split.seq", "Sequence number", base.DEC)
  1003. local f_split_chunkcount = ProtoField.uint16("minetest.split.chunkcount", "Chunk count", base.DEC)
  1004. local f_split_chunknum = ProtoField.uint16("minetest.split.chunknum", "Chunk number", base.DEC)
  1005. local f_split_data = ProtoField.bytes("minetest.split.data", "Split message data")
  1006. p_split.fields = { f_split_seq, f_split_chunkcount, f_split_chunknum, f_split_data }
  1007. function p_split.dissector(buffer, pinfo, tree)
  1008. local t = tree:add(p_split, buffer(0,6))
  1009. t:add(f_split_seq, buffer(0,2))
  1010. t:add(f_split_chunkcount, buffer(2,2))
  1011. t:add(f_split_chunknum, buffer(4,2))
  1012. t:add(f_split_data, buffer(6))
  1013. pinfo.cols.info:append(" " .. buffer(0,2):uint() .. " chunk " .. buffer(4,2):uint() .. "/" .. buffer(2,2):uint())
  1014. end
  1015. end
  1016. -------------------------------------
  1017. -- Part 4 --
  1018. -- Wrapper protocol main dissector --
  1019. -------------------------------------
  1020. -- minetest dissector
  1021. do
  1022. local p_minetest = Proto("minetest", "Minetest")
  1023. local minetest_id = 0x4f457403
  1024. local vs_id = {
  1025. [minetest_id] = "Valid"
  1026. }
  1027. local vs_peer = {
  1028. [0] = "Inexistent",
  1029. [1] = "Server"
  1030. }
  1031. local vs_type = {
  1032. [0] = "Control",
  1033. [1] = "Original",
  1034. [2] = "Split",
  1035. [3] = "Reliable"
  1036. }
  1037. local f_id = ProtoField.uint32("minetest.id", "ID", base.HEX, vs_id)
  1038. local f_peer = ProtoField.uint16("minetest.peer", "Peer", base.DEC, vs_peer)
  1039. local f_channel = ProtoField.uint8("minetest.channel", "Channel", base.DEC)
  1040. local f_type = ProtoField.uint8("minetest.type", "Type", base.DEC, vs_type)
  1041. local f_seq = ProtoField.uint16("minetest.seq", "Sequence number", base.DEC)
  1042. local f_subtype = ProtoField.uint8("minetest.subtype", "Subtype", base.DEC, vs_type)
  1043. p_minetest.fields = { f_id, f_peer, f_channel, f_type, f_seq, f_subtype }
  1044. local data_dissector = Dissector.get("data")
  1045. local control_dissector = Dissector.get("minetest.control")
  1046. local client_dissector = Dissector.get("minetest.client")
  1047. local server_dissector = Dissector.get("minetest.server")
  1048. local split_dissector = Dissector.get("minetest.split")
  1049. function p_minetest.dissector(buffer, pinfo, tree)
  1050. -- Add Minetest tree item and verify the ID
  1051. local t = tree:add(p_minetest, buffer(0,8))
  1052. t:add(f_id, buffer(0,4))
  1053. if buffer(0,4):uint() ~= minetest_id then
  1054. t:add_expert_info(PI_UNDECODED, PI_WARN, "Invalid ID, this is not a Minetest packet")
  1055. return
  1056. end
  1057. -- ID is valid, so replace packet's shown protocol
  1058. pinfo.cols.protocol = "Minetest"
  1059. pinfo.cols.info = "Minetest"
  1060. -- Set the other header fields
  1061. t:add(f_peer, buffer(4,2))
  1062. t:add(f_channel, buffer(6,1))
  1063. t:add(f_type, buffer(7,1))
  1064. t:set_text("Minetest, Peer: " .. buffer(4,2):uint() .. ", Channel: " .. buffer(6,1):uint())
  1065. local reliability_info
  1066. if buffer(7,1):uint() == 3 then
  1067. -- Reliable message
  1068. reliability_info = "Seq=" .. buffer(8,2):uint()
  1069. t:set_len(11)
  1070. t:add(f_seq, buffer(8,2))
  1071. t:add(f_subtype, buffer(10,1))
  1072. pos = 10
  1073. else
  1074. -- Unreliable message
  1075. reliability_info = "Unrel"
  1076. pos = 7
  1077. end
  1078. if buffer(pos,1):uint() == 0 then
  1079. -- Control message, possibly reliable
  1080. control_dissector:call(buffer(pos+1):tvb(), pinfo, tree)
  1081. elseif buffer(pos,1):uint() == 1 then
  1082. -- Original message, possibly reliable
  1083. if buffer(4,2):uint() == 1 then
  1084. server_dissector:call(buffer(pos+1):tvb(), pinfo, tree)
  1085. else
  1086. client_dissector:call(buffer(pos+1):tvb(), pinfo, tree)
  1087. end
  1088. elseif buffer(pos,1):uint() == 2 then
  1089. -- Split message, possibly reliable
  1090. if buffer(4,2):uint() == 1 then
  1091. pinfo.cols.info = "Server: Split message"
  1092. else
  1093. pinfo.cols.info = "Client: Split message"
  1094. end
  1095. split_dissector:call(buffer(pos+1):tvb(), pinfo, tree)
  1096. elseif buffer(pos,1):uint() == 3 then
  1097. -- Doubly reliable message??
  1098. t:add_expert_info(PI_MALFORMED, PI_ERROR, "Reliable message wrapped in reliable message")
  1099. else
  1100. data_dissector:call(buffer(pos+1):tvb(), pinfo, tree)
  1101. end
  1102. pinfo.cols.info:append(" (" .. reliability_info .. ")")
  1103. end
  1104. -- FIXME Is there a way to let the dissector table check if the first payload bytes are 0x4f457403?
  1105. DissectorTable.get("udp.port"):add(30000, p_minetest)
  1106. DissectorTable.get("udp.port"):add(30001, p_minetest)
  1107. end
  1108. -----------------------
  1109. -- Part 5 --
  1110. -- Utility functions --
  1111. -----------------------
  1112. -- Checks if a (sub-)Tvb is long enough to be further dissected.
  1113. -- If it is long enough, sets the dissector tree item length to min_len
  1114. -- and returns true. If it is not long enough, adds expert info to the
  1115. -- dissector tree and returns false.
  1116. -- Parameter: tvb: the Tvb
  1117. -- Parameter: min_len: required minimum length
  1118. -- Parameter: t: dissector tree item
  1119. -- Returns: true if tvb:len() >= min_len, false otherwise
  1120. function minetest_check_length(tvb, min_len, t)
  1121. if tvb:len() >= min_len then
  1122. t:set_len(min_len)
  1123. return true
  1124. -- Tvb:reported_length_remaining() has been added in August 2011
  1125. -- and is not yet widely available, disable for the time being
  1126. -- TODO: uncomment at a later date
  1127. -- TODO: when uncommenting this, also re-check if other parts of
  1128. -- the dissector could benefit from reported_length_remaining
  1129. --elseif tvb:reported_length_remaining() >= min_len then
  1130. -- t:add_expert_info(PI_UNDECODED, PI_INFO, "Only part of this packet was captured, unable to decode.")
  1131. -- return false
  1132. else
  1133. t:add_expert_info(PI_MALFORMED, PI_ERROR, "Message is too short")
  1134. return false
  1135. end
  1136. end
  1137. -- Takes a Tvb or TvbRange (i.e. part of a packet) that
  1138. -- contains a UTF-16 string and returns a TvbRange containing
  1139. -- string converted to ASCII. Any characters outside the range
  1140. -- 0x20 to 0x7e are replaced by a question mark.
  1141. -- Parameter: tvb: Tvb or TvbRange that contains the UTF-16 data
  1142. -- Parameter: name: will be the name of the newly created Tvb.
  1143. -- Returns: New TvbRange containing the ASCII string.
  1144. -- TODO: Handle surrogates (should only produce one question mark)
  1145. -- TODO: Remove this when Wireshark supports UTF-16 strings natively.
  1146. function minetest_convert_utf16(tvb, name)
  1147. local hex, pos, char
  1148. hex = ""
  1149. for pos = 0, tvb:len() - 2, 2 do
  1150. char = tvb(pos, 2):uint()
  1151. if (char >= 0x20) and (char <= 0x7e) then
  1152. hex = hex .. string.format(" %02x", char)
  1153. else
  1154. hex = hex .. " 3F"
  1155. end
  1156. end
  1157. if hex == "" then
  1158. -- This is a hack to avoid a failed assertion in tvbuff.c
  1159. -- (function: ensure_contiguous_no_exception)
  1160. return ByteArray.new("00"):tvb(name):range(0,0)
  1161. else
  1162. return ByteArray.new(hex):tvb(name):range()
  1163. end
  1164. end