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