minetest.lua 44 KB

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