1 /* packet-tibia.c
2  * Routines for Tibia/OTServ login and game protocol dissection
3  *
4  * Copyright 2017, Ahmad Fatoum <ahmad[AT]a3f.at>
5  *
6  * A dissector for:
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * SPDX-License-Identifier: GPL-2.0-or-later
12  */
13 
14 
15 /* Tibia (https://tibia.com) is a Massively Multiplayer Online Role-Playing
16  * Game (MMORPG) by Cipsoft GmbH.
17  *
18  * Three official clients exist: The current Qt-based 11.0+ client,
19  * the old C++ client used from Tibia 7.0 till 10.99 and the Flash client.
20  * The latter two are being phased out. They use the same protocol,
21  * except that the session key for the Flash client is transported alongside
22  * the character list over HTTPS. It's possible this is done in the same manner
23  * as in the native client from 10.74 up. We don't support the Flash client.
24  *
25  * The dissector supports Tibia versions from 7.0 (2001) till
26  * 11.00 (2016-10-12). Tibia has an active open source server emulator
27  * community (OTServ) that still makes use of older versions and surpasses
28  * the official servers in popularity, therefore compatibility with older
29  * protocol iterations should be maintained.
30  *
31  * Transport is over TCP, with recent versions encrypting player interaction
32  * with XTEA. Authentication and key exchange is done with a hard-coded
33  * RSA public key in the client.
34  *
35  * Two protocols are dissected: The Tibia login protocol and the Tibia game
36  * protocol. Traditionally, login servers were stateless and only responsible
37  * for providing the addresses of the game servers alongside the character
38  * list upon successful authentication. Then a new authentication request
39  * (this time with character selection) is sent to the game server.
40  * That way, a client who knows the game server address can very well skip
41  * the login server entirely. Starting with 10.61, this is no longer possible,
42  * as the login server provides a session key that needs to be sent to the
43  * game server.
44  *
45  * Starting with Tibia 7.61, login server requests can't be reliably
46  * differentiated from game server requests. Therefore we apply some heuristics
47  * to classify packets.
48  *
49  * Starting with Tibia 11.01, a web service takes the role of the login server.
50  * Starting with Tibia 11.11, the Adler32 checksum was replaced by a 32-bit
51  * sequence number. The most significant bit indicates whether the packet was
52  * DEFLATE-compressed. These features are not yet supported.
53  *
54  * Packets from and to the game server contain commands. Commands are
55  * identified by the first octet and are variable in length. The dissector has
56  * most command names hard-coded. However, a complete implementation of the
57  * game protocol is unlikely.
58  *
59  * The RSA private key usually used by OTServ is hard-coded in. Server
60  * administrators may add their own private key in PEM or PKCS#12 format over
61  * an UAT. For servers where the private key is indeed private (like
62  * for official servers), the symmetric XTEA key (retrievable by memory
63  * peeking or MitM) may be provided to the dissector via UAT.
64  *
65  * Unsurprisingly, no official specification of the protocol exist, following
66  * resources have been written by the community:
67  *
68  * - OTServ: Community effort to replicate a Tibia Server.
69  * - Outcast: A Tibia client implementation of the game protocol as of 2006.
70  *            Comes with a PDF spec written by Khaos
71  * - TibiaAPI: Bot framework, containing a listing of commands as of 2009
72  * - TFS: OTServ-Fork which is kept up-to-date with most of the official protocol
73  * - otclient: Open Source implementation of an up-to-date Tibia client
74  *
75  * An official slide set by Cipsoft detailing the architecture of Tibia
76  * from Game Developers Conference Europe 2011 is also available:
77  * http://www.gdcvault.com/play/1014908/Inside-Tibia-The-Technical-Infrastructure
78  *
79  * The login protocol, as implemented here, has been inferred from network
80  * footage and game client execution traces and was written from scratch.
81  * The listing of game protocol commands were taken from TibiaAPI and Khaos' spec
82  * No code of Cipsoft GmbH was used.
83  *
84  * Tibia is a registered trademark of Cipsoft GmbH.
85  */
86 
87 #include "config.h"
88 #include <epan/packet.h>
89 #include "packet-tcp.h"
90 #include <wsutil/adler32.h>
91 #include <epan/address.h>
92 #include <epan/to_str.h>
93 #include <epan/prefs.h>
94 #include <epan/uat.h>
95 #include <epan/conversation.h>
96 #include <epan/value_string.h>
97 #include <epan/expert.h>
98 #include <epan/address.h>
99 #include <wsutil/filesystem.h>
100 #include <wsutil/file_util.h>
101 #include <wsutil/wsgcrypt.h>
102 #include <wsutil/report_message.h>
103 #include <wsutil/xtea.h>
104 #include <wsutil/strtoi.h>
105 #include <wsutil/rsa.h>
106 #include <errno.h>
107 #include <epan/ws_printf.h>
108 #include <epan/ptvcursor.h>
109 
110 void proto_register_tibia(void);
111 void proto_reg_handoff_tibia(void);
112 
113 /* preferences */
114 static gboolean try_otserv_key          = TRUE,
115                 show_char_name          = TRUE,
116                 show_acc_info           = TRUE,
117                 show_xtea_key           = FALSE,
118                 dissect_game_commands   = FALSE,
119                 reassemble_tcp_segments = TRUE;
120 
121 /* User Access Tables */
122 #if HAVE_LIBGNUTLS
123 struct rsakey {
124     address addr;
125     guint16 port;
126 
127     gcry_sexp_t privkey;
128 };
129 GHashTable *rsakeys;
130 
131 struct rsakeys_assoc {
132     char *ipaddr;
133     char *port;
134 
135     char *keyfile;
136     char *password;
137 };
138 
139 UAT_CSTRING_CB_DEF(rsakeylist_uats,  ipaddr,   struct rsakeys_assoc)
140 UAT_CSTRING_CB_DEF(rsakeylist_uats,  port,     struct rsakeys_assoc)
141 UAT_FILENAME_CB_DEF(rsakeylist_uats, keyfile,  struct rsakeys_assoc)
142 UAT_CSTRING_CB_DEF(rsakeylist_uats,  password, struct rsakeys_assoc)
143 
144 static void rsakey_free(void *_rsakey);
145 
146 static uat_t *rsakeys_uat = NULL;
147 static struct rsakeys_assoc  *rsakeylist_uats = NULL;
148 static guint nrsakeys = 0;
149 #endif
150 
151 #define XTEA_KEY_LEN 16
152 
153 struct xteakeys_assoc {
154     guint32 framenum;
155 
156     char *key;
157 };
158 GHashTable *xteakeys;
159 
160 static void *xteakeys_copy_cb(void *, const void *, size_t);
161 static void xteakeys_free_cb(void *);
162 static void xtea_parse_uat(void);
163 static gboolean xteakeys_uat_fld_key_chk_cb(void *, const char *, guint, const void *, const void *, char **);
164 
165 UAT_DEC_CB_DEF(xteakeylist_uats, framenum, struct xteakeys_assoc)
166 UAT_CSTRING_CB_DEF(xteakeylist_uats, key, struct xteakeys_assoc)
167 
168 static uat_t *xteakeys_uat = NULL;
169 static struct xteakeys_assoc *xteakeylist_uats = NULL;
170 static guint nxteakeys = 0;
171 
172 #define COND_POISONED     0x1
173 #define COND_BURNING      0x2
174 #define COND_ELECTROCUTED 0x4
175 #define COND_DRUNK        0x8
176 #define COND_MANASHIELD   0x10
177 #define COND_PARALYZED    0x20
178 #define COND_HASTE        0x40
179 #define COND_BATTLE       0x80
180 #define COND_DROWNING     0x100
181 #define COND_FREEZING     0x200
182 #define COND_DAZZLED      0x400
183 #define COND_CURSED       0x800
184 #define COND_BUFF         0x1000
185 #define COND_PZBLOCK      0x2000
186 #define COND_PZ           0x4000
187 #define COND_BLEEDING     0x8000
188 #define COND_HUNGRY       0x10000
189 
190 /* The login server has been traditionally on 7171,
191  * For OTServ, the game server often listens on the same IP/port,
192  * but occasionally on 7172. Official Tibia doesn't host login and
193  * game servers on the same IP address
194  */
195 
196 #define TIBIA_DEFAULT_TCP_PORT_RANGE "7171,7172"
197 
198 static gint proto_tibia = -1;
199 
200 static gint hf_tibia_len                     = -1;
201 static gint hf_tibia_nonce                   = -1;
202 static gint hf_tibia_adler32                 = -1;
203 static gint hf_tibia_adler32_status          = -1;
204 static gint hf_tibia_os                      = -1;
205 static gint hf_tibia_proto_version           = -1;
206 static gint hf_tibia_client_version          = -1;
207 static gint hf_tibia_file_versions           = -1;
208 static gint hf_tibia_file_version_spr        = -1;
209 static gint hf_tibia_file_version_dat        = -1;
210 static gint hf_tibia_file_version_pic        = -1;
211 static gint hf_tibia_game_preview_state      = -1;
212 static gint hf_tibia_content_revision        = -1;
213 static gint hf_tibia_undecoded_rsa_data      = -1;
214 static gint hf_tibia_undecoded_xtea_data     = -1;
215 static gint hf_tibia_unknown                 = -1;
216 static gint hf_tibia_xtea_key                = -1;
217 static gint hf_tibia_loginflags_gm           = -1;
218 static gint hf_tibia_acc_name                = -1;
219 static gint hf_tibia_acc_number              = -1;
220 static gint hf_tibia_session_key             = -1;
221 static gint hf_tibia_char_name               = -1;
222 static gint hf_tibia_acc_pass                = -1;
223 static gint hf_tibia_char_name_convo         = -1;
224 static gint hf_tibia_acc_name_convo          = -1;
225 static gint hf_tibia_acc_pass_convo          = -1;
226 static gint hf_tibia_session_key_convo       = -1;
227 
228 static gint hf_tibia_client_info             = -1;
229 static gint hf_tibia_client_locale           = -1;
230 static gint hf_tibia_client_locale_id        = -1;
231 static gint hf_tibia_client_locale_name      = -1;
232 static gint hf_tibia_client_ram              = -1;
233 static gint hf_tibia_client_cpu              = -1;
234 static gint hf_tibia_client_cpu_name         = -1;
235 static gint hf_tibia_client_clock            = -1;
236 static gint hf_tibia_client_clock2           = -1;
237 static gint hf_tibia_client_gpu              = -1;
238 static gint hf_tibia_client_vram             = -1;
239 static gint hf_tibia_client_resolution       = -1;
240 static gint hf_tibia_client_resolution_x     = -1;
241 static gint hf_tibia_client_resolution_y     = -1;
242 static gint hf_tibia_client_resolution_hz    = -1;
243 
244 static gint hf_tibia_payload_len             = -1;
245 static gint hf_tibia_loginserv_command       = -1;
246 static gint hf_tibia_gameserv_command        = -1;
247 static gint hf_tibia_client_command          = -1;
248 
249 static gint hf_tibia_motd                    = -1;
250 static gint hf_tibia_dlg_error               = -1;
251 static gint hf_tibia_dlg_info                = -1;
252 
253 static gint hf_tibia_charlist                = -1;
254 static gint hf_tibia_charlist_length         = -1;
255 static gint hf_tibia_charlist_entry_name     = -1;
256 static gint hf_tibia_charlist_entry_world    = -1;
257 static gint hf_tibia_charlist_entry_ip       = -1;
258 static gint hf_tibia_charlist_entry_port     = -1;
259 
260 static gint hf_tibia_worldlist               = -1;
261 static gint hf_tibia_worldlist_length        = -1;
262 static gint hf_tibia_worldlist_entry_name    = -1;
263 static gint hf_tibia_worldlist_entry_ip      = -1;
264 static gint hf_tibia_worldlist_entry_port    = -1;
265 static gint hf_tibia_worldlist_entry_preview = -1;
266 static gint hf_tibia_worldlist_entry_id      = -1;
267 static gint hf_tibia_pacc_days               = -1;
268 
269 static gint hf_tibia_channel_id              = -1;
270 static gint hf_tibia_channel_name            = -1;
271 
272 static gint hf_tibia_char_cond               = -1;
273 static gint hf_tibia_char_cond_poisoned      = -1;
274 static gint hf_tibia_char_cond_burning       = -1;
275 static gint hf_tibia_char_cond_electrocuted  = -1;
276 static gint hf_tibia_char_cond_drunk         = -1;
277 static gint hf_tibia_char_cond_manashield    = -1;
278 static gint hf_tibia_char_cond_paralyzed     = -1;
279 static gint hf_tibia_char_cond_haste         = -1;
280 static gint hf_tibia_char_cond_battle        = -1;
281 static gint hf_tibia_char_cond_drowning      = -1;
282 static gint hf_tibia_char_cond_freezing      = -1;
283 static gint hf_tibia_char_cond_dazzled       = -1;
284 static gint hf_tibia_char_cond_cursed        = -1;
285 static gint hf_tibia_char_cond_buff          = -1;
286 static gint hf_tibia_char_cond_pzblock       = -1;
287 static gint hf_tibia_char_cond_pz            = -1;
288 static gint hf_tibia_char_cond_bleeding      = -1;
289 static gint hf_tibia_char_cond_hungry        = -1;
290 
291 static int * const char_conds[] = {
292     &hf_tibia_char_cond_poisoned,
293     &hf_tibia_char_cond_burning,
294     &hf_tibia_char_cond_electrocuted,
295     &hf_tibia_char_cond_drunk,
296     &hf_tibia_char_cond_manashield,
297     &hf_tibia_char_cond_paralyzed,
298     &hf_tibia_char_cond_haste,
299     &hf_tibia_char_cond_battle,
300     &hf_tibia_char_cond_drowning,
301     &hf_tibia_char_cond_freezing,
302     &hf_tibia_char_cond_dazzled,
303     &hf_tibia_char_cond_cursed,
304     &hf_tibia_char_cond_buff,
305     &hf_tibia_char_cond_pzblock,
306     &hf_tibia_char_cond_pz,
307     &hf_tibia_char_cond_bleeding,
308     &hf_tibia_char_cond_hungry,
309     NULL
310 };
311 
312 static gint hf_tibia_chat_msg            = -1;
313 static gint hf_tibia_speech_type         = -1;
314 
315 static gint hf_tibia_coords_x            = -1;
316 static gint hf_tibia_coords_y            = -1;
317 static gint hf_tibia_coords_z            = -1;
318 static gint hf_tibia_coords              = -1;
319 static gint hf_tibia_stackpos            = -1;
320 
321 #if 0
322 static gint hf_tibia_item                = -1;
323 #endif
324 static gint hf_tibia_container           = -1;
325 static gint hf_tibia_container_icon      = -1;
326 static gint hf_tibia_container_slot      = -1;
327 static gint hf_tibia_container_slots     = -1;
328 static gint hf_tibia_inventory           = -1;
329 static gint hf_tibia_vip                 = -1;
330 static gint hf_tibia_vip_online          = -1;
331 static gint hf_tibia_player              = -1;
332 static gint hf_tibia_creature            = -1;
333 static gint hf_tibia_creature_health     = -1;
334 static gint hf_tibia_window              = -1;
335 static gint hf_tibia_window_icon         = -1;
336 static gint hf_tibia_window_textlen      = -1;
337 static gint hf_tibia_window_text         = -1;
338 
339 static gint hf_tibia_light_level         = -1;
340 static gint hf_tibia_light_color         = -1;
341 static gint hf_tibia_magic_effect_id     = -1;
342 static gint hf_tibia_animated_text_color = -1;
343 static gint hf_tibia_animated_text       = -1;
344 static gint hf_tibia_projectile          = -1;
345 static gint hf_tibia_squarecolor         = -1;
346 static gint hf_tibia_textmsg_class       = -1;
347 static gint hf_tibia_textmsg             = -1;
348 static gint hf_tibia_walk_dir            = -1;
349 
350 
351 static gint ett_tibia               = -1;
352 static gint ett_command             = -1;
353 static gint ett_file_versions       = -1;
354 static gint ett_client_info         = -1;
355 static gint ett_locale              = -1;
356 static gint ett_cpu                 = -1;
357 static gint ett_resolution          = -1;
358 static gint ett_charlist            = -1;
359 static gint ett_worldlist           = -1;
360 static gint ett_char                = -1;
361 static gint ett_world               = -1;
362 static gint ett_coords              = -1;
363 static gint ett_char_cond           = -1;
364 
365 
366 static expert_field ei_xtea_len_toobig               = EI_INIT;
367 static expert_field ei_adler32_checksum_bad          = EI_INIT;
368 static expert_field ei_rsa_plaintext_no_leading_zero = EI_INIT;
369 static expert_field ei_rsa_ciphertext_too_short      = EI_INIT;
370 static expert_field ei_rsa_decrypt_failed            = EI_INIT;
371 
372 
373 struct proto_traits {
374     guint32 adler32:1, rsa:1, compression:1, xtea:1, login_webservice:1, acc_name:1, nonce:1,
375             extra_gpu_info:1, gmbyte:1, hwinfo:1;
376     guint32 outfit_addons:1, stamina:1, lvl_on_msg:1;
377     guint32 ping:1, client_version:1, game_preview:1, auth_token:1, session_key:1;
378     guint32 game_content_revision:1, worldlist_in_charlist:1;
379     guint string_enc;
380 };
381 
382 struct tibia_convo {
383     guint32 xtea_key[XTEA_KEY_LEN / sizeof (guint32)];
384     guint32 xtea_framenum;
385     const guint8 *acc, *pass, *char_name, *session_key;
386     struct proto_traits has;
387 
388     guint16 proto_version;
389     guint8 loginserv_is_peer :1;
390     guint16 clientport;
391     guint16 servport;
392 
393     gcry_sexp_t privkey;
394 };
395 
396 static struct proto_traits
get_version_traits(guint16 version)397 get_version_traits(guint16 version)
398 {
399     struct proto_traits has;
400     memset(&has, 0, sizeof has);
401     has.gmbyte = TRUE; /* Not sure when the GM byte first appeared */
402     has.string_enc = ENC_ISO_8859_1;
403 
404     if (version >= 761) /* 761 was a test client. 770 was the first release */
405         has.xtea = has.rsa = TRUE;
406     if (version >= 780)
407         has.outfit_addons = has.stamina = has.lvl_on_msg = TRUE;
408     if (version >= 830)
409         has.adler32 = has.acc_name = TRUE;
410     if (version >= 841)
411         has.hwinfo = has.nonce = TRUE;
412     if (version >= 953)
413         has.ping = TRUE;
414     if (version >= 980)
415         has.client_version = has.game_preview = TRUE;
416     if (version >= 1010)
417         has.worldlist_in_charlist = TRUE;
418     if (version >= 1061)
419         has.extra_gpu_info = TRUE;
420     if (version >= 1071)
421         has.game_content_revision = TRUE;
422     if (version >= 1072)
423         has.auth_token = TRUE;
424     if (version >= 1074)
425         has.session_key = TRUE;
426     if (version >= 1101)
427         has.login_webservice = TRUE;
428     if (version >= 1111) {
429         has.compression = TRUE; /* with DEFLATE */
430         has.adler32 = FALSE;
431     }
432 #if 0 /* With the legacy client being phased out, maybe Unicode support incoming? */
433     if (version >= 11xy)
434         has.string_enc = ENC_UTF_8;
435 #endif
436 
437     return has;
438 }
439 
440 static guint16
get_version_get_charlist_packet_size(struct proto_traits * has)441 get_version_get_charlist_packet_size(struct proto_traits *has)
442 {
443     guint16 size = 2;
444     if (has->adler32 || has->compression)
445         size += 4;
446     size += 17;
447     if (has->extra_gpu_info)
448         size += 222;
449     if (has->rsa)
450         size += 128;
451 
452     return size;
453 }
454 static guint16
get_version_char_login_packet_size(struct proto_traits * has)455 get_version_char_login_packet_size(struct proto_traits *has)
456 {
457     guint16 size = 2;
458     if (has->adler32 || has->compression)
459         size += 4;
460     size += 5;
461     if (has->client_version)
462         size += 4;
463     if (has->game_content_revision)
464         size += 2;
465     if (has->game_preview)
466         size += 1;
467     if (has->rsa)
468         size += 128;
469 
470     return size;
471 }
472 
473 
474 #define XTEA_FROM_UAT 0
475 #define XTEA_UNKNOWN  0xFFFFFFFF
476 
477 static struct tibia_convo *
tibia_get_convo(packet_info * pinfo)478 tibia_get_convo(packet_info *pinfo)
479 {
480     conversation_t *epan_conversation = find_or_create_conversation(pinfo);
481 
482     struct tibia_convo *convo = (struct tibia_convo*)conversation_get_proto_data(epan_conversation, proto_tibia);
483 
484     if (!convo) {
485         address *servaddr;
486         convo = wmem_new0(wmem_file_scope(), struct tibia_convo);
487 
488         /* FIXME there gotta be a cleaner way... */
489         if (pinfo->srcport >= 0xC000) {
490             convo->clientport = pinfo->srcport;
491 
492             convo->servport = pinfo->destport;
493             servaddr = &pinfo->dst;
494         } else {
495             convo->clientport = pinfo->destport;
496 
497             convo->servport = pinfo->srcport;
498             servaddr = &pinfo->src;
499         }
500         (void)servaddr;
501 #ifdef HAVE_LIBGNUTLS
502         struct rsakey rsa_key;
503         rsa_key.port = convo->servport;
504         rsa_key.addr = *servaddr;
505         convo->privkey = (gcry_sexp_t)g_hash_table_lookup(rsakeys, &rsa_key);
506 #endif
507         convo->xtea_framenum = XTEA_UNKNOWN;
508 
509         conversation_add_proto_data(epan_conversation, proto_tibia, (void *)convo);
510     }
511 
512     if (convo->xtea_framenum == XTEA_UNKNOWN) {
513         guint8 *xtea_key = (guint8*)g_hash_table_lookup(xteakeys, GUINT_TO_POINTER(pinfo->num));
514         if (xtea_key) {
515             memcpy(convo->xtea_key, xtea_key, XTEA_KEY_LEN);
516             convo->xtea_framenum = XTEA_FROM_UAT;
517         }
518     }
519 
520     return convo;
521 }
522 
523 static guint32
ipv4tonl(const char * str)524 ipv4tonl(const char *str)
525 {
526         guint32 ipaddr = 0;
527         for (int octet = 0; octet < 4; octet++) {
528             ws_strtou8(str, &str, &((guint8*)&ipaddr)[octet]);
529             str++;
530         }
531         return ipaddr;
532 }
533 
534 static void
register_gameserv_addr(struct tibia_convo * convo,guint32 ipaddr,guint16 port)535 register_gameserv_addr(struct tibia_convo *convo, guint32 ipaddr, guint16 port)
536 {
537     (void)convo; (void)ipaddr; (void)port;
538 #if HAVE_LIBGNUTLS
539     /* Game servers in the list inherit the same RSA key as the login server */
540     if (convo->has.rsa) {
541         struct rsakey *entry = g_new(struct rsakey, 1);
542         alloc_address_wmem(NULL, &entry->addr, AT_IPv4, sizeof ipaddr, &ipaddr);
543         entry->port = port;
544         entry->privkey = NULL;
545         if (g_hash_table_lookup(rsakeys, entry) == NULL) {
546             entry->privkey = convo->privkey;
547             g_hash_table_insert(rsakeys, entry, entry->privkey);
548         } else {
549             rsakey_free(entry);
550         }
551     }
552 
553     /* TODO Mark all communication with the IP/Port pair above
554      * as Tibia communication. How?
555      */
556 #endif
557 }
558 
559 static gcry_sexp_t otserv_key;
560 static gcry_sexp_t
convo_get_privkey(struct tibia_convo * convo)561 convo_get_privkey(struct tibia_convo *convo)
562 {
563     return convo->privkey ? convo->privkey
564          : try_otserv_key ? otserv_key
565          : NULL;
566 }
567 
568 enum client_cmd {
569     /* from TibiaAPI */
570     C_GET_CHARLIST          = 0x01,
571     C_LOGIN_CHAR            = 0x0A,
572     C_LOGOUT                = 0x14, /* I think this is a 7.7+ thing */
573     C_PONG                  = 0x1E,
574 
575     C_AUTO_WALK             = 0x64,
576     C_GO_NORTH              = 0x65,
577     C_GO_EAST               = 0x66,
578     C_GO_SOUTH              = 0x67,
579     C_GO_WEST               = 0x68,
580     C_AUTO_WALK_CANCEL      = 0x69,
581     C_GO_NE                 = 0x6A,
582     C_GO_SE                 = 0x6B,
583     C_GO_SW                 = 0x6C,
584     C_GO_NW                 = 0x6D,
585     C_TURN_NORTH            = 0x6F,
586     C_TURN_EAST             = 0x70,
587     C_TURN_SOUTH            = 0x71,
588     C_TURN_WEST             = 0x72,
589     C_MOVE_ITEM             = 0x78,
590     C_SHOP_BUY              = 0x7A,
591     C_SHOP_SELL             = 0x7B,
592     C_SHOP_CLOSE            = 0x7C,
593     C_ITEM_USE              = 0x82,
594     C_ITEM_USE_ON           = 0x83,
595     C_ITEM_USE_BATTLELIST   = 0x84,
596     C_ITEM_ROTATE           = 0x85,
597     C_CONTAINER_CLOSE       = 0x87,
598     C_CONTAINER_OPEN_PARENT = 0x88,
599     C_LOOK_AT               = 0x8C,
600     C_PLAYER_SPEECH         = 0x96,
601     C_CHANNEL_LIST          = 0x97,
602     C_CHANNEL_OPEN          = 0x98,
603     C_CHANNEL_CLOSE         = 0x99,
604     C_PRIVATE_CHANNEL_OPEN  = 0x9A,
605     C_NPC_CHANNEL_CLOSE     = 0x9E,
606     C_FIGHT_MODES           = 0xA0,
607     C_ATTACK                = 0xA1,
608     C_FOLLOW                = 0xA2,
609     C_CANCEL_GO             = 0xBE,
610     C_TILE_UPDATE           = 0xC9,
611     C_CONTAINER_UPDATE      = 0xCA,
612     C_SET_OUTFIT            = 0xD3,
613     C_VIP_ADD               = 0xDC,
614     C_VIP_REMOVE            = 0xDD
615  };
616 
617 static const value_string from_client_packet_types[] = {
618     { C_GET_CHARLIST,     "Charlist request" },
619     { C_LOGIN_CHAR,       "Character login" },
620 
621     { C_LOGOUT,           "Logout" },
622     { C_PONG,             "Pong" },
623 
624     { C_AUTO_WALK,        "Map walk" },
625     { C_GO_NORTH,         "Go north"},
626     { C_GO_EAST,          "Go east"},
627     { C_GO_SOUTH,         "Go south"},
628     { C_GO_WEST,          "Go west"},
629     { C_AUTO_WALK_CANCEL, "Map walk cancel" },
630     { C_GO_NE,            "Go north-east"},
631     { C_GO_SE,            "Go south-east"},
632     { C_GO_SW,            "Go south-west"},
633     { C_GO_NW,            "Go north-west"},
634 
635     { C_TURN_NORTH,      "Turn north" },
636     { C_TURN_EAST,       "Turn east" },
637     { C_TURN_SOUTH,      "Turn south" },
638     { C_TURN_WEST,       "Turn west" },
639     { C_MOVE_ITEM,       "Move item" },
640     { C_SHOP_BUY,        "Buy in shop" },
641     { C_SHOP_SELL,       "Sell in shop" },
642     { C_SHOP_CLOSE,      "Close shop" },
643     { C_ITEM_USE,        "Use item" },
644     { C_ITEM_USE_ON,     "Use item on" },
645     { C_ITEM_USE_BATTLELIST,   "Use item on battle list" },
646     { C_ITEM_ROTATE,           "Rotate item" },
647 
648     { C_CONTAINER_CLOSE,       "Close container" },
649     { C_CONTAINER_OPEN_PARENT, "Open parent container" },
650     { C_LOOK_AT,               "Look at" },
651     { C_PLAYER_SPEECH,         "Speech" },
652     { C_CHANNEL_LIST,          "List channels" },
653     { C_CHANNEL_OPEN,          "Open public channel" },
654     { C_CHANNEL_CLOSE,         "close channel" },
655     { C_PRIVATE_CHANNEL_OPEN,  "Open private channel" },
656     { C_NPC_CHANNEL_CLOSE,     "Open NPC channel" },
657     { C_FIGHT_MODES,           "Set fight modes" },
658     { C_ATTACK,                "Attack" },
659     { C_FOLLOW,                "Follow" },
660     { C_CANCEL_GO,             "Cancel go" },
661     { C_TILE_UPDATE,           "Update tile" },
662     { C_CONTAINER_UPDATE,      "Update container" },
663     { C_SET_OUTFIT,            "Set outfit" },
664     { C_VIP_ADD,               "Add VIP" },
665     { C_VIP_REMOVE,            "Remove VIP" },
666 
667     { 0, NULL }
668 };
669 
670 static value_string_ext from_client_packet_types_ext = VALUE_STRING_EXT_INIT(from_client_packet_types);
671 
672 enum loginserv_cmd {
673     LOGINSERV_DLG_ERROR    = 0x0A,
674     LOGINSERV_DLG_ERROR2   = 0x0B,
675     LOGINSERV_DLG_MOTD     = 0x14,
676     LOGINSERV_SESSION_KEY  = 0x28,
677     LOGINSERV_DLG_CHARLIST = 0x64
678 };
679 
680 static const value_string from_loginserv_packet_types[] = {
681     { LOGINSERV_DLG_ERROR,    "Error" },
682     { LOGINSERV_DLG_ERROR2,   "Error" },
683     { LOGINSERV_DLG_MOTD,     "MOTD" },
684     { LOGINSERV_SESSION_KEY,  "Session key" },
685     { LOGINSERV_DLG_CHARLIST, "Charlist" },
686 
687     { 0, NULL }
688 };
689 
690 enum gameserv_cmd {
691     /* Credit to Khaos (OBJECT Networks). Values and comments extracted from PDF table */
692     S_MAPINIT =                0x0A, /* Long playerCreatureId Int unknownU16 (Byte reportBugs?) */
693     S_GMACTIONS =              0x0B, /* Used to be 32 unknown bytes, but with GMs removed it                                      might not be in use anymore */
694     S_DLG_ERROR =              0x14, /* String errorMessage */
695     S_DLG_INFO =               0x15,
696     S_DLG_TOOMANYPLAYERS =     0x16,
697     S_PING =                   0x1E,
698     S_NONCE =                  0x1F,
699     S_PLAYERLOC =              0x64, /* Coord pos */
700     S_GO_NORTH =               0x65, /* MapDescription (18,1) */
701     S_GO_EAST =                0x66, /* MapDescription (1,14) */
702     S_GO_SOUTH =               0x67, /* MapDescription (18,1) */
703     S_GO_WEST =                0x68, /* MapDescription (1,14) */
704     S_TILEUPDATE =             0x69, /* Coord pos TileDescription td */
705     S_ADDITEM =                0x6a, /* Coord pos ThingDescription thing */
706     S_REPLACEITEM =            0x6b, /* Coord pos Byte stackpos ThingDescription thing */
707     S_REMOVEITEM =             0x6c, /* Coord pos Byte stackpos */
708     S_MOVE_THING =             0x6d,
709     S_CONTAINER =              0x6e, /* Byte index Short containerIcon Byte slotCount ThingDescription item */
710     S_CONTAINERCLOSE =         0x6f, /* Byte index */
711     S_ADDITEMCONTAINER =       0x70, /* Byte index ThingDescription itm */
712     S_TRANSFORMITEMCONTAINER = 0x71, /* Byte index Byte slot */
713     S_REMOVEITEMCONTAINER =    0x72, /* Byte index Byte slot */
714     S_INVENTORYEMPTY =         0x78, /* Byte invSlot */
715     S_INVENTORYITEM =          0x79, /* Byte invSlot ThingDescription itm */
716     S_TRADEREQ =               0x7d, /* String otherperson Byte slotCount ThingDescription itm */
717     S_TRADEACK =               0x7e, /* String otherperson Byte slotCount ThingDescription itm */
718     S_TRADECLOSE =             0x7f,
719     S_LIGHTLEVEL =             0x82, /* Byte lightlevel Byte lightcolor */
720     S_MAGIC_EFFECT =           0x83,
721     S_ANIMATEDTEXT =           0x84, /* Coord pos Byte color String message */
722     S_DISTANCESHOT =           0x85, /* Coord pos1 Byte stackposition Coord pos2 */
723     S_CREATURESQUARE =         0x86, /* Long creatureid Byte squarecolor */
724     S_CREATURE_HEALTH =        0x8C,
725     S_CREATURELIGHT =          0x8d, /* Long creatureid Byte ? Byte ? */
726     S_SETOUTFIT =              0x8e, /* Long creatureid Byte lookType Byte headType Byte bodyType Byte legsType Byte feetType // can extended look go here too? */
727     S_CREATURESPEED =          0x8f, /* YIKES! I didnt handle this! */
728     S_TEXTWINDOW =             0x96, /* Long windowId Byte icon Byte maxlength String message */
729     S_STATUSMSG =              0xA0, /* Status status */
730     S_SKILLS =                 0xA1, /* Skills skills */
731     S_PLAYER_CONDITION =       0xA2,
732     S_CANCELATTACK =           0xA3,
733     S_SPEAK =                  0xAA,
734     S_CHANNELSDIALOG =         0xAB, /* Byte channelCount (Int channelId String channelName) */
735     S_CHANNEL_OPEN =           0xAC,
736     S_OPENPRIV =               0xAD, /* String playerName */
737     S_TEXTMESSAGE =            0xB4, /* Byte msgClass String string */
738     S_CANCELWALK =             0xB5, /* Byte direction */
739     S_FLOORUP =                0xBE, /* Advanced topic; read separate text */
740     S_FLOORDOWN =              0xBF, /* Advanced topic; read separate text */
741     S_OUTFITLIST =             0xC8, /* Byte lookType Byte headType Byte bodyType Byte legsType Byte feetType Byte firstModel Byte lastModel */
742     S_VIPADD =                 0xD2, /* long guid string name byte isonline */
743     S_VIPLOGIN =               0xD3, /* long guid */
744     S_VIPLOGOUT =              0xD4  /* long guid*/
745 };
746 static const value_string from_gameserv_packet_types[] = {
747 
748     { S_MAPINIT,            "Initialize map" },
749     { S_GMACTIONS,          "GM actions" },
750     { S_DLG_ERROR,          "Error" },
751     { S_DLG_INFO,           "Info" },
752     { S_DLG_TOOMANYPLAYERS, "Too many players" },
753     { S_PING,               "Ping" },
754     { S_NONCE,              "Nonce" },
755     { S_PLAYERLOC,      "Set player location" },
756     { S_GO_NORTH,       "Go north" },
757     { S_GO_EAST,        "Go east" },
758     { S_GO_SOUTH,       "Go south" },
759     { S_GO_WEST,        "Go west" },
760     { S_TILEUPDATE,     "Update tile" },
761     { S_ADDITEM,        "Add item" },
762     { S_REPLACEITEM,    "Replace item" },
763     { S_REMOVEITEM,     "Remove item" },
764     { S_MOVE_THING,     "Move thing" },
765     { S_CONTAINER,      "Open container" },
766     { S_CONTAINERCLOSE, "Close container" },
767 
768     { S_ADDITEMCONTAINER,       "Add item in container" },
769     { S_TRANSFORMITEMCONTAINER, "Transform item in container" },
770     { S_REMOVEITEMCONTAINER,    "Remove item in container" },
771 
772     { S_INVENTORYEMPTY,   "Inventory empty" },
773     { S_INVENTORYITEM,    "Inventory item" },
774     { S_TRADEREQ,         "Trade request" },
775     { S_TRADEACK,         "Trade acknowledge" },
776     { S_TRADECLOSE,       "Trade over" },
777     { S_LIGHTLEVEL,       "Light level" },
778     { S_MAGIC_EFFECT,     "Magic effect" },
779     { S_ANIMATEDTEXT,     "Animated text" },
780     { S_DISTANCESHOT,     "Distance shot" },
781     { S_CREATURESQUARE,   "Creature square" },
782     { S_CREATURE_HEALTH,  "Creature health" },
783     { S_CREATURELIGHT,    "Creature light" },
784     { S_SETOUTFIT,        "Set outfit" },
785     { S_CREATURESPEED,    "Set creature speed" },
786     { S_TEXTWINDOW,       "Text window" },
787     { S_STATUSMSG,        "Status message" },
788     { S_SKILLS,           "Skills" },
789     { S_PLAYER_CONDITION, "Player condition" },
790     { S_CANCELATTACK,     "Cancel attack" },
791     { S_SPEAK,            "Creature speech" },
792     { S_CHANNELSDIALOG,   "Channels dialog" },
793     { S_CHANNEL_OPEN,     "Channel open" },
794     { S_OPENPRIV,         "Private channel open" },
795     { S_TEXTMESSAGE,      "Text message" },
796     { S_CANCELWALK,       "Cancel walk" },
797     { S_FLOORUP,          "Floor +1" },
798     { S_FLOORDOWN,        "Floor -1" },
799     { S_OUTFITLIST,       "Outfit list" },
800     { S_VIPADD,           "Add VIP" },
801     { S_VIPLOGIN,         "VIP login" },
802     { S_VIPLOGOUT,        "VIP logout" },
803 
804     { 0, NULL }
805 };
806 
807 static value_string_ext from_gameserv_packet_types_ext = VALUE_STRING_EXT_INIT(from_gameserv_packet_types);
808 
809 static const unit_name_string mb_unit = {"MB", NULL};
810 
811 static int
dissect_loginserv_packet(struct tibia_convo * convo,tvbuff_t * tvb,int offset,int len,packet_info * pinfo,proto_tree * tree,gboolean first_fragment)812 dissect_loginserv_packet(struct tibia_convo *convo, tvbuff_t *tvb, int offset, int len, packet_info *pinfo, proto_tree *tree, gboolean first_fragment )
813 {
814     ptvcursor_t *ptvc = ptvcursor_new(pinfo->pool, tree, tvb, offset);
815 
816     col_append_str(pinfo->cinfo, COL_INFO, first_fragment ? " commands:" : ",");
817     len += offset;
818 
819     if (ptvcursor_current_offset(ptvc) < len) {
820         for (;;) {
821             int cmd = tvb_get_guint8(tvb, ptvcursor_current_offset(ptvc));
822             ptvcursor_add_with_subtree(ptvc, hf_tibia_loginserv_command, 1, convo->has.string_enc, ett_command);
823             ptvcursor_advance(ptvc, 1);
824 
825             switch ((enum loginserv_cmd)cmd) {
826                 case LOGINSERV_DLG_ERROR:
827                 case LOGINSERV_DLG_ERROR2:
828                     ptvcursor_add(ptvc, hf_tibia_dlg_error, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
829                     break;
830                 case LOGINSERV_DLG_MOTD:
831                     ptvcursor_add(ptvc, hf_tibia_motd, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
832                     break;
833                 case LOGINSERV_SESSION_KEY:
834                     ptvcursor_add(ptvc, hf_tibia_session_key, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
835                     break;
836                 case LOGINSERV_DLG_CHARLIST:
837                     if (convo->has.worldlist_in_charlist) {
838                         guint8 world_count = tvb_get_guint8(tvb, ptvcursor_current_offset(ptvc));
839                         ptvcursor_add(ptvc, hf_tibia_worldlist_length, 1, ENC_NA);
840                         /* Empty character list? */
841                         if (world_count) {
842                             ptvcursor_add_with_subtree(ptvc, hf_tibia_worldlist, SUBTREE_UNDEFINED_LENGTH, ENC_NA, ett_worldlist);
843                             while (world_count--) {
844                                 proto_item *it = ptvcursor_add(ptvc, hf_tibia_worldlist_entry_id, 1, ENC_NA);
845                                 ptvcursor_push_subtree(ptvc, it, ett_world);
846 
847                                 ptvcursor_add(ptvc, hf_tibia_worldlist_entry_name, 2, convo->has.string_enc | ENC_LITTLE_ENDIAN);
848                                 guint ipv4addr_len = tvb_get_letohs(tvb, ptvcursor_current_offset(ptvc));
849                                 char *ipv4addr_str = (char*)tvb_get_string_enc(pinfo->pool, tvb, ptvcursor_current_offset(ptvc) + 2, ipv4addr_len, ENC_LITTLE_ENDIAN | convo->has.string_enc);
850                                 guint32 ipv4addr = ipv4tonl(ipv4addr_str);
851                                 ptvcursor_add(ptvc, hf_tibia_worldlist_entry_ip, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
852                                 guint16 port = tvb_get_letohs(tvb, ptvcursor_current_offset(ptvc));
853                                 ptvcursor_add(ptvc, hf_tibia_worldlist_entry_port, 2, ENC_LITTLE_ENDIAN);
854                                 ptvcursor_add(ptvc, hf_tibia_worldlist_entry_preview, 1, ENC_NA);
855 
856                                 ptvcursor_pop_subtree(ptvc);
857 
858                                 register_gameserv_addr(convo, ipv4addr, port);
859                             }
860                             ptvcursor_pop_subtree(ptvc);
861                         }
862 
863                         guint8 char_count = tvb_get_guint8(tvb, ptvcursor_current_offset(ptvc));
864                         ptvcursor_add(ptvc, hf_tibia_charlist_length, 1, ENC_NA);
865                         if (char_count) {
866                             ptvcursor_add_with_subtree(ptvc, hf_tibia_charlist, SUBTREE_UNDEFINED_LENGTH, ENC_NA, ett_charlist);
867                             while (char_count--) {
868                                 proto_item *it = ptvcursor_add(ptvc, hf_tibia_worldlist_entry_id, 1, ENC_NA);
869                                 ptvcursor_push_subtree(ptvc, it, ett_char);
870                                 ptvcursor_add(ptvc, hf_tibia_charlist_entry_name, 2, convo->has.string_enc | ENC_LITTLE_ENDIAN);
871 
872 
873                                 ptvcursor_pop_subtree(ptvc);
874                             }
875                             ptvcursor_pop_subtree(ptvc);
876                         }
877                     } else {
878                         guint8 char_count = tvb_get_guint8(tvb, ptvcursor_current_offset(ptvc));
879                         ptvcursor_add(ptvc, hf_tibia_charlist_length, 1, ENC_NA);
880                         if (char_count) {
881                             ptvcursor_add_with_subtree(ptvc, hf_tibia_charlist, SUBTREE_UNDEFINED_LENGTH, ENC_NA, ett_charlist);
882 
883                             while (char_count--) {
884                                 proto_item *it = ptvcursor_add(ptvc, hf_tibia_charlist_entry_name, 2, convo->has.string_enc | ENC_LITTLE_ENDIAN);
885                                 ptvcursor_push_subtree(ptvc, it, ett_char);
886 
887                                 ptvcursor_add(ptvc, hf_tibia_charlist_entry_world, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
888 
889                                 guint32 ipv4addr = tvb_get_ipv4(tvb, ptvcursor_current_offset(ptvc));
890                                 ptvcursor_add(ptvc, hf_tibia_charlist_entry_ip, 4, ENC_BIG_ENDIAN);
891 
892                                 guint16 port = tvb_get_letohs(tvb, ptvcursor_current_offset(ptvc));
893                                 ptvcursor_add(ptvc, hf_tibia_charlist_entry_port, 2, ENC_BIG_ENDIAN);
894 
895 
896                                 ptvcursor_pop_subtree(ptvc);
897 
898                                 register_gameserv_addr(convo, ipv4addr, port);
899                             }
900 
901                             ptvcursor_pop_subtree(ptvc);
902                         }
903 
904                         ptvcursor_add(ptvc, hf_tibia_pacc_days, 2, ENC_LITTLE_ENDIAN);
905                     }
906                     break;
907                 default:
908                     offset = ptvcursor_current_offset(ptvc);
909                     call_data_dissector(tvb_new_subset_length(tvb, offset, len - offset), pinfo, ptvcursor_tree(ptvc));
910                     ptvcursor_advance(ptvc, len - offset);
911             }
912 
913             ptvcursor_pop_subtree(ptvc);
914 
915             col_append_fstr(pinfo->cinfo, COL_INFO, " %s (0x%x)",
916                     val_to_str(cmd, from_loginserv_packet_types, "Unknown"), cmd);
917 
918             if (ptvcursor_current_offset(ptvc) >= len)
919                 break;
920 
921             col_append_str(pinfo->cinfo, COL_INFO, ",");
922         }
923     }
924 
925     offset = ptvcursor_current_offset(ptvc);
926     ptvcursor_free(ptvc);
927 
928     return offset;
929 }
930 
931 static void
dissect_coord(ptvcursor_t * ptvc,gboolean with_stackpos)932 dissect_coord(ptvcursor_t *ptvc, gboolean with_stackpos)
933 {
934     tvbuff_t *tvb;
935     proto_tree *tree;
936     int offset;
937 
938     guint32 x, y, z, stackpos;
939     proto_item *coords_tuple = ptvcursor_add_with_subtree(ptvc, hf_tibia_coords, SUBTREE_UNDEFINED_LENGTH, ENC_NA, ett_coords);
940     {
941         tvb = ptvcursor_tvbuff(ptvc);
942         tree = ptvcursor_tree(ptvc);
943         offset = ptvcursor_current_offset(ptvc);
944 
945         proto_tree_add_item_ret_uint(tree, hf_tibia_coords_x, tvb, offset, 2, ENC_LITTLE_ENDIAN, &x);
946         offset += 2;
947         proto_tree_add_item_ret_uint(tree, hf_tibia_coords_y, tvb, offset, 2, ENC_LITTLE_ENDIAN, &y);
948         offset += 2;
949         proto_tree_add_item_ret_uint(tree, hf_tibia_coords_z, tvb, offset, 1, ENC_NA, &z);
950         offset += 1;
951 
952         ptvcursor_advance(ptvc, 5);
953     }
954     if (with_stackpos) {
955         proto_tree_add_item_ret_uint(tree, hf_tibia_stackpos, tvb, offset, 1, ENC_NA, &stackpos);
956         proto_item_set_text(coords_tuple, "Coordinates: (%u, %u, %u)[%u]", x, y, z, stackpos);
957         ptvcursor_advance(ptvc, 1);
958     } else {
959         proto_item_set_text(coords_tuple, "Coordinates: (%u, %u, %u)", x, y, z);
960     }
961 
962     ptvcursor_pop_subtree(ptvc);
963 }
964 
965 
966 static int
dissect_gameserv_packet(struct tibia_convo * convo,tvbuff_t * tvb,int offset,int len,packet_info * pinfo,proto_tree * tree,gboolean first_fragment)967 dissect_gameserv_packet(struct tibia_convo *convo, tvbuff_t *tvb, int offset, int len, packet_info *pinfo, proto_tree *tree, gboolean first_fragment)
968 {
969     ptvcursor_t *ptvc = ptvcursor_new(pinfo->pool, tree, tvb, offset);
970 
971     col_append_str(pinfo->cinfo, COL_INFO, first_fragment ? " commands:" : ",");
972     len += offset;
973 
974     if (ptvcursor_current_offset(ptvc) < len) {
975         for (;;) {
976             int cmd = tvb_get_guint8(tvb, ptvcursor_current_offset(ptvc));
977             ptvcursor_add_with_subtree(ptvc, hf_tibia_gameserv_command, 1, convo->has.string_enc, ett_command);
978             ptvcursor_advance(ptvc, 1);
979 
980             switch ((enum gameserv_cmd)cmd) {
981                 case S_DLG_INFO:
982                 case S_DLG_ERROR:
983                 case S_DLG_TOOMANYPLAYERS:
984                     ptvcursor_add(ptvc, cmd == S_DLG_ERROR ? hf_tibia_dlg_error : hf_tibia_dlg_info, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
985                     break;
986                 case S_GMACTIONS: /* 0x0B, Used to be 32 unknown bytes, but with GMs removed it                                     might not be in use anymore */
987                     ptvcursor_add(ptvc, hf_tibia_unknown, 32, ENC_NA);
988                     break;
989                 case S_PLAYERLOC: /* 0x64,Coord pos */
990                     dissect_coord(ptvc, FALSE);
991                     break;
992                 case S_TILEUPDATE: /* 0x69,Coord pos TileDescription td */
993                     dissect_coord(ptvc, FALSE);
994                     ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
995                     break;
996                 case S_ADDITEM: /* 0x6a,Coord pos ThingDescription thing */
997                     dissect_coord(ptvc, FALSE);
998                     ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
999                     break;
1000                 case S_REPLACEITEM: /* 0x6b,Coord pos Byte stackpos ThingDescription thing */
1001                     dissect_coord(ptvc, TRUE);
1002                     ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
1003                     break;
1004                 case S_REMOVEITEM: /* 0x6c,Coord pos Byte stackpos */
1005                     dissect_coord(ptvc, TRUE);
1006                     break;
1007                 case S_MOVE_THING: /* 0x6d, */
1008                     dissect_coord(ptvc, TRUE);
1009                     dissect_coord(ptvc, FALSE);
1010                     break;
1011                 case S_CONTAINER: /* 0x6e,Byte index Short containerIcon Byte slotCount ThingDescription item */
1012                     ptvcursor_add(ptvc, hf_tibia_container, 1, ENC_NA);
1013                     ptvcursor_add(ptvc, hf_tibia_container_icon, 2, ENC_LITTLE_ENDIAN);
1014                     ptvcursor_add(ptvc, hf_tibia_container_slots, 2, ENC_LITTLE_ENDIAN);
1015                     ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
1016                     break;
1017                 case S_CONTAINERCLOSE: /* 0x6f,Byte index */
1018                     ptvcursor_add(ptvc, hf_tibia_container, 1, ENC_NA);
1019                     break;
1020                 case S_ADDITEMCONTAINER: /* 0x70,Byte index ThingDescription itm */
1021                     ptvcursor_add(ptvc, hf_tibia_container, 1, ENC_NA);
1022                     ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
1023                     break;
1024                 case S_TRANSFORMITEMCONTAINER:/* 0x71,Byte index Byte slot */
1025                     ptvcursor_add(ptvc, hf_tibia_container, 1, ENC_NA);
1026                     ptvcursor_add(ptvc, hf_tibia_container_slot, 1, ENC_NA);
1027                     break;
1028                 case S_REMOVEITEMCONTAINER: /* 0x72,Byte index Byte slot */
1029                     ptvcursor_add(ptvc, hf_tibia_container, 1, ENC_NA);
1030                     ptvcursor_add(ptvc, hf_tibia_container_slot, 1, ENC_NA);
1031                     break;
1032                 case S_INVENTORYEMPTY: /* 0x78,Byte invSlot */
1033                     ptvcursor_add(ptvc, hf_tibia_inventory, 1, ENC_NA);
1034                     break;
1035                 case S_INVENTORYITEM: /* 0x79,Byte invSlot ThingDescription itm */
1036                     ptvcursor_add(ptvc, hf_tibia_inventory, 1, ENC_NA);
1037                     ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
1038                     break;
1039                 case S_TRADEREQ: /* 0x7d,String otherperson Byte slotCount ThingDescription itm */
1040                     ptvcursor_add(ptvc, hf_tibia_player, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
1041                     ptvcursor_add(ptvc, hf_tibia_inventory, 1, ENC_NA);
1042                     ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
1043                     break;
1044                 case S_TRADEACK: /* 0x7e,String otherperson Byte slotCount ThingDescription itm */
1045                     ptvcursor_add(ptvc, hf_tibia_player, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
1046                     ptvcursor_add(ptvc, hf_tibia_inventory, 1, ENC_NA);
1047                     ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
1048                     break;
1049 
1050                 case S_TRADECLOSE: /* 0x7f, */
1051                     break;
1052                 case S_LIGHTLEVEL: /* 0x82,Byte lightlevel Byte lightcolor */
1053                     ptvcursor_add(ptvc, hf_tibia_light_level, 1, ENC_NA);
1054                     ptvcursor_add(ptvc, hf_tibia_light_color, 1, ENC_NA);
1055                     break;
1056                 case S_MAGIC_EFFECT: /* 0x83, */
1057                     dissect_coord(ptvc, FALSE);
1058                     ptvcursor_add(ptvc, hf_tibia_magic_effect_id, 1, ENC_NA);
1059                     break;
1060                 case S_ANIMATEDTEXT: /* 0x84,Coord pos Byte color String message */
1061                     dissect_coord(ptvc, FALSE);
1062                     ptvcursor_add(ptvc, hf_tibia_animated_text_color, 1, ENC_NA);
1063                     ptvcursor_add(ptvc, hf_tibia_animated_text, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
1064                     break;
1065                 case S_DISTANCESHOT: /* 0x85,Coord pos1 Byte stackposition Coord pos2 */
1066                     dissect_coord(ptvc, FALSE);
1067                     ptvcursor_add(ptvc, hf_tibia_projectile, 4, ENC_LITTLE_ENDIAN);
1068                     dissect_coord(ptvc, FALSE);
1069                     break;
1070                 case S_CREATURESQUARE: /* 0x86,Long creatureid Byte squarecolor */
1071                     ptvcursor_add(ptvc, hf_tibia_creature, 4, ENC_LITTLE_ENDIAN);
1072                     ptvcursor_add(ptvc, hf_tibia_squarecolor, 1, ENC_NA);
1073                     break;
1074                 case S_CREATURE_HEALTH: /* 0x8C, */
1075                     ptvcursor_add(ptvc, hf_tibia_creature, 1, ENC_LITTLE_ENDIAN);
1076                     ptvcursor_add(ptvc, hf_tibia_creature_health, 1, ENC_NA);
1077                     break;
1078                 case S_CREATURELIGHT: /* 0x8d,Long creatureid Byte ? Byte ? */
1079                     ptvcursor_add(ptvc, hf_tibia_creature, 1, ENC_LITTLE_ENDIAN);
1080                     ptvcursor_add(ptvc, hf_tibia_unknown, 2, ENC_NA);
1081                     break;
1082                 case S_SETOUTFIT: /* 0x8e,Long creatureid Byte lookType Byte headType Byte bodyType Byte legsType Byte feetType // can extended look go here too? */
1083                     ptvcursor_add(ptvc, hf_tibia_creature, 1, ENC_LITTLE_ENDIAN);
1084                     ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
1085                     break;
1086                 case S_TEXTWINDOW: /* 0x96,Long windowId Byte icon Byte maxlength String message */
1087                     ptvcursor_add(ptvc, hf_tibia_window, 4, ENC_LITTLE_ENDIAN);
1088                     ptvcursor_add(ptvc, hf_tibia_window_icon, 1, ENC_NA);
1089                     ptvcursor_add(ptvc, hf_tibia_window_textlen, 1, ENC_NA);
1090                     ptvcursor_add(ptvc, hf_tibia_window_text, 1, ENC_LITTLE_ENDIAN | convo->has.string_enc);
1091                     break;
1092                 case S_PLAYER_CONDITION: /* 0xA2, */
1093                     proto_tree_add_bitmask(ptvcursor_tree(ptvc), ptvcursor_tvbuff(ptvc), ptvcursor_current_offset(ptvc), hf_tibia_char_cond, ett_char_cond, char_conds, ENC_LITTLE_ENDIAN);
1094                     ptvcursor_advance(ptvc, 4);
1095                     break;
1096                 case S_CANCELATTACK: /* 0xA3, */
1097                     break;
1098                 case S_CHANNEL_OPEN:
1099                     ptvcursor_add(ptvc, hf_tibia_channel_id, 2, ENC_LITTLE_ENDIAN);
1100                     ptvcursor_add(ptvc, hf_tibia_channel_name, 2, ENC_LITTLE_ENDIAN|convo->has.string_enc);
1101                     ptvcursor_add(ptvc, hf_tibia_unknown, 4, ENC_NA);
1102                     break;
1103                 case S_OPENPRIV: /* 0xAD,String playerName */
1104                     ptvcursor_add(ptvc, hf_tibia_player, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
1105                     break;
1106                 case S_TEXTMESSAGE: /* 0xB4,Byte msgClass String string */
1107                     ptvcursor_add(ptvc, hf_tibia_textmsg_class, 1, ENC_NA);
1108                     ptvcursor_add(ptvc, hf_tibia_textmsg, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
1109                     break;
1110                 case S_CANCELWALK: /* 0xB5,Byte direction */
1111                     ptvcursor_add(ptvc, hf_tibia_walk_dir, 1, ENC_NA);
1112                     break;
1113                 case S_VIPADD: /* 0xd2,long guid string name byte isonline */
1114                     ptvcursor_add(ptvc, hf_tibia_vip, 4, ENC_LITTLE_ENDIAN);
1115                     ptvcursor_add(ptvc, hf_tibia_player, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
1116                     ptvcursor_add(ptvc, hf_tibia_vip_online, 1, ENC_NA);
1117                     break;
1118                 case S_VIPLOGIN: /* 0xd3,long guid */
1119                     ptvcursor_add(ptvc, hf_tibia_vip, 4, ENC_LITTLE_ENDIAN);
1120                     break;
1121                 case S_VIPLOGOUT: /* 0xd4long guid*/
1122                     ptvcursor_add(ptvc, hf_tibia_vip, 4, ENC_LITTLE_ENDIAN);
1123                     break;
1124                 case S_PING:
1125                     break;
1126                 case S_NONCE: /* 0x1F, */
1127                     ptvcursor_add(ptvc, hf_tibia_nonce, 5, ENC_NA);
1128                     break;
1129 
1130                 case S_MAPINIT: /* 0x0A, Long playerCreatureId Int unknownU16 (Byte reportBugs?) */
1131                 case S_OUTFITLIST: /* 0xC8,Byte lookType Byte headType Byte bodyType Byte legsType Byte feetType Byte firstModel Byte lastModel */
1132                     /* TODO This changed with mounts and outfit */
1133                 case S_FLOORUP: /* 0xBE,Advanced topic; read separate text */
1134                 case S_FLOORDOWN: /* 0xBF,Advanced topic; read separate text */
1135                 case S_SPEAK: /* 0xAA, */
1136                 case S_CHANNELSDIALOG: /* 0xAB,Byte channelCount (Int channelId String channelName) */
1137                 case S_STATUSMSG: /* 0xA0,Status status */
1138                 case S_SKILLS: /* 0xA1,Skills skills */
1139                 case S_CREATURESPEED: /* 0x8f,YIKES! I didnt handle this! */
1140                 case S_GO_NORTH: /* 0x65,MapDescription (18,1) */
1141                 case S_GO_EAST: /* 0x66,MapDescription (1,14) */
1142                 case S_GO_SOUTH: /* 0x67,MapDescription (18,1) */
1143                 case S_GO_WEST: /* 0x68,MapDescription (1,14) */
1144                 default:
1145                     offset = ptvcursor_current_offset(ptvc);
1146                     call_data_dissector(tvb_new_subset_length(tvb, offset, len - offset), pinfo, ptvcursor_tree(ptvc));
1147                     ptvcursor_advance(ptvc, len - offset);
1148             }
1149 
1150 
1151             ptvcursor_pop_subtree(ptvc);
1152 
1153             col_append_fstr(pinfo->cinfo, COL_INFO, " %s (0x%x)",
1154                     val_to_str(cmd, from_gameserv_packet_types, "Unknown"), cmd);
1155 
1156             if (ptvcursor_current_offset(ptvc) >= len)
1157                 break;
1158 
1159             col_append_str(pinfo->cinfo, COL_INFO, ",");
1160         }
1161     }
1162 
1163     offset = ptvcursor_current_offset(ptvc);
1164     ptvcursor_free(ptvc);
1165 
1166     return offset;
1167 }
1168 
1169 static int
dissect_client_packet(struct tibia_convo * convo,tvbuff_t * tvb,int offset,int len,packet_info * pinfo,proto_tree * tree,gboolean first_fragment)1170 dissect_client_packet(struct tibia_convo *convo, tvbuff_t *tvb, int offset, int len, packet_info *pinfo, proto_tree *tree, gboolean first_fragment)
1171 {
1172     ptvcursor_t *ptvc = ptvcursor_new(pinfo->pool, tree, tvb, offset);
1173 
1174     col_append_str(pinfo->cinfo, COL_INFO, first_fragment ? " commands:" : ",");
1175     len += offset;
1176 
1177     if (ptvcursor_current_offset(ptvc) < len) {
1178         for (;;) {
1179             int cmd = tvb_get_guint8(tvb, ptvcursor_current_offset(ptvc));
1180             ptvcursor_add_with_subtree(ptvc, hf_tibia_client_command, 1, convo->has.string_enc, ett_command);
1181             ptvcursor_advance(ptvc, 1);
1182 
1183             switch ((enum client_cmd)cmd) {
1184                 case C_PLAYER_SPEECH: {
1185                     guint8 type = tvb_get_guint8(ptvcursor_tvbuff(ptvc), ptvcursor_current_offset(ptvc));
1186 
1187                     ptvcursor_add(ptvc, hf_tibia_speech_type, 1, ENC_NA);
1188                     if (type == 0x7)
1189                         ptvcursor_add(ptvc, hf_tibia_channel_id, 2, ENC_LITTLE_ENDIAN);
1190                     ptvcursor_add(ptvc, hf_tibia_chat_msg, 2, ENC_LITTLE_ENDIAN|convo->has.string_enc);
1191                     }
1192                     break;
1193                 case C_PONG:
1194                     break;
1195                 default:
1196                     offset = ptvcursor_current_offset(ptvc);
1197                     call_data_dissector(tvb_new_subset_length(tvb, offset, len - offset), pinfo, ptvcursor_tree(ptvc));
1198                     ptvcursor_advance(ptvc, len - offset);
1199             }
1200 
1201             ptvcursor_pop_subtree(ptvc);
1202 
1203             col_append_fstr(pinfo->cinfo, COL_INFO, " %s (0x%x)",
1204                     val_to_str(cmd, from_client_packet_types, "Unknown"), cmd);
1205 
1206             if (ptvcursor_current_offset(ptvc) >= len)
1207                 break;
1208 
1209             col_append_str(pinfo->cinfo, COL_INFO, ",");
1210         }
1211     }
1212 
1213     offset = ptvcursor_current_offset(ptvc);
1214     ptvcursor_free(ptvc);
1215 
1216     return offset;
1217 }
1218 
1219 static int
dissect_game_packet(struct tibia_convo * convo,tvbuff_t * tvb,int offset,packet_info * pinfo,proto_tree * tree,gboolean is_xtea_encrypted,gboolean first_fragment)1220 dissect_game_packet(struct tibia_convo *convo, tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, gboolean is_xtea_encrypted, gboolean first_fragment)
1221 {
1222     proto_item *ti = NULL;
1223     int len = tvb_captured_length_remaining(tvb, offset);
1224 
1225     if (show_acc_info) {
1226         if (convo->has.session_key) {
1227             if (convo->session_key) {
1228                 ti = proto_tree_add_string(tree, hf_tibia_session_key_convo, tvb, offset, 0, (const char*)convo->session_key);
1229                 proto_item_set_generated(ti);
1230             }
1231         } else {
1232             if (convo->acc) {
1233                 ti = proto_tree_add_string(tree, hf_tibia_acc_name_convo, tvb, offset, 0, (const char*)convo->acc);
1234                 proto_item_set_generated(ti);
1235             }
1236 
1237             if (convo->pass) {
1238                 ti = proto_tree_add_string(tree, hf_tibia_acc_pass_convo, tvb, offset, 0, (const char*)convo->pass);
1239                 proto_item_set_generated(ti);
1240             }
1241         }
1242     }
1243 
1244     if (show_char_name && convo->char_name) {
1245         ti = proto_tree_add_string(tree, hf_tibia_char_name_convo, tvb, offset, 0, (const char*)convo->char_name);
1246         proto_item_set_generated(ti);
1247     }
1248 
1249     if (is_xtea_encrypted) {
1250         if (pinfo->num > convo->xtea_framenum) {
1251             if (show_xtea_key && convo->has.xtea) {
1252                 ti = proto_tree_add_bytes_with_length(tree, hf_tibia_xtea_key, tvb, 0, 0, (guint8*)convo->xtea_key, XTEA_KEY_LEN);
1253                 proto_item_set_generated(ti);
1254             }
1255 
1256             int end = offset + len;
1257 
1258             if (len % 8 != 0)
1259                 return -1;
1260 
1261             guint8 *decrypted_buffer = (guint8*)wmem_alloc(pinfo->pool, len);
1262 
1263             for (guint8 *dstblock = decrypted_buffer; offset < end; offset += 8) {
1264                 decrypt_xtea_le_ecb(dstblock, tvb_get_ptr(tvb, offset, 8), convo->xtea_key, 32);
1265                 dstblock += 8;
1266             }
1267 
1268             tvb = tvb_new_child_real_data(tvb, decrypted_buffer, len, len);
1269             add_new_data_source(pinfo, tvb, "Decrypted Game Data");
1270 
1271             offset = 0;
1272         } else {
1273             proto_tree_add_item(tree, hf_tibia_undecoded_xtea_data, tvb, offset, len, ENC_NA);
1274             return offset;
1275         }
1276     }
1277     if (convo->has.xtea) {
1278         len = tvb_get_letohs(tvb, offset);
1279         ti = proto_tree_add_item(tree, hf_tibia_payload_len, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1280         offset += 2;
1281         if (len > tvb_captured_length_remaining(tvb, offset)) {
1282             expert_add_info(pinfo, ti, &ei_xtea_len_toobig);
1283             return offset;
1284         }
1285     }
1286 
1287 
1288     if (pinfo->srcport == convo->servport && convo->loginserv_is_peer)
1289         return dissect_loginserv_packet(convo, tvb, offset, len, pinfo, tree, first_fragment);
1290 
1291     if (!dissect_game_commands) {
1292         call_data_dissector(tvb_new_subset_length(tvb, offset, len), pinfo, tree);
1293         return offset + len;
1294     }
1295 
1296     if (pinfo->srcport == convo->servport)
1297         return dissect_gameserv_packet(convo, tvb, offset, len, pinfo, tree, first_fragment);
1298     else
1299         return dissect_client_packet(convo, tvb, offset, len, pinfo, tree, first_fragment);
1300 }
1301 
1302 static int
dissect_tibia(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * fragment_num)1303 dissect_tibia(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *fragment_num)
1304 {
1305     tvbuff_t *tvb_decrypted = tvb;
1306     gboolean is_xtea_encrypted = FALSE;
1307     enum { TIBIA_GAMESERV, TIBIA_LOGINSERV } serv = TIBIA_GAMESERV;
1308     guint16 plen = tvb_get_letohs(tvb, 0) + 2;
1309 
1310     /* if announced length != real length it's not a tibia packet */
1311     if (tvb_reported_length_remaining(tvb, 0) != plen)
1312         return 0;
1313 
1314     struct tibia_convo *convo = tibia_get_convo(pinfo);
1315 
1316     int offset = 2;
1317     int a32len = tvb_reported_length_remaining(tvb, offset + 4);
1318     guint32 packet_cksum = tvb_get_letohl(tvb, offset);
1319     guint32 computed_cksum = GUINT32_TO_LE(adler32_bytes(tvb_get_ptr(tvb, offset + 4, a32len), a32len));
1320     convo->has.adler32 = packet_cksum == computed_cksum;
1321     if (convo->has.adler32)
1322         offset += 4;
1323 
1324     /* FIXME Tibia >=11.11 has a sequence number instead, this is yet unhandled */
1325 
1326     /* Is it a nonce? */
1327     if (tvb_get_letohs(tvb, offset) == plen - offset - 2
1328             && tvb_get_guint8(tvb, offset+2) == S_NONCE) {
1329         /* Don't do anything. We'll handle it as unencrypted game command later */
1330     } else {
1331         guint8 cmd;
1332         guint16 version;
1333         struct proto_traits version_has;
1334         cmd = tvb_get_guint8(tvb, offset);
1335         offset += 1;
1336         offset += 2; /* OS */
1337         version = tvb_get_letohs(tvb, offset);
1338         version_has = get_version_traits(version);
1339 
1340         switch(cmd) {
1341             case C_GET_CHARLIST:
1342                 if ((700 <= version && version <= 760 && !convo->has.adler32 && 25 <= plen && plen <= 54)
1343                         || get_version_get_charlist_packet_size(&version_has) == plen) {
1344                     serv = TIBIA_LOGINSERV;
1345                     convo->loginserv_is_peer = TRUE;
1346                 }
1347                 break;
1348             case C_LOGIN_CHAR:
1349                 /* The outcast client I tried, zero-pads the 760 login request.
1350                  * I don't think the Cipsoft client ever did this.
1351                  */
1352                 if ((700 <= version && version <= 760 && !convo->has.adler32 && 25 <= plen && plen <= 54)
1353                         ||  get_version_char_login_packet_size(&version_has) == plen)
1354                     serv = TIBIA_LOGINSERV;
1355                 break;
1356             default:
1357                 is_xtea_encrypted = convo->has.xtea;
1358         }
1359     }
1360 
1361 
1362     offset = 0; /* With the version extracted, let's build the tree */
1363 
1364     col_set_str(pinfo->cinfo, COL_PROTOCOL, "Tibia");
1365     if (GPOINTER_TO_UINT(fragment_num) == 1) {
1366         /* We don't want to repeat ourselves in the info column if there are fragments */
1367         if (serv == TIBIA_LOGINSERV)
1368             col_set_str(pinfo->cinfo, COL_INFO, "Login");
1369         else if (pinfo->srcport == convo->servport)
1370             col_set_str(pinfo->cinfo, COL_INFO, "Server");
1371         else
1372             col_set_str(pinfo->cinfo, COL_INFO, "Client");
1373 
1374     }
1375 
1376     proto_item *ti = proto_tree_add_item(tree, proto_tibia, tvb, 0, -1, ENC_NA);
1377     proto_tree *tibia_tree = proto_item_add_subtree(ti, ett_tibia);
1378 
1379     proto_tree_add_item(tibia_tree, hf_tibia_len, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1380     offset += 2;
1381     if (convo->has.adler32) {
1382         proto_tree_add_checksum(tibia_tree, tvb, offset, hf_tibia_adler32, hf_tibia_adler32_status, &ei_adler32_checksum_bad, pinfo, computed_cksum, ENC_LITTLE_ENDIAN, PROTO_CHECKSUM_VERIFY);
1383         offset += 4;
1384     } else if (convo->has.compression) {
1385         offset += 4;
1386     }
1387 
1388     if (serv == TIBIA_GAMESERV)
1389         return dissect_game_packet(convo, tvb, offset, pinfo, tibia_tree, is_xtea_encrypted, GPOINTER_TO_UINT(fragment_num) == 1);
1390 
1391     proto_tree_add_item(tibia_tree, hf_tibia_client_command, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1392     offset += 1;
1393     proto_tree_add_item(tibia_tree, hf_tibia_os, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1394     offset += 2;
1395 
1396     convo->proto_version = tvb_get_letohs(tvb, offset);
1397     convo->has = get_version_traits(convo->proto_version);
1398     proto_tree_add_item(tibia_tree, hf_tibia_proto_version, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1399     offset += 2;
1400     if (convo->has.client_version) {
1401         proto_tree_add_item(tibia_tree, hf_tibia_client_version, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1402         offset += 4;
1403     }
1404     if (convo->loginserv_is_peer) {
1405         proto_tree *vertree;
1406         /* The first 4 bytes of the client's tibia.pic, tibia.dat and tibia.spr files */
1407         proto_item *subti = proto_tree_add_item(tibia_tree, hf_tibia_file_versions, tvb, offset, 12, ENC_NA);
1408         vertree = proto_item_add_subtree(subti, ett_file_versions);
1409         proto_tree_add_item(vertree, hf_tibia_file_version_spr, tvb, offset, 4, ENC_BIG_ENDIAN);
1410         offset += 4;
1411         proto_tree_add_item(vertree, hf_tibia_file_version_dat, tvb, offset, 4, ENC_BIG_ENDIAN);
1412         offset += 4;
1413         proto_tree_add_item(vertree, hf_tibia_file_version_pic, tvb, offset, 4, ENC_BIG_ENDIAN);
1414         offset += 4;
1415     } else if (convo->has.game_content_revision) {
1416         proto_tree_add_item(tibia_tree, hf_tibia_content_revision, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1417         offset += 2;
1418     }
1419 
1420     if (convo->has.game_preview) {
1421         proto_tree_add_item(tibia_tree, hf_tibia_game_preview_state, tvb, offset, 1, ENC_NA);
1422         offset += 1;
1423     }
1424 
1425     int rsa1_end = 0; /* End of first RSA block */
1426     if (convo->has.rsa) {
1427         gcry_sexp_t privkey;
1428         if (!(privkey = convo_get_privkey(convo))) {
1429             proto_tree_add_item(tibia_tree, hf_tibia_undecoded_rsa_data, tvb, offset, plen - offset, ENC_NA);
1430             return offset;
1431         }
1432 
1433         guint ciphertext_len = tvb_captured_length_remaining(tvb, offset);
1434         if (ciphertext_len < 128) {
1435             expert_add_info(pinfo, ti, &ei_rsa_ciphertext_too_short);
1436             return offset;
1437         }
1438         rsa1_end = offset + 128;
1439         guint8 *payload = (guint8*)tvb_memdup(pinfo->pool, tvb, offset, 128);
1440 
1441         char *err = NULL;
1442         size_t payload_len;
1443         if (!(payload_len = rsa_decrypt_inplace(128, payload, privkey, FALSE, &err))) {
1444             expert_add_info_format(pinfo, ti, &ei_rsa_decrypt_failed, "Decrypting RSA block failed: %s", err);
1445             g_free(err);
1446             return offset;
1447         }
1448         size_t leading_zeroes = 128 - payload_len;
1449         memmove(payload + leading_zeroes, payload, payload_len);
1450         memset(payload, 0x00, leading_zeroes);
1451 
1452         tvb_decrypted = tvb_new_child_real_data(tvb, payload, 128, 128);
1453         add_new_data_source(pinfo, tvb_decrypted, "Decrypted Login Data");
1454 
1455         if (tvb_get_guint8(tvb_decrypted, 0) != 0x00) {
1456             expert_add_info(pinfo, ti, &ei_rsa_plaintext_no_leading_zero);
1457             return offset;
1458         }
1459 
1460         offset = 1;
1461 
1462         tvb_memcpy(tvb_decrypted, convo->xtea_key, 1, XTEA_KEY_LEN);
1463         proto_tree_add_item(tibia_tree, hf_tibia_xtea_key, tvb_decrypted, 1, XTEA_KEY_LEN, ENC_NA);
1464         offset += XTEA_KEY_LEN;
1465         convo->xtea_framenum = pinfo->num;
1466     }
1467 
1468     if (!convo->loginserv_is_peer && convo->has.gmbyte) {
1469         proto_tree_add_item(tibia_tree, hf_tibia_loginflags_gm, tvb_decrypted, offset, 1, ENC_NA);
1470         offset += 1;
1471     }
1472 
1473     int len;
1474     if (convo->has.session_key && !convo->loginserv_is_peer) {
1475         /* OTServs I tested against use "$acc\n$pacc" as session key */
1476         if (convo->session_key) {
1477             proto_tree_add_item_ret_length(tibia_tree, hf_tibia_session_key, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, &len);
1478         } else {
1479             proto_tree_add_item_ret_string_and_length(tibia_tree, hf_tibia_session_key, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, wmem_file_scope(), &convo->session_key, &len);
1480         }
1481         offset += len;
1482     } else if (convo->has.acc_name) {
1483         if (convo->acc) {
1484             proto_tree_add_item_ret_length(tibia_tree, hf_tibia_acc_name, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, &len);
1485         } else {
1486             proto_tree_add_item_ret_string_and_length(tibia_tree, hf_tibia_acc_name, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, wmem_file_scope(), &convo->acc, &len);
1487         }
1488         offset += len;
1489     } else /* account number */ {
1490         char *accnum = wmem_strdup_printf(pinfo->pool, "%" G_GUINT32_FORMAT, tvb_get_letohl(tvb_decrypted, offset));
1491         proto_tree_add_string(tibia_tree, hf_tibia_acc_number, tvb_decrypted, offset, 4, accnum);
1492         if (!convo->acc)
1493             convo->acc = (guint8*)wmem_strdup(wmem_file_scope(), accnum);
1494         offset += 4;
1495     }
1496 
1497     if (!convo->loginserv_is_peer) {
1498         if (convo->char_name) {
1499             proto_tree_add_item_ret_length(tibia_tree, hf_tibia_char_name, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, &len);
1500         } else {
1501             proto_tree_add_item_ret_string_and_length(tibia_tree, hf_tibia_char_name, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, wmem_file_scope(), &convo->char_name, &len);
1502         }
1503         offset += len;
1504     }
1505 
1506     if (!convo->has.session_key || convo->loginserv_is_peer) {
1507         if (convo->pass) {
1508             proto_tree_add_item_ret_length(tibia_tree, hf_tibia_acc_pass, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, &len);
1509         } else {
1510             proto_tree_add_item_ret_string_and_length(tibia_tree, hf_tibia_acc_pass, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, wmem_file_scope(), &convo->pass, &len);
1511         }
1512         offset += len;
1513     }
1514 
1515     if (convo->loginserv_is_peer && convo->has.hwinfo) {
1516         proto_item *item;
1517         proto_tree *infotree, *subtree;
1518 
1519         item = proto_tree_add_item(tibia_tree, hf_tibia_client_info, tvb_decrypted, offset, 47, ENC_NA);
1520         infotree = proto_item_add_subtree(item, ett_client_info);
1521 
1522         /* Subtree { */
1523         guint locale_id;
1524         const guint8 *locale_name;
1525 
1526         item = proto_tree_add_item(infotree, hf_tibia_client_locale, tvb_decrypted, offset, 4, ENC_NA);
1527         subtree = proto_item_add_subtree(item, ett_locale);
1528 
1529         proto_tree_add_item_ret_uint(subtree, hf_tibia_client_locale_id, tvb_decrypted, offset, 1, ENC_NA, &locale_id);
1530         offset += 1;
1531 
1532         proto_tree_add_item_ret_string(subtree, hf_tibia_client_locale_name, tvb_decrypted, offset, 3, convo->has.string_enc|ENC_NA, pinfo->pool, &locale_name);
1533         offset += 3;
1534         proto_item_set_text(item, "Locale: %s (0x%X)", locale_name, locale_id);
1535         /* } */
1536 
1537         proto_tree_add_item(infotree, hf_tibia_client_ram, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN);
1538         offset += 2;
1539 
1540         proto_tree_add_item(infotree, hf_tibia_unknown, tvb_decrypted, offset, 6, ENC_NA);
1541         offset += 6;
1542 
1543         /* Subtree { */
1544         guint clock1, clock2;
1545         const guint8 *cpu;
1546 
1547         item = proto_tree_add_item(infotree, hf_tibia_client_cpu, tvb_decrypted, offset, 15, ENC_NA);
1548         subtree = proto_item_add_subtree(item, ett_cpu);
1549 
1550         proto_tree_add_item_ret_string(subtree, hf_tibia_client_cpu_name, tvb_decrypted, offset, 9, convo->has.string_enc|ENC_NA, pinfo->pool, &cpu);
1551         offset += 9;
1552 
1553         proto_tree_add_item(subtree, hf_tibia_unknown, tvb_decrypted, offset, 2, ENC_NA);
1554         offset += 2;
1555 
1556         proto_tree_add_item_ret_uint(subtree, hf_tibia_client_clock, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN, &clock1);
1557         offset += 2;
1558 
1559         proto_tree_add_item_ret_uint(subtree, hf_tibia_client_clock2, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN, &clock2);
1560         offset += 2;
1561 
1562         proto_item_set_text(item, "CPU: %s (%uMhz/%uMhz)", cpu, clock2, clock1);
1563         /* } */
1564 
1565 
1566         proto_tree_add_item(infotree, hf_tibia_unknown, tvb_decrypted, offset, 4, ENC_NA);
1567         offset += 4;
1568 
1569         proto_tree_add_item(infotree, hf_tibia_client_gpu, tvb_decrypted, offset, 9, convo->has.string_enc|ENC_NA);
1570         offset += 9;
1571 
1572         proto_tree_add_item(infotree, hf_tibia_client_vram, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN);
1573         offset += 2;
1574 
1575         /* Subtree { */
1576         guint x, y, hz;
1577 
1578         item = proto_tree_add_item(infotree, hf_tibia_client_resolution, tvb_decrypted, offset, 5, ENC_NA);
1579         subtree = proto_item_add_subtree(item, ett_resolution);
1580 
1581         proto_tree_add_item_ret_uint(subtree, hf_tibia_client_resolution_x, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN, &x);
1582         offset += 2;
1583         proto_tree_add_item_ret_uint(subtree, hf_tibia_client_resolution_y, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN, &y);
1584         offset += 2;
1585         proto_tree_add_item_ret_uint(subtree, hf_tibia_client_resolution_hz, tvb_decrypted, offset, 1, ENC_LITTLE_ENDIAN, &hz);
1586         offset += 1;
1587 
1588         proto_item_set_text(item, "Resolution: %ux%u @ %uHz", x, y, hz);
1589         /* } */
1590 
1591     } else if (!convo->loginserv_is_peer && convo->has.nonce) {
1592         proto_tree_add_item(tibia_tree, hf_tibia_nonce, tvb_decrypted, offset, 5, ENC_NA);
1593         offset += 5;
1594     }
1595 
1596     if (convo->has.rsa) {
1597         /* Undecoded hardware info maybe */
1598         call_data_dissector(tvb_new_subset_length(tvb_decrypted, offset, 128 - offset), pinfo, tibia_tree);
1599     }
1600 
1601     if (rsa1_end)
1602         offset = rsa1_end;
1603 
1604     if (offset != plen) {
1605         /* TODO Extended GPU info and authentication token (RSA-encrypted again) */
1606         call_data_dissector(tvb_new_subset_length(tvb, offset, plen - offset), pinfo, tibia_tree);
1607     }
1608     return plen;
1609 }
1610 
1611 static const value_string operating_systems[] = {
1612     { 2, "Windows" },
1613     { 0, NULL }
1614 };
1615 
1616 static const value_string speech_types[] = {
1617     { 0x1, "Say" },
1618     { 0x2, "Whisper" },
1619     { 0x3, "Yell" },
1620     { 0x7, "Public Channel" },
1621     { 0, NULL }
1622 };
1623 
1624 #if defined(HAVE_LIBGNUTLS)
1625 static guint
rsakey_hash(gconstpointer _rsakey)1626 rsakey_hash(gconstpointer _rsakey)
1627 {
1628     const struct rsakey *rsakey = (const struct rsakey *)_rsakey;
1629     return add_address_to_hash(rsakey->port, &rsakey->addr);
1630 }
1631 
1632 static gboolean
rsakey_equal(gconstpointer _a,gconstpointer _b)1633 rsakey_equal(gconstpointer _a, gconstpointer _b)
1634 {
1635     const struct rsakey *a = (const struct rsakey *)_a,
1636                         *b = (const struct rsakey *)_b;
1637     return a->port == b->port && addresses_equal(&a->addr, &b->addr);
1638 }
1639 static void
rsakey_free(void * _rsakey)1640 rsakey_free(void *_rsakey)
1641 {
1642     struct rsakey *rsakey = (struct rsakey *)_rsakey;
1643 
1644     /* gcry_sexp_release(rsakey->privkey); */ /* private key may be shared. */
1645     free_address_wmem(NULL, &rsakey->addr);
1646     g_free(rsakey);
1647 }
1648 
1649 static void
rsa_parse_uat(void)1650 rsa_parse_uat(void)
1651 {
1652     g_hash_table_remove_all(rsakeys);
1653 
1654     for (guint i = 0; i < nrsakeys; i++) {
1655         struct rsakeys_assoc *uats = &rsakeylist_uats[i];
1656 
1657         /* try to load keys file first */
1658         FILE *fp = ws_fopen(uats->keyfile, "rb");
1659         if (!fp) {
1660             report_open_failure(uats->keyfile, errno, FALSE);
1661             return;
1662         }
1663 
1664         gnutls_x509_privkey_t priv_key;
1665         char *err = NULL;
1666         if (*uats->password) {
1667             priv_key = rsa_load_pkcs12(fp, uats->password, &err);
1668             if (err) {
1669                 report_failure("%s\n", err);
1670                 g_free(err);
1671             }
1672         } else {
1673             priv_key = rsa_load_pem_key(fp, &err);
1674             if (err) {
1675                 report_failure("%s\n", err);
1676                 g_free(err);
1677             }
1678         }
1679         fclose(fp);
1680 
1681         if (!priv_key) {
1682             report_failure("Can't load private key from %s\n", uats->keyfile);
1683             return;
1684         }
1685 
1686         struct rsakey *entry;
1687         guint32 ipaddr;
1688         gcry_sexp_t private_key = rsa_privkey_to_sexp(priv_key, &err);
1689         if (!private_key) {
1690             g_free(err);
1691             report_failure("Can't extract private key parameters for %s", uats->keyfile);
1692             goto end;
1693         }
1694 
1695         entry = g_new(struct rsakey, 1);
1696         ws_strtou16(uats->port, NULL, &entry->port);
1697         ipaddr = ipv4tonl(uats->ipaddr);
1698         alloc_address_wmem(NULL, &entry->addr, AT_IPv4, sizeof ipaddr, &ipaddr);
1699         entry->privkey = private_key;
1700 
1701 
1702         g_hash_table_insert(rsakeys, entry, entry->privkey);
1703 
1704 end:
1705         gnutls_x509_privkey_deinit(priv_key);
1706     }
1707 }
1708 
1709 static void
rsakeys_free_cb(void * r)1710 rsakeys_free_cb(void *r)
1711 {
1712     struct rsakeys_assoc *h = (struct rsakeys_assoc *)r;
1713 
1714     g_free(h->ipaddr);
1715     g_free(h->port);
1716     g_free(h->keyfile);
1717     g_free(h->password);
1718 }
1719 
1720 static void*
rsakeys_copy_cb(void * dst_,const void * src_,size_t len _U_)1721 rsakeys_copy_cb(void *dst_, const void *src_, size_t len _U_)
1722 {
1723     const struct rsakeys_assoc *src = (const struct rsakeys_assoc *)src_;
1724     struct rsakeys_assoc       *dst = (struct rsakeys_assoc *)dst_;
1725 
1726     dst->ipaddr    = g_strdup(src->ipaddr);
1727     dst->port      = g_strdup(src->port);
1728     dst->keyfile   = g_strdup(src->keyfile);
1729     dst->password  = g_strdup(src->password);
1730 
1731     return dst;
1732 }
1733 
1734 static gboolean
rsakeys_uat_fld_ip_chk_cb(void * r _U_,const char * ipaddr,guint len _U_,const void * u1 _U_,const void * u2 _U_,char ** err)1735 rsakeys_uat_fld_ip_chk_cb(void* r _U_, const char* ipaddr, guint len _U_, const void* u1 _U_, const void* u2 _U_, char** err)
1736 {
1737     /* There are no Tibia IPv6 servers, although Tibia 11.0+'s Protocol in theory supports it */
1738     if (ipaddr && g_hostname_is_ip_address(ipaddr) && strchr(ipaddr, '.')) {
1739         *err = NULL;
1740         return TRUE;
1741     }
1742 
1743     *err = g_strdup_printf("No IPv4 address given.");
1744     return FALSE;
1745 }
1746 
1747 static gboolean
rsakeys_uat_fld_port_chk_cb(void * _record _U_,const char * str,guint len _U_,const void * chk_data _U_,const void * fld_data _U_,char ** err)1748 rsakeys_uat_fld_port_chk_cb(void *_record _U_, const char *str, guint len _U_, const void *chk_data _U_, const void *fld_data _U_, char **err)
1749 {
1750     guint16 val;
1751     if (!ws_strtou16(str, NULL, &val)) {
1752         *err = g_strdup("Invalid argument. Expected a decimal between [0-65535]");
1753         return FALSE;
1754     }
1755     *err = NULL;
1756     return TRUE;
1757 }
1758 
1759 static gboolean
rsakeys_uat_fld_fileopen_chk_cb(void * r _U_,const char * p,guint len _U_,const void * u1 _U_,const void * u2 _U_,char ** err)1760 rsakeys_uat_fld_fileopen_chk_cb(void* r _U_, const char* p, guint len _U_, const void* u1 _U_, const void* u2 _U_, char** err)
1761 {
1762     if (p && *p) {
1763         ws_statb64 st;
1764         if (ws_stat64(p, &st) != 0) {
1765             *err = g_strdup_printf("File '%s' does not exist or access is denied.", p);
1766             return FALSE;
1767         }
1768     } else {
1769         *err = g_strdup("No filename given.");
1770         return FALSE;
1771     }
1772 
1773     *err = NULL;
1774     return TRUE;
1775 }
1776 
1777 static gboolean
rsakeys_uat_fld_password_chk_cb(void * r,const char * p,guint len _U_,const void * u1 _U_,const void * u2 _U_,char ** err)1778 rsakeys_uat_fld_password_chk_cb(void *r, const char *p, guint len _U_, const void *u1 _U_, const void *u2 _U_, char **err)
1779 {
1780     if (p && *p) {
1781         struct rsakeys_assoc *f = (struct rsakeys_assoc *)r;
1782         FILE *fp = ws_fopen(f->keyfile, "rb");
1783         if (fp) {
1784             char *msg = NULL;
1785             gnutls_x509_privkey_t priv_key = rsa_load_pkcs12(fp, p, &msg);
1786             if (!priv_key) {
1787                 fclose(fp);
1788                 *err = g_strdup_printf("Could not load PKCS#12 key file: %s", msg);
1789                 g_free(msg);
1790                 return FALSE;
1791             }
1792             g_free(msg);
1793             gnutls_x509_privkey_deinit(priv_key);
1794             fclose(fp);
1795         } else {
1796             *err = g_strdup_printf("Leave this field blank if the keyfile is not PKCS#12.");
1797             return FALSE;
1798         }
1799     }
1800 
1801     *err = NULL;
1802     return TRUE;
1803 }
1804 #endif
1805 
1806 static void
xtea_parse_uat(void)1807 xtea_parse_uat(void)
1808 {
1809     g_hash_table_remove_all(xteakeys);
1810 
1811     for (guint i = 0; i < nxteakeys; i++) {
1812         guint key_idx = 0;
1813         guint8 *key = (guint8*)g_malloc(XTEA_KEY_LEN);
1814 
1815         for (const char *str = xteakeylist_uats[i].key; str[0] && str[1] && key_idx < XTEA_KEY_LEN; str++) {
1816             if (g_ascii_ispunct(*str))
1817                 continue;
1818 
1819             key[key_idx++] = (g_ascii_xdigit_value(str[0]) << 4)
1820                            +  g_ascii_xdigit_value(str[1]);
1821             str++;
1822         }
1823 
1824         g_hash_table_insert(xteakeys, GUINT_TO_POINTER(xteakeylist_uats[i].framenum), key);
1825     }
1826 }
1827 
1828 static void
xteakeys_free_cb(void * r)1829 xteakeys_free_cb(void *r)
1830 {
1831     struct xteakeys_assoc *h = (struct xteakeys_assoc *)r;
1832 
1833     g_free(h->key);
1834 }
1835 
1836 static void*
xteakeys_copy_cb(void * dst_,const void * src_,size_t len _U_)1837 xteakeys_copy_cb(void *dst_, const void *src_, size_t len _U_)
1838 {
1839     const struct xteakeys_assoc *src = (const struct xteakeys_assoc *)src_;
1840     struct xteakeys_assoc       *dst = (struct xteakeys_assoc *)dst_;
1841 
1842     dst->framenum = src->framenum;
1843     dst->key      = g_strdup(src->key);
1844 
1845     return dst;
1846 }
1847 
1848 static gboolean
xteakeys_uat_fld_key_chk_cb(void * r _U_,const char * key,guint len,const void * u1 _U_,const void * u2 _U_,char ** err)1849 xteakeys_uat_fld_key_chk_cb(void *r _U_, const char *key, guint len, const void *u1 _U_, const void *u2 _U_, char **err)
1850 {
1851     if (len >= XTEA_KEY_LEN*2) {
1852         gsize i = 0;
1853 
1854         do {
1855             if (g_ascii_ispunct(*key))
1856                 continue;
1857             if (!g_ascii_isxdigit(*key))
1858                 break;
1859             i++;
1860         } while (*++key);
1861 
1862         if (*key == '\0' && i == 2*XTEA_KEY_LEN) {
1863             *err = NULL;
1864             return TRUE;
1865         }
1866     }
1867 
1868     *err = g_strdup_printf("XTEA keys are 32 character long hex strings.");
1869     return FALSE;
1870 }
1871 
1872 
1873 void
proto_register_tibia(void)1874 proto_register_tibia(void)
1875 {
1876     static hf_register_info hf[] = {
1877         { &hf_tibia_len,
1878             { "Packet length", "tibia.len",
1879                 FT_UINT16, BASE_DEC,
1880                 NULL, 0x0,
1881                 NULL, HFILL }
1882         },
1883         { &hf_tibia_adler32,
1884             { "Adler32 checksum", "tibia.checksum",
1885                 FT_UINT32, BASE_HEX,
1886                 NULL, 0x0,
1887                 NULL, HFILL }
1888         },
1889         { &hf_tibia_adler32_status,
1890             { "Checksum status", "tibia.checksum.status",
1891                 FT_UINT8, BASE_NONE,
1892                 VALS(proto_checksum_vals), 0x0,
1893                 NULL, HFILL }
1894         },
1895         { &hf_tibia_nonce,
1896             { "Game server nonce", "tibia.nonce",
1897                 FT_BYTES, BASE_NONE,
1898                 NULL, 0x0,
1899                 NULL, HFILL }
1900         },
1901         { &hf_tibia_os,
1902             { "Operating system", "tibia.os",
1903                 FT_UINT16, BASE_HEX,
1904                 VALS(operating_systems), 0x0,
1905                 NULL, HFILL }
1906         },
1907         { &hf_tibia_proto_version,
1908             { "Protocol version", "tibia.version",
1909                 FT_UINT16, BASE_DEC,
1910                 NULL, 0x0,
1911                 NULL, HFILL }
1912         },
1913         { &hf_tibia_client_version,
1914             { "Client version", "tibia.client_version",
1915                 FT_UINT32, BASE_DEC,
1916                 NULL, 0x0,
1917                 NULL, HFILL }
1918         },
1919         { &hf_tibia_file_versions,
1920             { "File versions", "tibia.version.files",
1921                 FT_NONE, BASE_NONE, NULL, 0x0,
1922                 NULL, HFILL }
1923         },
1924         { &hf_tibia_file_version_spr,
1925             { "Tibia.spr version", "tibia.version.spr",
1926                 FT_UINT32, BASE_HEX,
1927                 NULL, 0x0,
1928                 NULL, HFILL }
1929         },
1930         { &hf_tibia_file_version_dat,
1931             { "Tibia.dat version", "tibia.version.dat",
1932                 FT_UINT32, BASE_HEX,
1933                 NULL, 0x0,
1934                 NULL, HFILL }
1935         },
1936         { &hf_tibia_file_version_pic,
1937             { "Tibia.pic version", "tibia.version.pic",
1938                 FT_UINT32, BASE_HEX,
1939                 NULL, 0x0,
1940                 NULL, HFILL }
1941         },
1942         { &hf_tibia_content_revision,
1943             { "Content revision", "tibia.version.content",
1944                 FT_UINT16, BASE_HEX,
1945                 NULL, 0x0,
1946                 NULL, HFILL }
1947         },
1948         { &hf_tibia_undecoded_rsa_data,
1949             { "RSA-encrypted login data", "tibia.rsa_data",
1950                 FT_BYTES, BASE_NONE,
1951                 NULL, 0x0,
1952                 NULL, HFILL }
1953         },
1954         { &hf_tibia_undecoded_xtea_data,
1955             { "XTEA-encrypted game data", "tibia.xtea_data",
1956                 FT_BYTES, BASE_NONE,
1957                 NULL, 0x0,
1958                 NULL, HFILL }
1959         },
1960         { &hf_tibia_unknown,
1961             { "Unknown Data", "tibia.unknown",
1962                 FT_BYTES, BASE_NONE,
1963                 NULL, 0x0,
1964                 NULL, HFILL }
1965         },
1966         { &hf_tibia_xtea_key,
1967             { "Symmetric key (XTEA)", "tibia.xtea",
1968                 FT_BYTES, BASE_NONE,
1969                 NULL, 0x0,
1970                 NULL, HFILL }
1971         },
1972         { &hf_tibia_loginflags_gm,
1973             { "Gamemaster", "tibia.login.flags.gm",
1974                 FT_BOOLEAN, 8,
1975                 NULL, 0x1,
1976                 NULL, HFILL }
1977         },
1978         { &hf_tibia_game_preview_state,
1979             { "Game Preview State", "tibia.login.flags.preview",
1980                 FT_BOOLEAN, 8,
1981                 NULL, 0x1,
1982                 NULL, HFILL }
1983         },
1984         { &hf_tibia_char_cond,
1985           {"Character Condition", "tibia.cond",
1986                 FT_UINT32, BASE_HEX,
1987                 NULL, 0,
1988                 NULL, HFILL}
1989         },
1990         { &hf_tibia_char_cond_poisoned,
1991             { "Poisoned", "tibia.cond.poisoned",
1992                 FT_BOOLEAN, 32,
1993                 TFS(&tfs_yes_no), COND_POISONED,
1994                 NULL, HFILL }
1995         },
1996         { &hf_tibia_char_cond_burning,
1997             { "Burning", "tibia.cond.burning",
1998                 FT_BOOLEAN, 32,
1999                 TFS(&tfs_yes_no), COND_BURNING,
2000                 NULL, HFILL }
2001         },
2002         { &hf_tibia_char_cond_electrocuted,
2003             { "Electrocuted", "tibia.cond.electrocuted",
2004                 FT_BOOLEAN, 32,
2005                 TFS(&tfs_yes_no), COND_ELECTROCUTED,
2006                 NULL, HFILL }
2007         },
2008         { &hf_tibia_char_cond_drunk,
2009             { "Drunk", "tibia.cond.drunk",
2010                 FT_BOOLEAN, 32,
2011                 TFS(&tfs_yes_no), COND_DRUNK,
2012                 NULL, HFILL }
2013         },
2014         { &hf_tibia_char_cond_manashield, /* Utamo Vita */
2015             { "Mana Shield", "tibia.cond.manashield",
2016                 FT_BOOLEAN, 32,
2017                 TFS(&tfs_yes_no), COND_MANASHIELD,
2018                 NULL, HFILL }
2019         },
2020         { &hf_tibia_char_cond_paralyzed,
2021             { "Paralyzed", "tibia.cond.paralyzed",
2022                 FT_BOOLEAN, 32,
2023                 TFS(&tfs_yes_no), COND_PARALYZED,
2024                 NULL, HFILL }
2025         },
2026         { &hf_tibia_char_cond_haste,
2027             { "Haste", "tibia.cond.haste",
2028                 FT_BOOLEAN, 32,
2029                 TFS(&tfs_yes_no), COND_HASTE,
2030                 NULL, HFILL }
2031         },
2032         { &hf_tibia_char_cond_battle,
2033             { "Battle lock", "tibia.cond.battle",
2034                 FT_BOOLEAN, 32,
2035                 TFS(&tfs_yes_no), COND_BATTLE,
2036                 NULL, HFILL }
2037         },
2038         { &hf_tibia_char_cond_drowning,
2039             { "Drowning", "tibia.cond.drowning",
2040                 FT_BOOLEAN, 32,
2041                 TFS(&tfs_yes_no), COND_DROWNING,
2042                 NULL, HFILL }
2043         },
2044         { &hf_tibia_char_cond_freezing,
2045             { "Freezing", "tibia.cond.freezing",
2046                 FT_BOOLEAN, 32,
2047                 TFS(&tfs_yes_no), COND_FREEZING,
2048                 NULL, HFILL }
2049         },
2050         { &hf_tibia_char_cond_dazzled,
2051             { "Dazzled", "tibia.cond.dazzled",
2052                 FT_BOOLEAN, 32,
2053                 TFS(&tfs_yes_no), COND_DAZZLED,
2054                 NULL, HFILL }
2055         },
2056         { &hf_tibia_char_cond_cursed,
2057             { "Cursed", "tibia.cond.cursed",
2058                 FT_BOOLEAN, 32,
2059                 TFS(&tfs_yes_no), COND_CURSED,
2060                 NULL, HFILL }
2061         },
2062         { &hf_tibia_char_cond_buff, /* e.g. after casting Utura */
2063             { "Buff", "tibia.cond.buff",
2064                 FT_BOOLEAN, 32,
2065                 TFS(&tfs_yes_no), COND_BUFF,
2066                 NULL, HFILL }
2067         },
2068         { &hf_tibia_char_cond_pzblock, /* Blocked from entering PZ */
2069             { "Protection Zone Block", "tibia.cond.pzblock",
2070                 FT_BOOLEAN, 32,
2071                 TFS(&tfs_yes_no), COND_PZBLOCK,
2072                 NULL, HFILL }
2073         },
2074         { &hf_tibia_char_cond_pz,
2075             { "Protection Zone", "tibia.cond.pz",
2076                 FT_BOOLEAN, 32,
2077                 TFS(&tfs_yes_no), COND_PZ,
2078                 NULL, HFILL }
2079         },
2080         { &hf_tibia_char_cond_bleeding,
2081             { "Bleeding", "tibia.cond.bleeding",
2082                 FT_BOOLEAN, 32,
2083                 TFS(&tfs_yes_no), COND_BLEEDING,
2084                 NULL, HFILL }
2085         },
2086         { &hf_tibia_char_cond_hungry,
2087             { "Hungry", "tibia.cond.hungry",
2088                 FT_BOOLEAN, 32,
2089                 TFS(&tfs_yes_no), COND_HUNGRY,
2090                 NULL, HFILL }
2091         },
2092         { &hf_tibia_acc_name,
2093             { "Account", "tibia.acc",
2094                 FT_UINT_STRING, BASE_NONE,
2095                 NULL, 0x0,
2096                 NULL, HFILL }
2097         },
2098         { &hf_tibia_acc_number,
2099             { "Account", "tibia.acc",
2100                 FT_STRING, BASE_NONE,
2101                 NULL, 0x0,
2102                 NULL, HFILL }
2103         },
2104         { &hf_tibia_session_key,
2105             { "Session key", "tibia.session_key",
2106                 FT_UINT_STRING, BASE_NONE,
2107                 NULL, 0x0,
2108                 NULL, HFILL }
2109         },
2110         { &hf_tibia_char_name,
2111             { "Character name", "tibia.char",
2112                 FT_UINT_STRING, BASE_NONE,
2113                 NULL, 0x0,
2114                 NULL, HFILL }
2115         },
2116         { &hf_tibia_acc_pass,
2117             { "Password", "tibia.pass",
2118                 FT_UINT_STRING, BASE_NONE,
2119                 NULL, 0x0,
2120                 NULL, HFILL }
2121         },
2122         { &hf_tibia_char_name_convo,
2123             { "Character name", "tibia.char",
2124                 FT_STRING, BASE_NONE,
2125                 NULL, 0x0,
2126                 NULL, HFILL }
2127         },
2128         { &hf_tibia_acc_name_convo,
2129             { "Account", "tibia.acc",
2130                 FT_STRING, BASE_NONE,
2131                 NULL, 0x0,
2132                 NULL, HFILL }
2133         },
2134         { &hf_tibia_acc_pass_convo,
2135             { "Password", "tibia.pass",
2136                 FT_STRING, BASE_NONE,
2137                 NULL, 0x0,
2138                 NULL, HFILL }
2139         },
2140         { &hf_tibia_session_key_convo,
2141             { "Session key", "tibia.session_key",
2142                 FT_STRING, BASE_NONE,
2143                 NULL, 0x0,
2144                 NULL, HFILL }
2145         },
2146         { &hf_tibia_client_info,
2147             { "Client information", "tibia.client.info",
2148                 FT_NONE, BASE_NONE,
2149                 NULL, 0x0,
2150                 NULL, HFILL }
2151         },
2152         { &hf_tibia_client_locale,
2153             { "Locale", "tibia.client.locale",
2154                 FT_NONE, BASE_NONE,
2155                 NULL, 0x0,
2156                 NULL, HFILL }
2157         },
2158         { &hf_tibia_client_locale_id,
2159             { "Locale ID", "tibia.client.locale.id",
2160                 FT_UINT8, BASE_DEC,
2161                 NULL, 0x0,
2162                 NULL, HFILL }
2163         },
2164         { &hf_tibia_client_locale_name,
2165             { "Locale", "tibia.client.locale.name",
2166                 FT_STRING, BASE_NONE,
2167                 NULL, 0x0,
2168                 NULL, HFILL }
2169         },
2170         { &hf_tibia_client_ram,
2171             { "Total RAM", "tibia.client.ram",
2172                 FT_UINT16, BASE_DEC,
2173                 NULL, 0x0,
2174                 NULL, HFILL }
2175         },
2176         { &hf_tibia_client_cpu,
2177             { "CPU", "tibia.client.cpu",
2178                 FT_NONE, BASE_NONE,
2179                 NULL, 0x0,
2180                 NULL, HFILL }
2181         },
2182         { &hf_tibia_client_cpu_name,
2183             { "CPU", "tibia.client.cpu.name",
2184                 FT_STRINGZ, BASE_NONE,
2185                 NULL, 0x0,
2186                 NULL, HFILL }
2187         },
2188         { &hf_tibia_client_clock,
2189             { "CPU clock", "tibia.client.cpu.clock",
2190                 FT_UINT8, BASE_DEC,
2191                 NULL, 0x0,
2192                 NULL, HFILL }
2193         },
2194         { &hf_tibia_client_clock2,
2195             { "CPU clock2", "tibia.client.cpu.clock2",
2196                 FT_UINT8, BASE_DEC,
2197                 NULL, 0x0,
2198                 NULL, HFILL }
2199         },
2200         { &hf_tibia_client_gpu,
2201             { "GPU", "tibia.client.gpu",
2202                 FT_STRINGZ, BASE_NONE,
2203                 NULL, 0x0,
2204                 NULL, HFILL }
2205         },
2206         { &hf_tibia_client_vram,
2207             { "Video RAM", "tibia.client.vram",
2208                 FT_UINT8, BASE_DEC|BASE_UNIT_STRING,
2209                 &mb_unit, 0x0,
2210                 NULL, HFILL }
2211         },
2212         { &hf_tibia_client_resolution,
2213             { "Screen resolution", "tibia.client.resolution",
2214                 FT_NONE, BASE_NONE,
2215                 NULL, 0x0,
2216                 NULL, HFILL }
2217         },
2218         { &hf_tibia_client_resolution_x,
2219             { "Horizontal resolution", "tibia.client.resolution.x",
2220                 FT_UINT16, BASE_DEC,
2221                 NULL, 0x0,
2222                 NULL, HFILL }
2223         },
2224         { &hf_tibia_client_resolution_y,
2225             { "Vertical resolution", "tibia.client.resolution.y",
2226                 FT_UINT16, BASE_DEC,
2227                 NULL, 0x0,
2228                 NULL, HFILL }
2229         },
2230         { &hf_tibia_client_resolution_hz,
2231             { "Refresh rate", "tibia.client.resolution.hz",
2232                 FT_UINT8, BASE_DEC,
2233                 NULL, 0x0,
2234                 NULL, HFILL }
2235         },
2236         { &hf_tibia_payload_len,
2237             { "Payload length", "tibia.payload.len",
2238                 FT_UINT16, BASE_DEC,
2239                 NULL, 0x0,
2240                 NULL, HFILL }
2241         },
2242         { &hf_tibia_loginserv_command,
2243             { "Command", "tibia.cmd",
2244                 FT_UINT8, BASE_HEX,
2245                 VALS(from_loginserv_packet_types), 0x0,
2246                 NULL, HFILL }
2247         },
2248         { &hf_tibia_gameserv_command,
2249             { "Command", "tibia.cmd",
2250                 FT_UINT8, BASE_HEX|BASE_EXT_STRING,
2251                 &from_gameserv_packet_types_ext, 0x0,
2252                 NULL, HFILL }
2253         },
2254         { &hf_tibia_client_command,
2255             { "Command", "tibia.cmd",
2256                 FT_UINT8, BASE_HEX|BASE_EXT_STRING,
2257                 &from_client_packet_types_ext, 0x0,
2258                 NULL, HFILL }
2259         },
2260         { &hf_tibia_motd,
2261             { "Message of the day", "tibia.motd",
2262                 FT_UINT_STRING, BASE_NONE, NULL, 0x0,
2263                 NULL, HFILL }
2264         },
2265         { &hf_tibia_dlg_error,
2266             { "Error message", "tibia.login.err",
2267                 FT_UINT_STRING, BASE_NONE, NULL, 0x0,
2268                 NULL, HFILL }
2269         },
2270         { &hf_tibia_dlg_info,
2271             { "Info message", "tibia.login.info",
2272                 FT_UINT_STRING, BASE_NONE, NULL, 0x0,
2273                 NULL, HFILL }
2274         },
2275         { &hf_tibia_charlist,
2276             { "Character list", "tibia.charlist",
2277                 FT_NONE, BASE_NONE, NULL, 0x0,
2278                 NULL, HFILL }
2279         },
2280         { &hf_tibia_charlist_length,
2281             { "Character count", "tibia.charlist.count",
2282                 FT_UINT8, BASE_DEC,
2283                 NULL, 0x0,
2284                 NULL, HFILL }
2285         },
2286         { &hf_tibia_charlist_entry_name,
2287             { "Character name", "tibia.charlist.name",
2288                 FT_UINT_STRING, BASE_NONE,
2289                 NULL, 0x0,
2290                 NULL, HFILL }
2291         },
2292         { &hf_tibia_charlist_entry_world,
2293             { "World", "tibia.charlist.world",
2294                 FT_UINT_STRING, BASE_NONE,
2295                 NULL, 0x0,
2296                 NULL, HFILL }
2297         },
2298         { &hf_tibia_charlist_entry_ip,
2299             { "IP", "tibia.charlist.ip",
2300                 FT_IPv4, BASE_NONE,
2301                 NULL, 0x0,
2302                 NULL, HFILL }
2303         },
2304         { &hf_tibia_charlist_entry_port,
2305             { "Port", "tibia.charlist.port",
2306                 FT_UINT16, BASE_DEC,
2307                 NULL, 0x0,
2308                 NULL, HFILL }
2309         },
2310         { &hf_tibia_worldlist,
2311             { "World list", "tibia.worldlist",
2312                 FT_NONE, BASE_NONE, NULL, 0x0,
2313                 NULL, HFILL }
2314         },
2315         { &hf_tibia_worldlist_entry_name,
2316             { "World", "tibia.worldlist.name",
2317                 FT_UINT_STRING, BASE_NONE,
2318                 NULL, 0x0,
2319                 NULL, HFILL }
2320         },
2321         { &hf_tibia_worldlist_length,
2322             { "World count", "tibia.worldlist.count",
2323                 FT_UINT16, BASE_DEC,
2324                 NULL, 0x0,
2325                 NULL, HFILL }
2326         },
2327         { &hf_tibia_worldlist_entry_id,
2328             { "World ID", "tibia.worldlist.id",
2329                 FT_UINT8, BASE_DEC,
2330                 NULL, 0x0,
2331                 NULL, HFILL }
2332         },
2333         { &hf_tibia_worldlist_entry_ip,
2334             { "IP", "tibia.worldlist.ip",
2335                 FT_UINT_STRING, BASE_NONE,
2336                 NULL, 0x0,
2337                 NULL, HFILL }
2338         },
2339         { &hf_tibia_worldlist_entry_port,
2340             { "Port", "tibia.worldlist.port",
2341                 FT_UINT16, BASE_DEC,
2342                 NULL, 0x0,
2343                 NULL, HFILL }
2344         },
2345         { &hf_tibia_worldlist_entry_preview,
2346             { "Preview State", "tibia.worldlist.preview",
2347                 FT_BOOLEAN, 8,
2348                 NULL, 0x1,
2349                 NULL, HFILL }
2350         },
2351         { &hf_tibia_pacc_days,
2352             { "Premium days left", "tibia.pacc",
2353                 FT_UINT16, BASE_DEC,
2354                 NULL, 0x0,
2355                 NULL, HFILL }
2356         },
2357         { &hf_tibia_channel_id,
2358             { "Channel id", "tibia.channel.id",
2359                 FT_UINT16, BASE_HEX,
2360                 NULL, 0x0,
2361                 NULL, HFILL }
2362         },
2363         { &hf_tibia_channel_name,
2364             { "Channel name", "tibia.channel",
2365                 FT_UINT_STRING, BASE_NONE,
2366                 NULL, 0x0,
2367                 NULL, HFILL }
2368         },
2369         { &hf_tibia_speech_type,
2370             { "Type", "tibia.speechtype",
2371                 FT_UINT8, BASE_HEX,
2372                 VALS(speech_types), 0x0,
2373                 NULL, HFILL }
2374         },
2375         { &hf_tibia_chat_msg,
2376             { "Message", "tibia.msg",
2377                 FT_UINT_STRING, BASE_NONE, NULL, 0x0,
2378                 NULL, HFILL }
2379         },
2380         { &hf_tibia_coords_x,
2381             { "X-Coordinate", "tibia.coord.x",
2382                 FT_UINT16, BASE_DEC, NULL, 0x0,
2383                 NULL, HFILL }
2384         },
2385         { &hf_tibia_coords_y,
2386             { "Y-Coordinate", "tibia.coords.y",
2387                 FT_UINT16, BASE_DEC, NULL, 0x0,
2388                 NULL, HFILL }
2389         },
2390         { &hf_tibia_coords_z,
2391             { "Z-Coordinate", "tibia.coords.z",
2392                 FT_UINT8, BASE_DEC, NULL, 0x0,
2393                 NULL, HFILL }
2394         },
2395         { &hf_tibia_coords,
2396             { "Coordinates", "tibia.coords",
2397                 FT_STRING, BASE_NONE,
2398                 NULL, 0x0,
2399                 NULL, HFILL }
2400         },
2401         { &hf_tibia_stackpos,
2402             { "Stack position", "tibia.coords.stackpos",
2403                 FT_UINT8, BASE_DEC,
2404                 NULL, 0x0,
2405                 NULL, HFILL }
2406         },
2407 #if 0
2408         { &hf_tibia_item,
2409             { "Item ID", "tibia.item",
2410                 FT_UINT16, BASE_DEC,
2411                 NULL, 0x0,
2412                 NULL, HFILL }
2413         },
2414 #endif
2415         { &hf_tibia_container,
2416             { "Container index", "tibia.container",
2417                 FT_UINT8, BASE_DEC,
2418                 NULL, 0x0,
2419                 NULL, HFILL }
2420         },
2421         { &hf_tibia_container_icon,
2422             { "Container icon", "tibia.container.icon",
2423                 FT_UINT8, BASE_DEC,
2424                 NULL, 0x0,
2425                 NULL, HFILL }
2426         },
2427         { &hf_tibia_container_slot,
2428             { "Container slot", "tibia.container.slot",
2429                 FT_UINT8, BASE_DEC,
2430                 NULL, 0x0,
2431                 NULL, HFILL }
2432         },
2433         { &hf_tibia_container_slots,
2434             { "Container slots", "tibia.container.slots",
2435                 FT_UINT8, BASE_DEC,
2436                 NULL, 0x0,
2437                 NULL, HFILL }
2438         },
2439         { &hf_tibia_inventory,
2440             { "Inventory slot", "tibia.inventory",
2441                 FT_UINT16, BASE_DEC,
2442                 NULL, 0x0,
2443                 NULL, HFILL }
2444         },
2445         { &hf_tibia_vip,
2446             { "VIP GUID", "tibia.vip",
2447                 FT_UINT32, BASE_HEX, NULL, 0x0,
2448                 NULL, HFILL }
2449         },
2450         { &hf_tibia_vip_online,
2451             { "Online", "tibia.vip.online",
2452                 FT_BOOLEAN, 8,
2453                 NULL, 0x1,
2454                 NULL, HFILL }
2455         },
2456         { &hf_tibia_player,
2457             { "Player name", "tibia.player",
2458                 FT_UINT_STRING, BASE_NONE,
2459                 NULL, 0x0,
2460                 NULL, HFILL }
2461         },
2462         { &hf_tibia_creature,
2463             { "Creature", "tibia.creature",
2464                 FT_UINT32, BASE_HEX,
2465                 NULL, 0x0,
2466                 NULL, HFILL }
2467         },
2468         { &hf_tibia_creature_health,
2469             { "Creature", "tibia.creature.health",
2470                 FT_UINT8, BASE_DEC|BASE_UNIT_STRING,
2471                 &units_percent, 0x0,
2472                 NULL, HFILL }
2473         },
2474         { &hf_tibia_window,
2475             { "Window", "tibia.window",
2476                 FT_UINT32, BASE_HEX,
2477                 NULL, 0x0,
2478                 NULL, HFILL }
2479         },
2480         { &hf_tibia_window_icon,
2481             { "Window Icon", "tibia.window.icon",
2482                 FT_UINT8, BASE_HEX,
2483                 NULL, 0x0,
2484                 NULL, HFILL }
2485         },
2486         { &hf_tibia_window_textlen,
2487             { "Window Text Length", "tibia.window.text.len",
2488                 FT_UINT8, BASE_DEC,
2489                 NULL, 0x0,
2490                 NULL, HFILL }
2491         },
2492         { &hf_tibia_window_text,
2493             { "Window Text", "tibia.window.text",
2494                 FT_UINT_STRING, BASE_NONE,
2495                 NULL, 0x0,
2496                 NULL, HFILL }
2497         },
2498         { &hf_tibia_squarecolor,
2499             { "Square Color", "tibia.creature.square",
2500                 FT_UINT8, BASE_HEX,
2501                 NULL, 0x0,
2502                 NULL, HFILL }
2503         },
2504         { &hf_tibia_light_color,
2505             { "Light Color", "tibia.light.color",
2506                 FT_UINT8, BASE_HEX,
2507                 NULL, 0x0,
2508                 NULL, HFILL }
2509         },
2510         { &hf_tibia_light_level,
2511             { "Light Level", "tibia.light.level",
2512                 FT_UINT8, BASE_DEC,
2513                 NULL, 0x0,
2514                 NULL, HFILL }
2515         },
2516         { &hf_tibia_magic_effect_id,
2517             { "Magic Effect", "tibia.magic_effect",
2518                 FT_UINT8, BASE_HEX,
2519                 NULL, 0x0,
2520                 NULL, HFILL }
2521         },
2522         { &hf_tibia_animated_text_color,
2523             { "Text Color", "tibia.animated_text.color",
2524                 FT_UINT8, BASE_HEX,
2525                 NULL, 0x0,
2526                 NULL, HFILL }
2527         },
2528         { &hf_tibia_animated_text,
2529             { "Text", "tibia.animated_text",
2530                 FT_UINT16, BASE_HEX,
2531                 NULL, 0x0,
2532                 NULL, HFILL }
2533         },
2534         { &hf_tibia_textmsg_class,
2535             { "Text Message Class", "tibia.textmsg.class",
2536                 FT_UINT8, BASE_HEX,
2537                 NULL, 0x0,
2538                 NULL, HFILL }
2539         },
2540         { &hf_tibia_textmsg,
2541             { "Text", "tibia.textmsg",
2542                 FT_UINT_STRING, BASE_NONE,
2543                 NULL, 0x0,
2544                 NULL, HFILL }
2545         },
2546         { &hf_tibia_projectile,
2547             { "Projectile", "tibia.projectile",
2548                 FT_UINT32, BASE_HEX,
2549                 NULL, 0x0,
2550                 NULL, HFILL }
2551         },
2552         { &hf_tibia_walk_dir,
2553             { "Walk Direction", "tibia.walk_dir",
2554                 FT_UINT8, BASE_HEX,
2555                 NULL, 0x0,
2556                 NULL, HFILL }
2557         },
2558     };
2559 
2560     /* Setup protocol subtree array */
2561     static gint *ett[] = {
2562         &ett_tibia,
2563         &ett_command,
2564         &ett_file_versions,
2565         &ett_client_info,
2566         &ett_locale,
2567         &ett_cpu,
2568         &ett_resolution,
2569         &ett_charlist,
2570         &ett_char,
2571         &ett_worldlist,
2572         &ett_world,
2573         &ett_coords,
2574         &ett_char_cond,
2575     };
2576 
2577     static ei_register_info ei[] = {
2578         { &ei_xtea_len_toobig,
2579             { "tibia.error.xtea.length.toobig", PI_DECRYPTION, PI_ERROR,
2580                 "XTEA-encrypted length exceeds packet", EXPFILL }
2581         },
2582         { &ei_adler32_checksum_bad, { "tibia.error.checksum_bad", PI_CHECKSUM, PI_ERROR,
2583                 "Bad checksum", EXPFILL }
2584         },
2585         { &ei_rsa_plaintext_no_leading_zero,
2586             { "tibia.error.rsa", PI_DECRYPTION, PI_ERROR,
2587                 "First byte after RSA decryption must be zero", EXPFILL }
2588         },
2589         { &ei_rsa_ciphertext_too_short,
2590             { "tibia.error.rsa.length.tooshort", PI_DECRYPTION, PI_ERROR,
2591                 "RSA-encrypted data is at least 128 byte long", EXPFILL }
2592         },
2593         { &ei_rsa_decrypt_failed,
2594             { "tibia.error.rsa.failed", PI_DECRYPTION, PI_ERROR,
2595                 "Decrypting RSA block failed", EXPFILL }
2596         },
2597     };
2598 
2599     proto_tibia = proto_register_protocol (
2600             "Tibia Protocol", /* name */
2601             "Tibia",          /* short name */
2602             "tibia"           /* abbrev */
2603             );
2604     proto_register_field_array(proto_tibia, hf, array_length(hf));
2605     proto_register_subtree_array(ett, array_length(ett));
2606 
2607     expert_module_t *expert_tibia = expert_register_protocol(proto_tibia);
2608     expert_register_field_array (expert_tibia, ei, array_length (ei));
2609 
2610     module_t *tibia_module = prefs_register_protocol(proto_tibia, proto_reg_handoff_tibia);
2611 
2612     prefs_register_bool_preference(tibia_module, "try_otserv_key", "Try OTServ's RSA key",
2613         "Try the default RSA key in use by nearly all Open Tibia servers", &try_otserv_key);
2614 
2615     prefs_register_bool_preference(tibia_module, "show_char_name", "Show character name for each packet",
2616         "Shows active character for every packet", &show_char_name);
2617     prefs_register_bool_preference(tibia_module, "show_acc_info", "Show account info for each packet",
2618         "Shows account name/password or session key for every packet", &show_acc_info);
2619     prefs_register_bool_preference(tibia_module, "show_xtea_key", "Show symmetric key used for each packet",
2620         "Shows which XTEA key was applied for a packet", &show_xtea_key);
2621     prefs_register_bool_preference(tibia_module, "dissect_game_commands", "Attempt dissection of game packet commands",
2622         "Only decrypt packets and dissect login packets. Pass game commands to the data dissector", &dissect_game_commands);
2623     prefs_register_bool_preference(tibia_module, "reassemble_tcp_segments",
2624                                    "Reassemble Tibia packets spanning multiple TCP segments",
2625                                    "Whether the Tibia dissector should reassemble packets spanning multiple TCP segments."
2626                                    " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
2627                                    &reassemble_tcp_segments);
2628 
2629 
2630 #ifdef HAVE_LIBGNUTLS
2631     static uat_field_t rsakeylist_uats_flds[] = {
2632         UAT_FLD_CSTRING_OTHER(rsakeylist_uats, ipaddr, "IP address", rsakeys_uat_fld_ip_chk_cb, "IPv4 address"),
2633         UAT_FLD_CSTRING_OTHER(rsakeylist_uats, port, "Port", rsakeys_uat_fld_port_chk_cb, "Port Number"),
2634         UAT_FLD_FILENAME_OTHER(rsakeylist_uats, keyfile, "Key File", rsakeys_uat_fld_fileopen_chk_cb, "Private keyfile."),
2635         UAT_FLD_CSTRING_OTHER(rsakeylist_uats, password,"Password", rsakeys_uat_fld_password_chk_cb, "Password (for keyfile)"),
2636         UAT_END_FIELDS
2637     };
2638 
2639     rsakeys_uat = uat_new("RSA Keys",
2640             sizeof(struct rsakeys_assoc),
2641             "tibia_rsa_keys",        /* filename */
2642             TRUE,                    /* from_profile */
2643             &rsakeylist_uats,        /* data_ptr */
2644             &nrsakeys,               /* numitems_ptr */
2645             UAT_AFFECTS_DISSECTION,
2646             NULL,
2647             rsakeys_copy_cb,
2648             NULL,
2649             rsakeys_free_cb,
2650             rsa_parse_uat,
2651             NULL,
2652             rsakeylist_uats_flds);
2653     prefs_register_uat_preference(tibia_module, "rsakey_table",
2654             "RSA keys list",
2655             "A table of RSA keys for decrypting protocols newer than 7.61",
2656             rsakeys_uat
2657     );
2658 
2659     rsakeys = g_hash_table_new_full(rsakey_hash, rsakey_equal, rsakey_free, NULL);
2660 #endif
2661 
2662     static uat_field_t xteakeylist_uats_flds[] = {
2663         UAT_FLD_DEC(xteakeylist_uats, framenum, "Frame Number", "XTEA key"),
2664         UAT_FLD_CSTRING_OTHER(xteakeylist_uats, key, "XTEA Key", xteakeys_uat_fld_key_chk_cb, "Symmetric (XTEA) key"),
2665         UAT_END_FIELDS
2666     };
2667 
2668     xteakeys_uat = uat_new("XTEA Keys",
2669             sizeof(struct xteakeys_assoc),
2670             "tibia_xtea_keys",       /* filename */
2671             TRUE,                    /* from_profile */
2672             &xteakeylist_uats,       /* data_ptr */
2673             &nxteakeys,              /* numitems_ptr */
2674             UAT_AFFECTS_DISSECTION,
2675             NULL,
2676             xteakeys_copy_cb,
2677             NULL,
2678             xteakeys_free_cb,
2679             xtea_parse_uat,
2680             NULL,
2681             xteakeylist_uats_flds);
2682     prefs_register_uat_preference(tibia_module, "xteakey_table",
2683             "XTEA keys list",
2684             "A table of XTEA keys for decrypting protocols newer than 7.61",
2685             xteakeys_uat
2686     );
2687 
2688     xteakeys = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
2689 
2690     /* TODO best way to store this in source? */
2691     const char sexp[] =
2692         "(private-key (rsa"
2693         "(n #9b646903b45b07ac956568d87353bd7165139dd7940703b03e6dd079399661b4a837aa60561d7ccb9452fa0080594909882ab5bca58a1a1b35f8b1059b72b1212611c6152ad3dbb3cfbee7adc142a75d3d75971509c321c5c24a5bd51fd460f01b4e15beb0de1930528a5d3f15c1e3cbf5c401d6777e10acaab33dbe8d5b7ff5#)"
2694         "(e #010001#)"
2695         "(d #428bd3b5346daf71a761106f71a43102f8c857d6549c54660bb6378b52b0261399de8ce648bac410e2ea4e0a1ced1fac2756331220ca6db7ad7b5d440b7828865856e7aa6d8f45837feee9b4a3a0aa21322a1e2ab75b1825e786cf81a28a8a09a1e28519db64ff9baf311e850c2bfa1fb7b08a056cc337f7df443761aefe8d81#)"
2696         "(p #91b37307abe12c05a1b78754746cda444177a784b035cbb96c945affdc022d21da4bd25a4eae259638153e9d73c97c89092096a459e5d16bcadd07fa9d504885#)"
2697         "(q #0111071b206bafb9c7a2287d7c8d17a42e32abee88dfe9520692b5439d9675817ff4f8c94a4abcd4b5f88e220f3a8658e39247a46c6983d85618fd891001a0acb1#)"
2698         "(u #6b21cd5e373fe462a22061b44a41fd01738a3892e0bd8728dbb5b5d86e7675235a469fea3266412fe9a659f486144c1e593d56eb3f6cfc7b2edb83ba8e95403a#)"
2699         "))";
2700 
2701     gcry_error_t err = gcry_sexp_new(&otserv_key, sexp, 0, 1);
2702     if (err)
2703         report_failure("Loading OTServ RSA key failed: %s/%s\n", gcry_strerror(err), gcry_strsource(err));
2704 }
2705 
2706 static guint
get_dissect_tibia_len(packet_info * pinfo _U_,tvbuff_t * tvb,int offset,void * data _U_)2707 get_dissect_tibia_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
2708 {
2709     return tvb_get_letohs(tvb, offset) + 2;
2710 }
2711 
2712 static int
dissect_tibia_tcp(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)2713 dissect_tibia_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
2714 {
2715     static guint32 packet_num, fragment_num;
2716 
2717     if (!packet_num) packet_num = pinfo->num;
2718     if (packet_num != pinfo->num) {
2719         fragment_num = 0;
2720         packet_num = pinfo->num;
2721     }
2722 
2723     fragment_num++;
2724 
2725 
2726     tcp_dissect_pdus(tvb, pinfo, tree, reassemble_tcp_segments, 2,
2727                get_dissect_tibia_len, dissect_tibia, GUINT_TO_POINTER(fragment_num));
2728     return tvb_reported_length(tvb);
2729 }
2730 
2731 void
proto_reg_handoff_tibia(void)2732 proto_reg_handoff_tibia(void)
2733 {
2734     dissector_handle_t tibia_handle = create_dissector_handle(dissect_tibia_tcp, proto_tibia);
2735 
2736     dissector_add_uint_range_with_preference("tcp.port", TIBIA_DEFAULT_TCP_PORT_RANGE, tibia_handle);
2737 }
2738 
2739 
2740 /*
2741  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
2742  *
2743  * Local variables:
2744  * c-basic-offset: 4
2745  * tab-width: 8
2746  * indent-tabs-mode: nil
2747  * End:
2748  *
2749  * vi: set shiftwidth=4 tabstop=8 expandtab:
2750  * :indentSize=4:tabSize=8:noTabs=true:
2751  */
2752