minetest.lua 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416
  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. -- Wireshark documentation:
  23. -- https://web.archive.org/web/20170711121726/https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Proto.html
  24. -- https://web.archive.org/web/20170711121844/https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Tree.html
  25. -- https://web.archive.org/web/20170711121917/https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Tvb.html
  26. -- Table of Contents:
  27. -- Part 1: Utility functions
  28. -- Part 2: Client command dissectors (TOSERVER_*)
  29. -- Part 3: Server command dissectors (TOCLIENT_*)
  30. -- Part 4: Wrapper protocol subdissectors
  31. -- Part 5: Wrapper protocol main dissector
  32. -- Part 6: Utility functions part 2
  33. -----------------------
  34. -- Part 1 --
  35. -- Utility functions --
  36. -----------------------
  37. -- Creates two ProtoFields to hold a length and variable-length text content
  38. -- lentype must be either "uint16" or "uint32"
  39. function minetest_field_helper(lentype, name, abbr)
  40. local f_textlen = ProtoField[lentype](name .. "len", abbr .. " (length)", base.DEC)
  41. local f_text = ProtoField.string(name, abbr)
  42. return f_textlen, f_text
  43. end
  44. --------------------------------------------
  45. -- Part 2 --
  46. -- Client command dissectors (TOSERVER_*) --
  47. --------------------------------------------
  48. minetest_client_commands = {}
  49. minetest_client_obsolete = {}
  50. -- TOSERVER_INIT
  51. do
  52. local abbr = "minetest.client.init_"
  53. local f_ser_fmt = ProtoField.uint8(abbr.."ser_version",
  54. "Maximum serialization format version", base.DEC)
  55. local f_comp_modes = ProtoField.uint16(abbr.."compression",
  56. "Supported compression modes", base.DEC, { [0] = "No compression" })
  57. local f_proto_min = ProtoField.uint16(abbr.."proto_min", "Minimum protocol version", base.DEC)
  58. local f_proto_max = ProtoField.uint16(abbr.."_proto_max", "Maximum protocol version", base.DEC)
  59. local f_player_namelen, f_player_name =
  60. minetest_field_helper("uint16", abbr.."player_name", "Player Name")
  61. minetest_client_commands[0x02] = {
  62. "INIT", -- Command name
  63. 11, -- Minimum message length including code
  64. { f_ser_fmt, -- List of fields [optional]
  65. f_comp_modes,
  66. f_proto_min,
  67. f_proto_max,
  68. f_player_namelen,
  69. f_player_name },
  70. function(buffer, pinfo, tree, t) -- Dissector function [optional]
  71. t:add(f_ser_fmt, buffer(2,1))
  72. t:add(f_comp_modes, buffer(3,2))
  73. t:add(f_proto_min, buffer(5,2))
  74. t:add(f_proto_max, buffer(7,2))
  75. minetest_decode_helper_ascii(buffer, t, "uint16", 9, f_player_namelen, f_player_name)
  76. end
  77. }
  78. end
  79. -- TOSERVER_INIT_LEGACY (obsolete)
  80. minetest_client_commands[0x10] = { "INIT_LEGACY", 53 }
  81. minetest_client_obsolete[0x10] = true
  82. -- TOSERVER_INIT2
  83. do
  84. local f_langlen, f_lang =
  85. minetest_field_helper("uint16", "minetest.client.init2_language", "Language Code")
  86. minetest_client_commands[0x11] = {
  87. "INIT2",
  88. 2,
  89. { f_langlen,
  90. f_lang },
  91. function(buffer, pinfo, tree, t)
  92. minetest_decode_helper_ascii(buffer, t, "uint16", 2, f_langlen, f_lang)
  93. end
  94. }
  95. end
  96. -- TOSERVER_MODCHANNEL_JOIN
  97. minetest_client_commands[0x17] = { "MODCHANNEL_JOIN", 2 }
  98. -- TOSERVER_MODCHANNEL_LEAVE
  99. minetest_client_commands[0x18] = { "MODCHANNEL_LEAVE", 2 }
  100. -- TOSERVER_MODCHANNEL_MSG
  101. minetest_client_commands[0x19] = { "MODCHANNEL_MSG", 2 }
  102. -- TOSERVER_GETBLOCK (obsolete)
  103. minetest_client_commands[0x20] = { "GETBLOCK", 2 }
  104. minetest_client_obsolete[0x20] = true
  105. -- TOSERVER_ADDNODE (obsolete)
  106. minetest_client_commands[0x21] = { "ADDNODE", 2 }
  107. minetest_client_obsolete[0x21] = true
  108. -- TOSERVER_REMOVENODE (obsolete)
  109. minetest_client_commands[0x22] = { "REMOVENODE", 2 }
  110. minetest_client_obsolete[0x22] = true
  111. -- TOSERVER_PLAYERPOS
  112. do
  113. local abbr = "minetest.client.playerpos_"
  114. local f_x = ProtoField.int32(abbr.."x", "Position X", base.DEC)
  115. local f_y = ProtoField.int32(abbr.."y", "Position Y", base.DEC)
  116. local f_z = ProtoField.int32(abbr.."z", "Position Z", base.DEC)
  117. local f_speed_x = ProtoField.int32(abbr.."speed_x", "Speed X", base.DEC)
  118. local f_speed_y = ProtoField.int32(abbr.."speed_y", "Speed Y", base.DEC)
  119. local f_speed_z = ProtoField.int32(abbr.."speed_z", "Speed Z", base.DEC)
  120. local f_pitch = ProtoField.int32(abbr.."pitch", "Pitch", base.DEC)
  121. local f_yaw = ProtoField.int32(abbr.."yaw", "Yaw", base.DEC)
  122. local f_key_pressed = ProtoField.bytes(abbr.."key_pressed", "Pressed keys")
  123. local f_fov = ProtoField.uint8(abbr.."fov", "FOV", base.DEC)
  124. local f_wanted_range = ProtoField.uint8(abbr.."wanted_range", "Requested view range", base.DEC)
  125. minetest_client_commands[0x23] = {
  126. "PLAYERPOS", 34,
  127. { f_x, f_y, f_z, f_speed_x, f_speed_y, f_speed_z, f_pitch, f_yaw,
  128. f_key_pressed, f_fov, f_wanted_range },
  129. function(buffer, pinfo, tree, t)
  130. t:add(f_x, buffer(2,4))
  131. t:add(f_y, buffer(6,4))
  132. t:add(f_z, buffer(10,4))
  133. t:add(f_speed_x, buffer(14,4))
  134. t:add(f_speed_y, buffer(18,4))
  135. t:add(f_speed_z, buffer(22,4))
  136. t:add(f_pitch, buffer(26,4))
  137. t:add(f_yaw, buffer(30,4))
  138. t:add(f_key_pressed, buffer(34,4))
  139. t:add(f_fov, buffer(38,1))
  140. t:add(f_wanted_range, buffer(39,1))
  141. end
  142. }
  143. end
  144. -- TOSERVER_GOTBLOCKS
  145. do
  146. local f_count = ProtoField.uint8("minetest.client.gotblocks_count", "Count", base.DEC)
  147. local f_block = ProtoField.bytes("minetest.client.gotblocks_block", "Block", base.NONE)
  148. local f_x = ProtoField.int16("minetest.client.gotblocks_x", "Block position X", base.DEC)
  149. local f_y = ProtoField.int16("minetest.client.gotblocks_y", "Block position Y", base.DEC)
  150. local f_z = ProtoField.int16("minetest.client.gotblocks_z", "Block position Z", base.DEC)
  151. minetest_client_commands[0x24] = {
  152. "GOTBLOCKS", 3,
  153. { f_count, f_block, f_x, f_y, f_z },
  154. function(buffer, pinfo, tree, t)
  155. t:add(f_count, buffer(2,1))
  156. local count = buffer(2,1):uint()
  157. if minetest_check_length(buffer, 3 + 6*count, t) then
  158. pinfo.cols.info:append(" * " .. count)
  159. local index
  160. for index = 0, count - 1 do
  161. local pos = 3 + 6*index
  162. local t2 = t:add(f_block, buffer(pos, 6))
  163. t2:set_text("Block, X: " .. buffer(pos, 2):int()
  164. .. ", Y: " .. buffer(pos + 2, 2):int()
  165. .. ", Z: " .. buffer(pos + 4, 2):int())
  166. t2:add(f_x, buffer(pos, 2))
  167. t2:add(f_y, buffer(pos + 2, 2))
  168. t2:add(f_z, buffer(pos + 4, 2))
  169. end
  170. end
  171. end
  172. }
  173. end
  174. -- TOSERVER_DELETEDBLOCKS
  175. do
  176. local f_count = ProtoField.uint8("minetest.client.deletedblocks_count", "Count", base.DEC)
  177. local f_block = ProtoField.bytes("minetest.client.deletedblocks_block", "Block", base.NONE)
  178. local f_x = ProtoField.int16("minetest.client.deletedblocks_x", "Block position X", base.DEC)
  179. local f_y = ProtoField.int16("minetest.client.deletedblocks_y", "Block position Y", base.DEC)
  180. local f_z = ProtoField.int16("minetest.client.deletedblocks_z", "Block position Z", base.DEC)
  181. minetest_client_commands[0x25] = {
  182. "DELETEDBLOCKS", 3,
  183. { f_count, f_block, f_x, f_y, f_z },
  184. function(buffer, pinfo, tree, t)
  185. t:add(f_count, buffer(2,1))
  186. local count = buffer(2,1):uint()
  187. if minetest_check_length(buffer, 3 + 6*count, t) then
  188. pinfo.cols.info:append(" * " .. count)
  189. local index
  190. for index = 0, count - 1 do
  191. local pos = 3 + 6*index
  192. local t2 = t:add(f_block, buffer(pos, 6))
  193. t2:set_text("Block, X: " .. buffer(pos, 2):int()
  194. .. ", Y: " .. buffer(pos + 2, 2):int()
  195. .. ", Z: " .. buffer(pos + 4, 2):int())
  196. t2:add(f_x, buffer(pos, 2))
  197. t2:add(f_y, buffer(pos + 2, 2))
  198. t2:add(f_z, buffer(pos + 4, 2))
  199. end
  200. end
  201. end
  202. }
  203. end
  204. -- TOSERVER_ADDNODE_FROM_INVENTORY (obsolete)
  205. minetest_client_commands[0x26] = { "ADDNODE_FROM_INVENTORY", 2 }
  206. minetest_client_obsolete[0x26] = true
  207. -- TOSERVER_CLICK_OBJECT (obsolete)
  208. minetest_client_commands[0x27] = { "CLICK_OBJECT", 2 }
  209. minetest_client_obsolete[0x27] = true
  210. -- TOSERVER_GROUND_ACTION (obsolete)
  211. minetest_client_commands[0x28] = { "GROUND_ACTION", 2 }
  212. minetest_client_obsolete[0x28] = true
  213. -- TOSERVER_RELEASE (obsolete)
  214. minetest_client_commands[0x29] = { "RELEASE", 2 }
  215. minetest_client_obsolete[0x29] = true
  216. -- TOSERVER_SIGNTEXT (obsolete)
  217. minetest_client_commands[0x30] = { "SIGNTEXT", 2 }
  218. minetest_client_obsolete[0x30] = true
  219. -- TOSERVER_INVENTORY_ACTION
  220. do
  221. local f_action = ProtoField.string("minetest.client.inventory_action", "Action")
  222. minetest_client_commands[0x31] = {
  223. "INVENTORY_ACTION", 2,
  224. { f_action },
  225. function(buffer, pinfo, tree, t)
  226. t:add(f_action, buffer(2, buffer:len() - 2))
  227. end
  228. }
  229. end
  230. -- TOSERVER_CHAT_MESSAGE
  231. do
  232. local f_length = ProtoField.uint16("minetest.client.chat_message_length", "Length", base.DEC)
  233. local f_message = ProtoField.string("minetest.client.chat_message", "Message")
  234. minetest_client_commands[0x32] = {
  235. "CHAT_MESSAGE", 4,
  236. { f_length, f_message },
  237. function(buffer, pinfo, tree, t)
  238. t:add(f_length, buffer(2,2))
  239. local textlen = buffer(2,2):uint()
  240. if minetest_check_length(buffer, 4 + textlen*2, t) then
  241. t:add(f_message, buffer(4, textlen*2), buffer(4, textlen*2):ustring())
  242. end
  243. end
  244. }
  245. end
  246. -- TOSERVER_SIGNNODETEXT (obsolete)
  247. minetest_client_commands[0x33] = { "SIGNNODETEXT", 2 }
  248. minetest_client_obsolete[0x33] = true
  249. -- TOSERVER_CLICK_ACTIVEOBJECT (obsolete)
  250. minetest_client_commands[0x34] = { "CLICK_ACTIVEOBJECT", 2 }
  251. minetest_client_obsolete[0x34] = true
  252. -- TOSERVER_DAMAGE
  253. do
  254. local f_amount = ProtoField.uint8("minetest.client.damage_amount", "Amount", base.DEC)
  255. minetest_client_commands[0x35] = {
  256. "DAMAGE", 3,
  257. { f_amount },
  258. function(buffer, pinfo, tree, t)
  259. t:add(f_amount, buffer(2,1))
  260. end
  261. }
  262. end
  263. -- TOSERVER_PASSWORD (obsolete)
  264. minetest_client_commands[0x36] = { "CLICK_ACTIVEOBJECT", 2 }
  265. minetest_client_obsolete[0x36] = true
  266. -- TOSERVER_PLAYERITEM
  267. do
  268. local f_item = ProtoField.uint16("minetest.client.playeritem_item", "Wielded item")
  269. minetest_client_commands[0x37] = {
  270. "PLAYERITEM", 4,
  271. { f_item },
  272. function(buffer, pinfo, tree, t)
  273. t:add(f_item, buffer(2,2))
  274. end
  275. }
  276. end
  277. -- TOSERVER_RESPAWN
  278. minetest_client_commands[0x38] = { "RESPAWN", 2 }
  279. -- TOSERVER_INTERACT
  280. do
  281. local abbr = "minetest.client.interact_"
  282. local vs_action = {
  283. [0] = "Start digging",
  284. [1] = "Stop digging",
  285. [2] = "Digging completed",
  286. [3] = "Place block or item",
  287. [4] = "Use item",
  288. [5] = "Activate held item",
  289. }
  290. local vs_pointed_type = {
  291. [0] = "Nothing",
  292. [1] = "Node",
  293. [2] = "Object",
  294. }
  295. local f_action = ProtoField.uint8(abbr.."action", "Action", base.DEC, vs_action)
  296. local f_item = ProtoField.uint16(abbr.."item", "Item Index", base.DEC)
  297. local f_plen = ProtoField.uint32(abbr.."plen", "Length of pointed thing", base.DEC)
  298. local f_pointed_version = ProtoField.uint8(abbr.."pointed_version",
  299. "Pointed Thing Version", base.DEC)
  300. local f_pointed_type = ProtoField.uint8(abbr.."pointed_version",
  301. "Pointed Thing Type", base.DEC, vs_pointed_type)
  302. local f_pointed_under_x = ProtoField.int16(abbr.."pointed_under_x",
  303. "Node position (under surface) X")
  304. local f_pointed_under_y = ProtoField.int16(abbr.."pointed_under_y",
  305. "Node position (under surface) Y")
  306. local f_pointed_under_z = ProtoField.int16(abbr.."pointed_under_z",
  307. "Node position (under surface) Z")
  308. local f_pointed_above_x = ProtoField.int16(abbr.."pointed_above_x",
  309. "Node position (above surface) X")
  310. local f_pointed_above_y = ProtoField.int16(abbr.."pointed_above_y",
  311. "Node position (above surface) Y")
  312. local f_pointed_above_z = ProtoField.int16(abbr.."pointed_above_z",
  313. "Node position (above surface) Z")
  314. local f_pointed_object_id = ProtoField.int16(abbr.."pointed_object_id",
  315. "Object ID")
  316. -- mising: additional playerpos data just like in TOSERVER_PLAYERPOS
  317. minetest_client_commands[0x39] = {
  318. "INTERACT", 11,
  319. { f_action,
  320. f_item,
  321. f_plen,
  322. f_pointed_version,
  323. f_pointed_type,
  324. f_pointed_under_x,
  325. f_pointed_under_y,
  326. f_pointed_under_z,
  327. f_pointed_above_x,
  328. f_pointed_above_y,
  329. f_pointed_above_z,
  330. f_pointed_object_id },
  331. function(buffer, pinfo, tree, t)
  332. t:add(f_action, buffer(2,1))
  333. t:add(f_item, buffer(3,2))
  334. t:add(f_plen, buffer(5,4))
  335. local plen = buffer(5,4):uint()
  336. if minetest_check_length(buffer, 9 + plen, t) then
  337. t:add(f_pointed_version, buffer(9,1))
  338. t:add(f_pointed_type, buffer(10,1))
  339. local ptype = buffer(10,1):uint()
  340. if ptype == 1 then -- Node
  341. t:add(f_pointed_under_x, buffer(11,2))
  342. t:add(f_pointed_under_y, buffer(13,2))
  343. t:add(f_pointed_under_z, buffer(15,2))
  344. t:add(f_pointed_above_x, buffer(17,2))
  345. t:add(f_pointed_above_x, buffer(19,2))
  346. t:add(f_pointed_above_x, buffer(21,2))
  347. elseif ptype == 2 then -- Object
  348. t:add(f_pointed_object_id, buffer(11,2))
  349. end
  350. end
  351. end
  352. }
  353. end
  354. -- ...
  355. minetest_client_commands[0x3a] = { "REMOVED_SOUNDS", 2 }
  356. minetest_client_commands[0x3b] = { "NODEMETA_FIELDS", 2 }
  357. minetest_client_commands[0x3c] = { "INVENTORY_FIELDS", 2 }
  358. minetest_client_commands[0x40] = { "REQUEST_MEDIA", 2 }
  359. minetest_client_commands[0x41] = { "RECEIVED_MEDIA", 2 }
  360. -- TOSERVER_BREATH (obsolete)
  361. minetest_client_commands[0x42] = { "BREATH", 2 }
  362. minetest_client_obsolete[0x42] = true
  363. -- TOSERVER_CLIENT_READY
  364. do
  365. local abbr = "minetest.client.client_ready_"
  366. local f_major = ProtoField.uint8(abbr.."major","Version Major")
  367. local f_minor = ProtoField.uint8(abbr.."minor","Version Minor")
  368. local f_patch = ProtoField.uint8(abbr.."patch","Version Patch")
  369. local f_reserved = ProtoField.uint8(abbr.."reserved","Reserved")
  370. local f_versionlen, f_version =
  371. minetest_field_helper("uint16", abbr.."version", "Full Version String")
  372. local f_formspec_ver = ProtoField.uint16(abbr.."formspec_version",
  373. "Formspec API version")
  374. minetest_client_commands[0x43] = {
  375. "CLIENT_READY",
  376. 8,
  377. { f_major, f_minor, f_patch, f_reserved, f_versionlen,
  378. f_version, f_formspec_ver },
  379. function(buffer, pinfo, tree, t)
  380. t:add(f_major, buffer(2,1))
  381. t:add(f_minor, buffer(3,1))
  382. t:add(f_patch, buffer(4,1))
  383. t:add(f_reserved, buffer(5,1))
  384. local off = minetest_decode_helper_ascii(buffer, t, "uint16", 6,
  385. f_versionlen, f_version)
  386. if off and minetest_check_length(buffer, off + 2, t) then
  387. t:add(f_formspec_ver, buffer(off,2))
  388. end
  389. end
  390. }
  391. end
  392. -- ...
  393. minetest_client_commands[0x50] = { "FIRST_SRP", 2 }
  394. minetest_client_commands[0x51] = { "SRP_BYTES_A", 2 }
  395. minetest_client_commands[0x52] = { "SRP_BYTES_M", 2 }
  396. --------------------------------------------
  397. -- Part 3 --
  398. -- Server command dissectors (TOCLIENT_*) --
  399. --------------------------------------------
  400. minetest_server_commands = {}
  401. minetest_server_obsolete = {}
  402. -- TOCLIENT_HELLO
  403. do
  404. local abbr = "minetest.server.hello_"
  405. local f_ser_fmt = ProtoField.uint8(abbr.."ser_version",
  406. "Deployed serialization format version", base.DEC)
  407. local f_comp_mode = ProtoField.uint16(abbr.."compression",
  408. "Deployed compression mode", base.DEC, { [0] = "No compression" })
  409. local f_proto = ProtoField.uint16(abbr.."proto",
  410. "Deployed protocol version", base.DEC)
  411. local f_auth_methods = ProtoField.bytes(abbr.."auth_modes",
  412. "Supported authentication modes")
  413. local f_legacy_namelen, f_legacy_name = minetest_field_helper("uint16",
  414. abbr.."legacy_name", "Legacy player name for hashing")
  415. minetest_server_commands[0x02] = {
  416. "HELLO",
  417. 13,
  418. { f_ser_fmt, f_comp_mode, f_proto, f_auth_methods,
  419. f_legacy_namelen, f_legacy_name },
  420. function(buffer, pinfo, tree, t)
  421. t:add(f_ser_fmt, buffer(2,1))
  422. t:add(f_comp_mode, buffer(3,2))
  423. t:add(f_proto, buffer(5,2))
  424. t:add(f_auth_methods, buffer(7,4))
  425. minetest_decode_helper_ascii(buffer, t, "uint16", 11, f_legacy_namelen, f_legacy_name)
  426. end
  427. }
  428. end
  429. -- TOCLIENT_AUTH_ACCEPT
  430. do
  431. local abbr = "minetest.server.auth_accept_"
  432. local f_player_x = ProtoField.float(abbr.."player_x", "Player position X")
  433. local f_player_y = ProtoField.float(abbr.."player_y", "Player position Y")
  434. local f_player_z = ProtoField.float(abbr.."player_z", "Player position Z")
  435. local f_map_seed = ProtoField.uint64(abbr.."map_seed", "Map seed")
  436. local f_send_interval = ProtoField.float(abbr.."send_interval",
  437. "Recommended send interval")
  438. local f_sudo_auth_methods = ProtoField.bytes(abbr.."sudo_auth_methods",
  439. "Supported auth methods for sudo mode")
  440. minetest_server_commands[0x03] = {
  441. "AUTH_ACCEPT",
  442. 30,
  443. { f_player_x, f_player_y, f_player_z, f_map_seed,
  444. f_send_interval, f_sudo_auth_methods },
  445. function(buffer, pinfo, tree, t)
  446. t:add(f_player_x, buffer(2,4))
  447. t:add(f_player_y, buffer(6,4))
  448. t:add(f_player_z, buffer(10,4))
  449. t:add(f_map_seed, buffer(14,8))
  450. t:add(f_send_interval, buffer(22,4))
  451. t:add(f_sudo_auth_methods, buffer(26,4))
  452. end
  453. }
  454. end
  455. -- ...
  456. minetest_server_commands[0x04] = {"ACCEPT_SUDO_MODE", 2}
  457. minetest_server_commands[0x05] = {"DENY_SUDO_MODE", 2}
  458. minetest_server_commands[0x0A] = {"ACCESS_DENIED", 2}
  459. -- TOCLIENT_INIT (obsolete)
  460. minetest_server_commands[0x10] = { "INIT", 2 }
  461. minetest_server_obsolete[0x10] = true
  462. -- TOCLIENT_BLOCKDATA
  463. do
  464. local f_x = ProtoField.int16("minetest.server.blockdata_x", "Block position X", base.DEC)
  465. local f_y = ProtoField.int16("minetest.server.blockdata_y", "Block position Y", base.DEC)
  466. local f_z = ProtoField.int16("minetest.server.blockdata_z", "Block position Z", base.DEC)
  467. local f_data = ProtoField.bytes("minetest.server.blockdata_block", "Serialized MapBlock")
  468. minetest_server_commands[0x20] = {
  469. "BLOCKDATA", 8,
  470. { f_x, f_y, f_z, f_data },
  471. function(buffer, pinfo, tree, t)
  472. t:add(f_x, buffer(2,2))
  473. t:add(f_y, buffer(4,2))
  474. t:add(f_z, buffer(6,2))
  475. t:add(f_data, buffer(8, buffer:len() - 8))
  476. end
  477. }
  478. end
  479. -- TOCLIENT_ADDNODE
  480. do
  481. local f_x = ProtoField.int16("minetest.server.addnode_x", "Position X", base.DEC)
  482. local f_y = ProtoField.int16("minetest.server.addnode_y", "Position Y", base.DEC)
  483. local f_z = ProtoField.int16("minetest.server.addnode_z", "Position Z", base.DEC)
  484. local f_data = ProtoField.bytes("minetest.server.addnode_node", "Serialized MapNode")
  485. minetest_server_commands[0x21] = {
  486. "ADDNODE", 8,
  487. { f_x, f_y, f_z, f_data },
  488. function(buffer, pinfo, tree, t)
  489. t:add(f_x, buffer(2,2))
  490. t:add(f_y, buffer(4,2))
  491. t:add(f_z, buffer(6,2))
  492. t:add(f_data, buffer(8, buffer:len() - 8))
  493. end
  494. }
  495. end
  496. -- TOCLIENT_REMOVENODE
  497. do
  498. local f_x = ProtoField.int16("minetest.server.removenode_x", "Position X", base.DEC)
  499. local f_y = ProtoField.int16("minetest.server.removenode_y", "Position Y", base.DEC)
  500. local f_z = ProtoField.int16("minetest.server.removenode_z", "Position Z", base.DEC)
  501. minetest_server_commands[0x22] = {
  502. "REMOVENODE", 8,
  503. { f_x, f_y, f_z },
  504. function(buffer, pinfo, tree, t)
  505. t:add(f_x, buffer(2,2))
  506. t:add(f_y, buffer(4,2))
  507. t:add(f_z, buffer(6,2))
  508. end
  509. }
  510. end
  511. -- TOCLIENT_PLAYERPOS (obsolete)
  512. minetest_server_commands[0x23] = { "PLAYERPOS", 2 }
  513. minetest_server_obsolete[0x23] = true
  514. -- TOCLIENT_PLAYERINFO (obsolete)
  515. minetest_server_commands[0x24] = { "PLAYERINFO", 2 }
  516. minetest_server_obsolete[0x24] = true
  517. -- TOCLIENT_OPT_BLOCK_NOT_FOUND (obsolete)
  518. minetest_server_commands[0x25] = { "OPT_BLOCK_NOT_FOUND", 2 }
  519. minetest_server_obsolete[0x25] = true
  520. -- TOCLIENT_SECTORMETA (obsolete)
  521. minetest_server_commands[0x26] = { "SECTORMETA", 2 }
  522. minetest_server_obsolete[0x26] = true
  523. -- TOCLIENT_INVENTORY
  524. do
  525. local f_inventory = ProtoField.string("minetest.server.inventory", "Inventory")
  526. minetest_server_commands[0x27] = {
  527. "INVENTORY", 2,
  528. { f_inventory },
  529. function(buffer, pinfo, tree, t)
  530. t:add(f_inventory, buffer(2, buffer:len() - 2))
  531. end
  532. }
  533. end
  534. -- TOCLIENT_OBJECTDATA (obsolete)
  535. minetest_server_commands[0x28] = { "OBJECTDATA", 2 }
  536. minetest_server_obsolete[0x28] = true
  537. -- TOCLIENT_TIME_OF_DAY
  538. do
  539. local f_time = ProtoField.uint16("minetest.server.time_of_day", "Time", base.DEC)
  540. local f_time_speed = ProtoField.float("minetest.server.time_speed", "Time Speed", base.DEC)
  541. minetest_server_commands[0x29] = {
  542. "TIME_OF_DAY", 4,
  543. { f_time, f_time_speed },
  544. function(buffer, pinfo, tree, t)
  545. t:add(f_time, buffer(2,2))
  546. t:add(f_time_speed, buffer(4,4))
  547. end
  548. }
  549. end
  550. -- TOCLIENT_CSM_RESTRICTION_FLAGS
  551. minetest_server_commands[0x2a] = { "CSM_RESTRICTION_FLAGS", 2 }
  552. -- TOCLIENT_PLAYER_SPEED
  553. minetest_server_commands[0x2b] = { "PLAYER_SPEED", 2 }
  554. -- TOCLIENT_CHAT_MESSAGE
  555. do
  556. local abbr = "minetest.server.chat_message_"
  557. local vs_type = {
  558. [0] = "Raw",
  559. [1] = "Normal",
  560. [2] = "Announce",
  561. [3] = "System",
  562. }
  563. local f_version = ProtoField.uint8(abbr.."version", "Version")
  564. local f_type = ProtoField.uint8(abbr.."type", "Message Type", base.DEC, vs_type)
  565. local f_senderlen, f_sender = minetest_field_helper("uint16", abbr.."sender",
  566. "Message sender")
  567. local f_messagelen, f_message = minetest_field_helper("uint16", abbr:sub(1,-2),
  568. "Message")
  569. minetest_server_commands[0x2f] = {
  570. "CHAT_MESSAGE", 8,
  571. { f_version, f_type, f_senderlen, f_sender,
  572. f_messagelen, f_message },
  573. function(buffer, pinfo, tree, t)
  574. t:add(f_version, buffer(2,1))
  575. t:add(f_type, buffer(3,1))
  576. local off = 4
  577. off = minetest_decode_helper_utf16(buffer, t, "uint16", off, f_senderlen, f_sender)
  578. if off then
  579. off = minetest_decode_helper_utf16(buffer, t, "uint16", off, f_messagelen, f_message)
  580. end
  581. end
  582. }
  583. end
  584. -- TOCLIENT_CHAT_MESSAGE_OLD (obsolete)
  585. minetest_server_commands[0x30] = { "CHAT_MESSAGE_OLD", 2 }
  586. minetest_server_obsolete[0x30] = true
  587. -- TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD
  588. do
  589. local f_removed_count = ProtoField.uint16(
  590. "minetest.server.active_object_remove_add_removed_count",
  591. "Count of removed objects", base.DEC)
  592. local f_removed = ProtoField.bytes(
  593. "minetest.server.active_object_remove_add_removed",
  594. "Removed object")
  595. local f_removed_id = ProtoField.uint16(
  596. "minetest.server.active_object_remove_add_removed_id",
  597. "ID", base.DEC)
  598. local f_added_count = ProtoField.uint16(
  599. "minetest.server.active_object_remove_add_added_count",
  600. "Count of added objects", base.DEC)
  601. local f_added = ProtoField.bytes(
  602. "minetest.server.active_object_remove_add_added",
  603. "Added object")
  604. local f_added_id = ProtoField.uint16(
  605. "minetest.server.active_object_remove_add_added_id",
  606. "ID", base.DEC)
  607. local f_added_type = ProtoField.uint8(
  608. "minetest.server.active_object_remove_add_added_type",
  609. "Type", base.DEC)
  610. local f_added_init_length = ProtoField.uint32(
  611. "minetest.server.active_object_remove_add_added_init_length",
  612. "Initialization data length", base.DEC)
  613. local f_added_init_data = ProtoField.bytes(
  614. "minetest.server.active_object_remove_add_added_init_data",
  615. "Initialization data")
  616. minetest_server_commands[0x31] = {
  617. "ACTIVE_OBJECT_REMOVE_ADD", 6,
  618. { f_removed_count, f_removed, f_removed_id,
  619. f_added_count, f_added, f_added_id,
  620. f_added_type, f_added_init_length, f_added_init_data },
  621. function(buffer, pinfo, tree, t)
  622. local t2, index, pos
  623. local removed_count_pos = 2
  624. local removed_count = buffer(removed_count_pos, 2):uint()
  625. t:add(f_removed_count, buffer(removed_count_pos, 2))
  626. local added_count_pos = removed_count_pos + 2 + 2 * removed_count
  627. if not minetest_check_length(buffer, added_count_pos + 2, t) then
  628. return
  629. end
  630. -- Loop through removed active objects
  631. for index = 0, removed_count - 1 do
  632. pos = removed_count_pos + 2 + 2 * index
  633. t2 = t:add(f_removed, buffer(pos, 2))
  634. t2:set_text("Removed object, ID = " .. buffer(pos, 2):uint())
  635. t2:add(f_removed_id, buffer(pos, 2))
  636. end
  637. local added_count = buffer(added_count_pos, 2):uint()
  638. t:add(f_added_count, buffer(added_count_pos, 2))
  639. -- Loop through added active objects
  640. pos = added_count_pos + 2
  641. for index = 0, added_count - 1 do
  642. if not minetest_check_length(buffer, pos + 7, t) then
  643. return
  644. end
  645. local init_length = buffer(pos + 3, 4):uint()
  646. if not minetest_check_length(buffer, pos + 7 + init_length, t) then
  647. return
  648. end
  649. t2 = t:add(f_added, buffer(pos, 7 + init_length))
  650. t2:set_text("Added object, ID = " .. buffer(pos, 2):uint())
  651. t2:add(f_added_id, buffer(pos, 2))
  652. t2:add(f_added_type, buffer(pos + 2, 1))
  653. t2:add(f_added_init_length, buffer(pos + 3, 4))
  654. t2:add(f_added_init_data, buffer(pos + 7, init_length))
  655. pos = pos + 7 + init_length
  656. end
  657. pinfo.cols.info:append(" * " .. (removed_count + added_count))
  658. end
  659. }
  660. end
  661. -- TOCLIENT_ACTIVE_OBJECT_MESSAGES
  662. do
  663. local f_object_count = ProtoField.uint16(
  664. "minetest.server.active_object_messages_object_count",
  665. "Count of objects", base.DEC)
  666. local f_object = ProtoField.bytes(
  667. "minetest.server.active_object_messages_object",
  668. "Object")
  669. local f_object_id = ProtoField.uint16(
  670. "minetest.server.active_object_messages_id",
  671. "ID", base.DEC)
  672. local f_message_length = ProtoField.uint16(
  673. "minetest.server.active_object_messages_message_length",
  674. "Message length", base.DEC)
  675. local f_message = ProtoField.bytes(
  676. "minetest.server.active_object_messages_message",
  677. "Message")
  678. minetest_server_commands[0x32] = {
  679. "ACTIVE_OBJECT_MESSAGES", 2,
  680. { f_object_count, f_object, f_object_id, f_message_length, f_message },
  681. function(buffer, pinfo, tree, t)
  682. local t2, count, pos, message_length
  683. count = 0
  684. pos = 2
  685. while pos < buffer:len() do
  686. if not minetest_check_length(buffer, pos + 4, t) then
  687. return
  688. end
  689. message_length = buffer(pos + 2, 2):uint()
  690. if not minetest_check_length(buffer, pos + 4 + message_length, t) then
  691. return
  692. end
  693. count = count + 1
  694. pos = pos + 4 + message_length
  695. end
  696. pinfo.cols.info:append(" * " .. count)
  697. t:add(f_object_count, count):set_generated()
  698. pos = 2
  699. while pos < buffer:len() do
  700. message_length = buffer(pos + 2, 2):uint()
  701. t2 = t:add(f_object, buffer(pos, 4 + message_length))
  702. t2:set_text("Object, ID = " .. buffer(pos, 2):uint())
  703. t2:add(f_object_id, buffer(pos, 2))
  704. t2:add(f_message_length, buffer(pos + 2, 2))
  705. t2:add(f_message, buffer(pos + 4, message_length))
  706. pos = pos + 4 + message_length
  707. end
  708. end
  709. }
  710. end
  711. -- TOCLIENT_HP
  712. do
  713. local f_hp = ProtoField.uint16("minetest.server.hp", "Health points", base.DEC)
  714. minetest_server_commands[0x33] = {
  715. "HP", 4,
  716. { f_hp },
  717. function(buffer, pinfo, tree, t)
  718. t:add(f_hp, buffer(2,2))
  719. end
  720. }
  721. end
  722. -- TOCLIENT_MOVE_PLAYER
  723. do
  724. local abbr = "minetest.server.move_player_"
  725. local f_x = ProtoField.float(abbr.."x", "Position X")
  726. local f_y = ProtoField.float(abbr.."y", "Position Y")
  727. local f_z = ProtoField.float(abbr.."z", "Position Z")
  728. local f_pitch = ProtoField.float(abbr.."_pitch", "Pitch")
  729. local f_yaw = ProtoField.float(abbr.."yaw", "Yaw")
  730. minetest_server_commands[0x34] = {
  731. "MOVE_PLAYER", 22,
  732. { f_x, f_y, f_z, f_pitch, f_yaw, f_garbage },
  733. function(buffer, pinfo, tree, t)
  734. t:add(f_x, buffer(2, 4))
  735. t:add(f_y, buffer(6, 4))
  736. t:add(f_z, buffer(10, 4))
  737. t:add(f_pitch, buffer(14, 4))
  738. t:add(f_yaw, buffer(18, 4))
  739. end
  740. }
  741. end
  742. -- TOCLIENT_ACCESS_DENIED_LEGACY
  743. do
  744. local f_reason_length = ProtoField.uint16("minetest.server.access_denied_reason_length", "Reason length", base.DEC)
  745. local f_reason = ProtoField.string("minetest.server.access_denied_reason", "Reason")
  746. minetest_server_commands[0x35] = {
  747. "ACCESS_DENIED_LEGACY", 4,
  748. { f_reason_length, f_reason },
  749. function(buffer, pinfo, tree, t)
  750. t:add(f_reason_length, buffer(2,2))
  751. local reason_length = buffer(2,2):uint()
  752. if minetest_check_length(buffer, 4 + reason_length * 2, t) then
  753. t:add(f_reason, minetest_convert_utf16(buffer(4, reason_length * 2), "Converted reason message"))
  754. end
  755. end
  756. }
  757. end
  758. -- TOCLIENT_FOV
  759. minetest_server_commands[0x36] = { "FOV", 2 }
  760. -- TOCLIENT_DEATHSCREEN
  761. do
  762. local f_set_camera_point_target = ProtoField.bool(
  763. "minetest.server.deathscreen_set_camera_point_target",
  764. "Set camera point target")
  765. local f_camera_point_target_x = ProtoField.int32(
  766. "minetest.server.deathscreen_camera_point_target_x",
  767. "Camera point target X", base.DEC)
  768. local f_camera_point_target_y = ProtoField.int32(
  769. "minetest.server.deathscreen_camera_point_target_y",
  770. "Camera point target Y", base.DEC)
  771. local f_camera_point_target_z = ProtoField.int32(
  772. "minetest.server.deathscreen_camera_point_target_z",
  773. "Camera point target Z", base.DEC)
  774. minetest_server_commands[0x37] = {
  775. "DEATHSCREEN", 15,
  776. { f_set_camera_point_target, f_camera_point_target_x,
  777. f_camera_point_target_y, f_camera_point_target_z},
  778. function(buffer, pinfo, tree, t)
  779. t:add(f_set_camera_point_target, buffer(2,1))
  780. t:add(f_camera_point_target_x, buffer(3,4))
  781. t:add(f_camera_point_target_y, buffer(7,4))
  782. t:add(f_camera_point_target_z, buffer(11,4))
  783. end
  784. }
  785. end
  786. -- TOCLIENT_MEDIA
  787. minetest_server_commands[0x38] = {"MEDIA", 2}
  788. -- TOCLIENT_TOOLDEF (obsolete)
  789. minetest_server_commands[0x39] = {"TOOLDEF", 2}
  790. minetest_server_obsolete[0x39] = true
  791. -- TOCLIENT_NODEDEF
  792. minetest_server_commands[0x3a] = {"NODEDEF", 2}
  793. -- TOCLIENT_CRAFTITEMDEF (obsolete)
  794. minetest_server_commands[0x3b] = {"CRAFTITEMDEF", 2}
  795. minetest_server_obsolete[0x3b] = true
  796. -- ...
  797. minetest_server_commands[0x3c] = {"ANNOUNCE_MEDIA", 2}
  798. minetest_server_commands[0x3d] = {"ITEMDEF", 2}
  799. minetest_server_commands[0x3f] = {"PLAY_SOUND", 2}
  800. minetest_server_commands[0x40] = {"STOP_SOUND", 2}
  801. minetest_server_commands[0x41] = {"PRIVILEGES", 2}
  802. minetest_server_commands[0x42] = {"INVENTORY_FORMSPEC", 2}
  803. minetest_server_commands[0x43] = {"DETACHED_INVENTORY", 2}
  804. minetest_server_commands[0x44] = {"SHOW_FORMSPEC", 2}
  805. minetest_server_commands[0x45] = {"MOVEMENT", 2}
  806. minetest_server_commands[0x46] = {"SPAWN_PARTICLE", 2}
  807. minetest_server_commands[0x47] = {"ADD_PARTICLE_SPAWNER", 2}
  808. -- TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY (obsolete)
  809. minetest_server_commands[0x48] = {"DELETE_PARTICLESPAWNER_LEGACY", 2}
  810. minetest_server_obsolete[0x48] = true
  811. -- ...
  812. minetest_server_commands[0x49] = {"HUDADD", 2}
  813. minetest_server_commands[0x4a] = {"HUDRM", 2}
  814. minetest_server_commands[0x4b] = {"HUDCHANGE", 2}
  815. minetest_server_commands[0x4c] = {"HUD_SET_FLAGS", 2}
  816. minetest_server_commands[0x4d] = {"HUD_SET_PARAM", 2}
  817. minetest_server_commands[0x4e] = {"BREATH", 2}
  818. minetest_server_commands[0x4f] = {"SET_SKY", 2}
  819. minetest_server_commands[0x50] = {"OVERRIDE_DAY_NIGHT_RATIO", 2}
  820. minetest_server_commands[0x51] = {"LOCAL_PLAYER_ANIMATIONS", 2}
  821. minetest_server_commands[0x52] = {"EYE_OFFSET", 2}
  822. minetest_server_commands[0x53] = {"DELETE_PARTICLESPAWNER", 2}
  823. minetest_server_commands[0x54] = {"CLOUD_PARAMS", 2}
  824. minetest_server_commands[0x55] = {"FADE_SOUND", 2}
  825. -- TOCLIENT_UPDATE_PLAYER_LIST
  826. do
  827. local abbr = "minetest.server.update_player_list_"
  828. local vs_type = {
  829. [0] = "Init",
  830. [1] = "Add",
  831. [2] = "Remove",
  832. }
  833. local f_type = ProtoField.uint8(abbr.."type", "Type", base.DEC, vs_type)
  834. local f_count = ProtoField.uint16(abbr.."count", "Number of players", base.DEC)
  835. local f_name = ProtoField.string(abbr.."name", "Name")
  836. minetest_server_commands[0x56] = {
  837. "UPDATE_PLAYER_LIST",
  838. 5,
  839. { f_type, f_count, f_name },
  840. function(buffer, pinfo, tree, t)
  841. t:add(f_type, buffer(2,1))
  842. t:add(f_count, buffer(3,2))
  843. local count = buffer(3,2):uint()
  844. local off = 5
  845. for i = 1, count do
  846. if not minetest_check_length(buffer, off + 2, t) then
  847. return
  848. end
  849. off = minetest_decode_helper_ascii(buffer, t, "uint16", off, nil, f_name)
  850. if not off then
  851. return
  852. end
  853. end
  854. end
  855. }
  856. end
  857. -- ...
  858. minetest_server_commands[0x57] = {"MODCHANNEL_MSG", 2}
  859. minetest_server_commands[0x58] = {"MODCHANNEL_SIGNAL", 2}
  860. minetest_server_commands[0x59] = {"NODEMETA_CHANGED", 2}
  861. minetest_server_commands[0x5a] = {"SET_SUN", 2}
  862. minetest_server_commands[0x5b] = {"SET_MOON", 2}
  863. minetest_server_commands[0x5c] = {"SET_STARS", 2}
  864. minetest_server_commands[0x60] = {"SRP_BYTES_S_B", 2}
  865. minetest_server_commands[0x61] = {"FORMSPEC_PREPEND", 2}
  866. ------------------------------------
  867. -- Part 4 --
  868. -- Wrapper protocol subdissectors --
  869. ------------------------------------
  870. -- minetest.control dissector
  871. do
  872. local p_control = Proto("minetest.control", "Minetest Control")
  873. local vs_control_type = {
  874. [0] = "Ack",
  875. [1] = "Set Peer ID",
  876. [2] = "Ping",
  877. [3] = "Disco"
  878. }
  879. local f_control_type = ProtoField.uint8("minetest.control.type", "Control Type", base.DEC, vs_control_type)
  880. local f_control_ack = ProtoField.uint16("minetest.control.ack", "ACK sequence number", base.DEC)
  881. local f_control_peerid = ProtoField.uint8("minetest.control.peerid", "New peer ID", base.DEC)
  882. p_control.fields = { f_control_type, f_control_ack, f_control_peerid }
  883. local data_dissector = Dissector.get("data")
  884. function p_control.dissector(buffer, pinfo, tree)
  885. local t = tree:add(p_control, buffer(0,1))
  886. t:add(f_control_type, buffer(0,1))
  887. pinfo.cols.info = "Control message"
  888. local pos = 1
  889. if buffer(0,1):uint() == 0 then
  890. pos = 3
  891. t:set_len(3)
  892. t:add(f_control_ack, buffer(1,2))
  893. pinfo.cols.info = "Ack " .. buffer(1,2):uint()
  894. elseif buffer(0,1):uint() == 1 then
  895. pos = 3
  896. t:set_len(3)
  897. t:add(f_control_peerid, buffer(1,2))
  898. pinfo.cols.info = "Set peer ID " .. buffer(1,2):uint()
  899. elseif buffer(0,1):uint() == 2 then
  900. pinfo.cols.info = "Ping"
  901. elseif buffer(0,1):uint() == 3 then
  902. pinfo.cols.info = "Disco"
  903. end
  904. data_dissector:call(buffer(pos):tvb(), pinfo, tree)
  905. end
  906. end
  907. -- minetest.client dissector
  908. -- minetest.server dissector
  909. -- Defines the minetest.client or minetest.server Proto. These two protocols
  910. -- are created by the same function because they are so similar.
  911. -- Parameter: proto: the Proto object
  912. -- Parameter: this_peer: "Client" or "Server"
  913. -- Parameter: other_peer: "Server" or "Client"
  914. -- Parameter: commands: table of command information, built above
  915. -- Parameter: obsolete: table of obsolete commands, built above
  916. function minetest_define_client_or_server_proto(is_client)
  917. -- Differences between minetest.client and minetest.server
  918. local proto_name, this_peer, other_peer, empty_message_info
  919. local commands, obsolete
  920. if is_client then
  921. proto_name = "minetest.client"
  922. this_peer = "Client"
  923. other_peer = "Server"
  924. empty_message_info = "Empty message / Connect"
  925. commands = minetest_client_commands -- defined in Part 2
  926. obsolete = minetest_client_obsolete -- defined in Part 2
  927. else
  928. proto_name = "minetest.server"
  929. this_peer = "Server"
  930. other_peer = "Client"
  931. empty_message_info = "Empty message"
  932. commands = minetest_server_commands -- defined in Part 3
  933. obsolete = minetest_server_obsolete -- defined in Part 3
  934. end
  935. -- Create the protocol object.
  936. local proto = Proto(proto_name, "Minetest " .. this_peer .. " to " .. other_peer)
  937. -- Create a table vs_command that maps command codes to command names.
  938. local vs_command = {}
  939. local code, command_info
  940. for code, command_info in pairs(commands) do
  941. local command_name = command_info[1]
  942. vs_command[code] = "TO" .. other_peer:upper() .. "_" .. command_name
  943. end
  944. -- Field definitions
  945. local f_command = ProtoField.uint16(proto_name .. ".command", "Command", base.HEX, vs_command)
  946. local f_empty = ProtoField.bool(proto_name .. ".empty", "Is empty", BASE_NONE)
  947. proto.fields = { f_command, f_empty }
  948. -- Add command-specific fields to the protocol
  949. for code, command_info in pairs(commands) do
  950. local command_fields = command_info[3]
  951. if command_fields ~= nil then
  952. for index, field in ipairs(command_fields) do
  953. assert(field ~= nil)
  954. table.insert(proto.fields, field)
  955. end
  956. end
  957. end
  958. -- minetest.client or minetest.server dissector function
  959. function proto.dissector(buffer, pinfo, tree)
  960. local t = tree:add(proto, buffer)
  961. pinfo.cols.info = this_peer
  962. if buffer:len() == 0 then
  963. -- Empty message.
  964. t:add(f_empty, 1):set_generated()
  965. pinfo.cols.info:append(": " .. empty_message_info)
  966. elseif minetest_check_length(buffer, 2, t) then
  967. -- Get the command code.
  968. t:add(f_command, buffer(0,2))
  969. local code = buffer(0,2):uint()
  970. local command_info = commands[code]
  971. if command_info == nil then
  972. -- Error: Unknown command.
  973. pinfo.cols.info:append(": Unknown command")
  974. t:add_expert_info(PI_UNDECODED, PI_WARN, "Unknown " .. this_peer .. " to " .. other_peer .. " command")
  975. else
  976. -- Process a known command
  977. local command_name = command_info[1]
  978. local command_min_length = command_info[2]
  979. local command_fields = command_info[3]
  980. local command_dissector = command_info[4]
  981. if minetest_check_length(buffer, command_min_length, t) then
  982. pinfo.cols.info:append(": " .. command_name)
  983. if command_dissector ~= nil then
  984. command_dissector(buffer, pinfo, tree, t)
  985. end
  986. end
  987. if obsolete[code] then
  988. t:add_expert_info(PI_REQUEST_CODE, PI_WARN, "Obsolete command.")
  989. end
  990. end
  991. end
  992. end
  993. end
  994. minetest_define_client_or_server_proto(true) -- minetest.client
  995. minetest_define_client_or_server_proto(false) -- minetest.server
  996. -- minetest.split dissector
  997. do
  998. local p_split = Proto("minetest.split", "Minetest Split Message")
  999. local f_split_seq = ProtoField.uint16("minetest.split.seq", "Sequence number", base.DEC)
  1000. local f_split_chunkcount = ProtoField.uint16("minetest.split.chunkcount", "Chunk count", base.DEC)
  1001. local f_split_chunknum = ProtoField.uint16("minetest.split.chunknum", "Chunk number", base.DEC)
  1002. local f_split_data = ProtoField.bytes("minetest.split.data", "Split message data")
  1003. p_split.fields = { f_split_seq, f_split_chunkcount, f_split_chunknum, f_split_data }
  1004. function p_split.dissector(buffer, pinfo, tree)
  1005. local t = tree:add(p_split, buffer(0,6))
  1006. t:add(f_split_seq, buffer(0,2))
  1007. t:add(f_split_chunkcount, buffer(2,2))
  1008. t:add(f_split_chunknum, buffer(4,2))
  1009. t:add(f_split_data, buffer(6))
  1010. pinfo.cols.info:append(" " .. buffer(0,2):uint() .. " chunk " .. buffer(4,2):uint() .. "/" .. buffer(2,2):uint())
  1011. end
  1012. end
  1013. -------------------------------------
  1014. -- Part 5 --
  1015. -- Wrapper protocol main dissector --
  1016. -------------------------------------
  1017. -- minetest dissector
  1018. do
  1019. local p_minetest = Proto("minetest", "Minetest")
  1020. local minetest_id = 0x4f457403
  1021. local vs_id = {
  1022. [minetest_id] = "Valid"
  1023. }
  1024. local vs_peer = {
  1025. [0] = "Inexistent",
  1026. [1] = "Server"
  1027. }
  1028. local vs_type = {
  1029. [0] = "Control",
  1030. [1] = "Original",
  1031. [2] = "Split",
  1032. [3] = "Reliable"
  1033. }
  1034. local f_id = ProtoField.uint32("minetest.id", "ID", base.HEX, vs_id)
  1035. local f_peer = ProtoField.uint16("minetest.peer", "Peer", base.DEC, vs_peer)
  1036. local f_channel = ProtoField.uint8("minetest.channel", "Channel", base.DEC)
  1037. local f_type = ProtoField.uint8("minetest.type", "Type", base.DEC, vs_type)
  1038. local f_seq = ProtoField.uint16("minetest.seq", "Sequence number", base.DEC)
  1039. local f_subtype = ProtoField.uint8("minetest.subtype", "Subtype", base.DEC, vs_type)
  1040. p_minetest.fields = { f_id, f_peer, f_channel, f_type, f_seq, f_subtype }
  1041. local data_dissector = Dissector.get("data")
  1042. local control_dissector = Dissector.get("minetest.control")
  1043. local client_dissector = Dissector.get("minetest.client")
  1044. local server_dissector = Dissector.get("minetest.server")
  1045. local split_dissector = Dissector.get("minetest.split")
  1046. function p_minetest.dissector(buffer, pinfo, tree)
  1047. -- Add Minetest tree item and verify the ID
  1048. local t = tree:add(p_minetest, buffer(0,8))
  1049. t:add(f_id, buffer(0,4))
  1050. if buffer(0,4):uint() ~= minetest_id then
  1051. t:add_expert_info(PI_UNDECODED, PI_WARN, "Invalid ID, this is not a Minetest packet")
  1052. return
  1053. end
  1054. -- ID is valid, so replace packet's shown protocol
  1055. pinfo.cols.protocol = "Minetest"
  1056. pinfo.cols.info = "Minetest"
  1057. -- Set the other header fields
  1058. t:add(f_peer, buffer(4,2))
  1059. t:add(f_channel, buffer(6,1))
  1060. t:add(f_type, buffer(7,1))
  1061. t:set_text("Minetest, Peer: " .. buffer(4,2):uint() .. ", Channel: " .. buffer(6,1):uint())
  1062. local reliability_info
  1063. if buffer(7,1):uint() == 3 then
  1064. -- Reliable message
  1065. reliability_info = "Seq=" .. buffer(8,2):uint()
  1066. t:set_len(11)
  1067. t:add(f_seq, buffer(8,2))
  1068. t:add(f_subtype, buffer(10,1))
  1069. pos = 10
  1070. else
  1071. -- Unreliable message
  1072. reliability_info = "Unrel"
  1073. pos = 7
  1074. end
  1075. if buffer(pos,1):uint() == 0 then
  1076. -- Control message, possibly reliable
  1077. control_dissector:call(buffer(pos+1):tvb(), pinfo, tree)
  1078. elseif buffer(pos,1):uint() == 1 then
  1079. -- Original message, possibly reliable
  1080. if buffer(4,2):uint() == 1 then
  1081. server_dissector:call(buffer(pos+1):tvb(), pinfo, tree)
  1082. else
  1083. client_dissector:call(buffer(pos+1):tvb(), pinfo, tree)
  1084. end
  1085. elseif buffer(pos,1):uint() == 2 then
  1086. -- Split message, possibly reliable
  1087. if buffer(4,2):uint() == 1 then
  1088. pinfo.cols.info = "Server: Split message"
  1089. else
  1090. pinfo.cols.info = "Client: Split message"
  1091. end
  1092. split_dissector:call(buffer(pos+1):tvb(), pinfo, tree)
  1093. elseif buffer(pos,1):uint() == 3 then
  1094. -- Doubly reliable message??
  1095. t:add_expert_info(PI_MALFORMED, PI_ERROR, "Reliable message wrapped in reliable message")
  1096. else
  1097. data_dissector:call(buffer(pos+1):tvb(), pinfo, tree)
  1098. end
  1099. pinfo.cols.info:append(" (" .. reliability_info .. ")")
  1100. end
  1101. -- FIXME Is there a way to let the dissector table check if the first payload bytes are 0x4f457403?
  1102. DissectorTable.get("udp.port"):add(30000, p_minetest)
  1103. DissectorTable.get("udp.port"):add(30001, p_minetest)
  1104. end
  1105. ------------------------------
  1106. -- Part 6 --
  1107. -- Utility functions part 2 --
  1108. ------------------------------
  1109. -- Checks if a (sub-)Tvb is long enough to be further dissected.
  1110. -- If it is long enough, sets the dissector tree item length to min_len
  1111. -- and returns true. If it is not long enough, adds expert info to the
  1112. -- dissector tree and returns false.
  1113. -- Parameter: tvb: the Tvb
  1114. -- Parameter: min_len: required minimum length
  1115. -- Parameter: t: dissector tree item
  1116. -- Returns: true if tvb:len() >= min_len, false otherwise
  1117. function minetest_check_length(tvb, min_len, t)
  1118. if tvb:len() >= min_len then
  1119. t:set_len(min_len)
  1120. return true
  1121. -- TODO: check if other parts of
  1122. -- the dissector could benefit from reported_length_remaining
  1123. elseif tvb:reported_length_remaining() >= min_len then
  1124. t:add_expert_info(PI_UNDECODED, PI_INFO, "Only part of this packet was captured, unable to decode.")
  1125. return false
  1126. else
  1127. t:add_expert_info(PI_MALFORMED, PI_ERROR, "Message is too short")
  1128. return false
  1129. end
  1130. end
  1131. -- Decodes a variable-length string as ASCII text
  1132. -- t_textlen, t_text should be the ProtoFields created by minetest_field_helper
  1133. -- alternatively t_text can be a ProtoField.string and t_textlen can be nil
  1134. -- lentype must be the type of the length field (as passed to minetest_field_helper)
  1135. -- returns nil if length check failed
  1136. function minetest_decode_helper_ascii(tvb, t, lentype, offset, f_textlen, f_text)
  1137. local n = ({uint16 = 2, uint32 = 4})[lentype]
  1138. assert(n)
  1139. if f_textlen then
  1140. t:add(f_textlen, tvb(offset, n))
  1141. end
  1142. local textlen = tvb(offset, n):uint()
  1143. if minetest_check_length(tvb, offset + n + textlen, t) then
  1144. t:add(f_text, tvb(offset + n, textlen))
  1145. return offset + n + textlen
  1146. end
  1147. end
  1148. -- Decodes a variable-length string as UTF-16 text
  1149. -- (see minetest_decode_helper_ascii)
  1150. function minetest_decode_helper_utf16(tvb, t, lentype, offset, f_textlen, f_text)
  1151. local n = ({uint16 = 2, uint32 = 4})[lentype]
  1152. assert(n)
  1153. if f_textlen then
  1154. t:add(f_textlen, tvb(offset, n))
  1155. end
  1156. local textlen = tvb(offset, n):uint() * 2
  1157. if minetest_check_length(tvb, offset + n + textlen, t) then
  1158. t:add(f_text, tvb(offset + n, textlen), tvb(offset + n, textlen):ustring())
  1159. return offset + n + textlen
  1160. end
  1161. end