minetest.lua 42 KB

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