Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-quakeworld.c
blob43f9126964111e3fd7656b56a7942092ff71eed5
1 /* packet-quakeworld.c
2 * Routines for QuakeWorld packet dissection
4 * Uwe Girlich <uwe@planetquake.com>
5 * http://www.idsoftware.com/q1source/q1source.zip
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * Copied from packet-quake.c
13 * SPDX-License-Identifier: GPL-2.0-or-later
16 #include "config.h"
18 #include <stdlib.h>
19 #include <epan/packet.h>
20 #include <epan/prefs.h>
21 #include <epan/expert.h>
23 #include <wsutil/strtoi.h>
25 void proto_register_quakeworld(void);
26 void proto_reg_handoff_quakeworld(void);
28 static dissector_handle_t quakeworld_handle;
30 static int proto_quakeworld;
32 static int hf_quakeworld_s2c;
33 static int hf_quakeworld_c2s;
34 static int hf_quakeworld_connectionless;
35 static int hf_quakeworld_game;
36 static int hf_quakeworld_connectionless_marker;
37 static int hf_quakeworld_connectionless_text;
38 static int hf_quakeworld_connectionless_command;
39 static int hf_quakeworld_connectionless_arguments;
40 static int hf_quakeworld_connectionless_connect_version;
41 static int hf_quakeworld_connectionless_connect_qport;
42 static int hf_quakeworld_connectionless_connect_challenge;
43 static int hf_quakeworld_connectionless_connect_infostring;
44 static int hf_quakeworld_connectionless_connect_infostring_key_value;
45 static int hf_quakeworld_connectionless_connect_infostring_key;
46 static int hf_quakeworld_connectionless_connect_infostring_value;
47 static int hf_quakeworld_connectionless_rcon_password;
48 static int hf_quakeworld_connectionless_rcon_command;
49 static int hf_quakeworld_game_seq1;
50 static int hf_quakeworld_game_rel1;
51 static int hf_quakeworld_game_seq2;
52 static int hf_quakeworld_game_rel2;
53 static int hf_quakeworld_game_qport;
55 static int ett_quakeworld;
56 static int ett_quakeworld_connectionless;
57 static int ett_quakeworld_connectionless_text;
58 static int ett_quakeworld_connectionless_arguments;
59 static int ett_quakeworld_connectionless_connect_infostring;
60 static int ett_quakeworld_connectionless_connect_infostring_key_value;
61 static int ett_quakeworld_game;
62 static int ett_quakeworld_game_seq1;
63 static int ett_quakeworld_game_seq2;
64 static int ett_quakeworld_game_clc;
65 static int ett_quakeworld_game_svc;
67 static expert_field ei_quakeworld_connectionless_command_invalid;
70 helper functions, they may ave to go somewhere else
71 they are mostly copied without change from
72 quakeworldsource/client/cmd.c
73 quakeworldsource/client/common.c
76 #define MAX_TEXT_SIZE 2048
78 static const char *
79 COM_Parse (const char *data, int data_len, int* token_start, int* token_len)
81 int c;
82 char* com_token = (char*)wmem_alloc(wmem_packet_scope(), data_len+1);
84 com_token[0] = '\0';
85 *token_start = 0;
86 *token_len = 0;
88 if (data == NULL)
89 return NULL;
91 /* skip whitespace */
92 skipwhite:
93 while (true) {
94 c = *data;
95 if (c == '\0')
96 return NULL; /* end of file; */
97 if ((c != ' ') && (!g_ascii_iscntrl(c)))
98 break;
99 data++;
100 (*token_start)++;
103 /* skip // comments */
104 if ((c=='/') && (data[1]=='/')) {
105 while (*data && *data != '\n'){
106 data++;
107 (*token_start)++;
109 goto skipwhite;
112 /* handle quoted strings specially */
113 if (c == '\"') {
114 data++;
115 (*token_start)++;
116 while (*token_len < data_len) {
117 c = *data++;
118 if ((c=='\"') || (c=='\0')) {
119 com_token[*token_len] = '\0';
120 return data;
122 com_token[*token_len] = c;
123 (*token_len)++;
127 if (*token_len == data_len) {
128 com_token[*token_len] = '\0';
129 return data;
132 /* parse a regular word */
133 do {
134 com_token[*token_len] = c;
135 data++;
136 (*token_len)++;
137 c = *data;
138 } while (( c != ' ') && (!g_ascii_iscntrl(c)) && (*token_len < data_len));
140 com_token[*token_len] = '\0';
141 return data;
145 #define MAX_ARGS 80
146 static int cmd_argc;
147 static const char *cmd_argv[MAX_ARGS];
148 static const char *cmd_null_string = "";
149 static int cmd_argv_start[MAX_ARGS];
150 static int cmd_argv_length[MAX_ARGS];
154 static int
155 Cmd_Argc(void)
157 return cmd_argc;
161 static const char*
162 Cmd_Argv(int arg)
164 if ( arg >= cmd_argc )
165 return cmd_null_string;
166 return cmd_argv[arg];
170 static int
171 Cmd_Argv_start(int arg)
173 if ( arg >= cmd_argc )
174 return 0;
175 return cmd_argv_start[arg];
179 static int
180 Cmd_Argv_length(int arg)
182 if ( arg >= cmd_argc )
183 return 0;
184 return cmd_argv_length[arg];
188 static void
189 Cmd_TokenizeString(const char* text, int text_len)
191 int start;
192 int com_token_start;
193 int com_token_length;
194 cmd_argc = 0;
196 start = 0;
197 while (start < text_len) {
199 /* skip whitespace up to a \n */
200 while (*text && *text <= ' ' && *text != '\n' && start < text_len) {
201 text++;
202 start++;
205 if (*text == '\n') {
206 /* a newline separates commands in the buffer */
207 text++;
208 break;
211 if ((!*text) || (start == text_len))
212 return;
214 text = COM_Parse (text, text_len-start, &com_token_start, &com_token_length);
215 if (!text)
216 return;
218 if (cmd_argc < MAX_ARGS) {
219 cmd_argv[cmd_argc] = (char*)text;
220 cmd_argv_start[cmd_argc] = start + com_token_start;
221 cmd_argv_length[cmd_argc] = com_token_length;
222 cmd_argc++;
225 start += com_token_start + com_token_length;
230 static void
231 dissect_id_infostring(tvbuff_t *tvb, proto_tree* tree,
232 int offset, char* infostring,
233 int ett_key_value, int hf_key_value, int hf_key, int hf_value)
235 char *newpos = infostring;
236 bool end_of_info = false;
238 /* to look at all the key/value pairs, we destroy infostring */
239 while(!end_of_info) {
240 char* keypos;
241 char* valuepos;
242 int keylength;
243 char* keyvaluesep;
244 int valuelength;
245 char* valueend;
247 keypos = newpos;
248 if (*keypos == '\0') break;
249 if (*keypos == '\\') keypos++;
251 for (keylength = 0
253 *(keypos + keylength) != '\\' &&
254 *(keypos + keylength) != '\0'
256 keylength++)
258 keyvaluesep = keypos + keylength;
259 if (*keyvaluesep == '\0') break;
260 valuepos = keyvaluesep+1;
261 for (valuelength = 0
263 *(valuepos + valuelength) != '\\' &&
264 *(valuepos + valuelength) != '\0'
266 valuelength++)
268 valueend = valuepos + valuelength;
269 if (*valueend == '\0') {
270 end_of_info = true;
272 *(keyvaluesep) = '=';
273 *(valueend) = '\0';
275 if (tree) {
276 proto_item* sub_item;
277 proto_tree* sub_tree;
279 sub_item = proto_tree_add_string(tree,
280 hf_key_value,
281 tvb,
282 offset + (int)(keypos-infostring),
283 keylength + 1 + valuelength, keypos);
284 sub_tree = proto_item_add_subtree(
285 sub_item,
286 ett_key_value);
287 *(keyvaluesep) = '\0';
288 proto_tree_add_string(sub_tree,
289 hf_key,
290 tvb,
291 offset + (int)(keypos-infostring),
292 keylength, keypos);
293 proto_tree_add_string(sub_tree,
294 hf_value,
295 tvb,
296 offset + (int)(valuepos-infostring),
297 valuelength, valuepos);
299 newpos = valueend + 1;
304 static const value_string names_direction[] = {
305 #define DIR_C2S 0
306 { DIR_C2S, "Client to Server" },
307 #define DIR_S2C 1
308 { DIR_S2C, "Server to Client" },
309 { 0, NULL }
313 /* I took this name and value directly out of the QW source. */
314 #define PORT_MASTER 27500 /* Not IANA registered */
315 static range_t *gbl_quakeworldServerPorts;
317 /* out of band message id bytes (taken out of quakeworldsource/client/protocol.h */
319 /* M = master, S = server, C = client, A = any */
320 /* the second character will allways be \n if the message isn't a single */
321 /* byte long (?? not true anymore?) */
323 #define S2C_CHALLENGE 'c'
324 #define S2C_CONNECTION 'j'
325 #define A2A_PING 'k' /* respond with an A2A_ACK */
326 #define A2A_ACK 'l' /* general acknowledgement without info */
327 #define A2A_NACK 'm' /* [+ comment] general failure */
328 #define A2A_ECHO 'e' /* for echoing */
329 #define A2C_PRINT 'n' /* print a message on client */
331 #define S2M_HEARTBEAT 'a' /* + serverinfo + userlist + fraglist */
332 #define A2C_CLIENT_COMMAND 'B' /* + command line */
333 #define S2M_SHUTDOWN 'C'
336 static void
337 dissect_quakeworld_ConnectionlessPacket(tvbuff_t *tvb, packet_info *pinfo,
338 proto_tree *tree, int direction)
340 proto_tree *cl_tree;
341 proto_tree *text_tree = NULL;
342 proto_item *pi = NULL;
343 uint8_t *text;
344 int len;
345 int offset;
346 uint32_t marker;
347 int command_len;
348 const char *command;
349 bool command_finished = false;
351 marker = tvb_get_ntohl(tvb, 0);
352 cl_tree = proto_tree_add_subtree(tree, tvb, 0, -1, ett_quakeworld_connectionless, NULL, "Connectionless");
354 proto_tree_add_uint(cl_tree, hf_quakeworld_connectionless_marker,
355 tvb, 0, 4, marker);
357 /* all the rest of the packet is just text */
358 offset = 4;
360 text = tvb_get_stringz_enc(pinfo->pool, tvb, offset, &len, ENC_ASCII|ENC_NA);
361 /* actually, we should look for a eol char and stop already there */
363 if (cl_tree) {
364 proto_item *text_item;
365 text_item = proto_tree_add_string(cl_tree, hf_quakeworld_connectionless_text,
366 tvb, offset, len, text);
367 text_tree = proto_item_add_subtree(text_item, ett_quakeworld_connectionless_text);
370 if (direction == DIR_C2S) {
371 /* client to server commands */
372 const char *c;
374 Cmd_TokenizeString(text, len);
375 c = Cmd_Argv(0);
377 /* client to sever commands */
378 if (strcmp(c,"ping") == 0) {
379 command = "Ping";
380 command_len = 4;
381 } else if (strcmp(c,"status") == 0) {
382 command = "Status";
383 command_len = 6;
384 } else if (strcmp(c,"log") == 0) {
385 command = "Log";
386 command_len = 3;
387 } else if (strcmp(c,"connect") == 0) {
388 uint32_t version = 0;
389 uint16_t qport = 0;
390 uint32_t challenge = 0;
391 bool version_valid = true;
392 bool qport_valid = true;
393 bool challenge_valid = true;
394 const char *infostring;
395 proto_tree *argument_tree = NULL;
396 command = "Connect";
397 command_len = Cmd_Argv_length(0);
398 if (text_tree) {
399 proto_item *argument_item;
400 pi = proto_tree_add_string(text_tree, hf_quakeworld_connectionless_command,
401 tvb, offset, command_len, command);
402 argument_item = proto_tree_add_string(text_tree,
403 hf_quakeworld_connectionless_arguments,
404 tvb, offset + Cmd_Argv_start(1), len + 1 - Cmd_Argv_start(1),
405 text + Cmd_Argv_start(1));
406 argument_tree = proto_item_add_subtree(argument_item,
407 ett_quakeworld_connectionless_arguments);
408 command_finished=true;
410 version_valid = ws_strtou32(Cmd_Argv(1), NULL, &version);
411 qport_valid = ws_strtou16(Cmd_Argv(2), NULL, &qport);
412 challenge_valid = ws_strtou32(Cmd_Argv(3), NULL, &challenge);
413 infostring = Cmd_Argv(4);
415 if (text_tree && (!version_valid || !qport_valid || !challenge_valid))
416 expert_add_info(pinfo, pi, &ei_quakeworld_connectionless_command_invalid);
418 if (argument_tree) {
419 proto_item *info_item;
420 proto_tree *info_tree;
421 proto_tree_add_uint(argument_tree,
422 hf_quakeworld_connectionless_connect_version,
423 tvb,
424 offset + Cmd_Argv_start(1),
425 Cmd_Argv_length(1), version);
426 proto_tree_add_uint(argument_tree,
427 hf_quakeworld_connectionless_connect_qport,
428 tvb,
429 offset + Cmd_Argv_start(2),
430 Cmd_Argv_length(2), qport);
431 proto_tree_add_int(argument_tree,
432 hf_quakeworld_connectionless_connect_challenge,
433 tvb,
434 offset + Cmd_Argv_start(3),
435 Cmd_Argv_length(3), challenge);
436 info_item = proto_tree_add_string(argument_tree,
437 hf_quakeworld_connectionless_connect_infostring,
438 tvb,
439 offset + Cmd_Argv_start(4),
440 Cmd_Argv_length(4), infostring);
441 info_tree = proto_item_add_subtree(
442 info_item, ett_quakeworld_connectionless_connect_infostring);
443 dissect_id_infostring(tvb, info_tree, offset + Cmd_Argv_start(4),
444 wmem_strdup(pinfo->pool, infostring),
445 ett_quakeworld_connectionless_connect_infostring_key_value,
446 hf_quakeworld_connectionless_connect_infostring_key_value,
447 hf_quakeworld_connectionless_connect_infostring_key,
448 hf_quakeworld_connectionless_connect_infostring_value);
450 } else if (strcmp(c,"getchallenge") == 0) {
451 command = "Get Challenge";
452 command_len = Cmd_Argv_length(0);
453 } else if (strcmp(c,"rcon") == 0) {
454 const char* password;
455 int i;
456 char remaining[MAX_TEXT_SIZE+1];
457 proto_tree *argument_tree = NULL;
458 command = "Remote Command";
459 command_len = Cmd_Argv_length(0);
460 if (text_tree) {
461 proto_item *argument_item;
462 proto_tree_add_string(text_tree, hf_quakeworld_connectionless_command,
463 tvb, offset, command_len, command);
464 argument_item = proto_tree_add_string(text_tree,
465 hf_quakeworld_connectionless_arguments,
466 tvb, offset + Cmd_Argv_start(1), len - Cmd_Argv_start(1),
467 text + Cmd_Argv_start(1));
468 argument_tree = proto_item_add_subtree(argument_item,
469 ett_quakeworld_connectionless_arguments);
470 command_finished=true;
472 password = Cmd_Argv(1);
473 if (argument_tree) {
474 proto_tree_add_string(argument_tree,
475 hf_quakeworld_connectionless_rcon_password,
476 tvb,
477 offset + Cmd_Argv_start(1),
478 Cmd_Argv_length(1), password);
480 remaining[0] = '\0';
481 for (i=2; i<Cmd_Argc() ; i++) {
482 (void) g_strlcat (remaining, Cmd_Argv(i), MAX_TEXT_SIZE+1);
483 (void) g_strlcat (remaining, " ", MAX_TEXT_SIZE+1);
485 if (text_tree) {
486 proto_tree_add_string(argument_tree,
487 hf_quakeworld_connectionless_rcon_command,
488 tvb, offset + Cmd_Argv_start(2),
489 Cmd_Argv_start(Cmd_Argc()-1) + Cmd_Argv_length(Cmd_Argc()-1) -
490 Cmd_Argv_start(2),
491 remaining);
493 } else if (c[0]==A2A_PING && ( c[1]=='\0' || c[1]=='\n')) {
494 command = "Ping";
495 command_len = 1;
496 } else if (c[0]==A2A_ACK && ( c[1]=='\0' || c[1]=='\n')) {
497 command = "Ack";
498 command_len = 1;
499 } else {
500 command = "Unknown";
501 command_len = len - 1;
504 else {
505 /* server to client commands */
506 if (text[0] == S2C_CONNECTION) {
507 command = "Connected";
508 command_len = 1;
509 } else if (text[0] == A2C_CLIENT_COMMAND) {
510 command = "Client Command";
511 command_len = 1;
512 /* stringz (command), stringz (localid) */
513 } else if (text[0] == A2C_PRINT) {
514 command = "Print";
515 command_len = 1;
516 /* string */
517 } else if (text[0] == A2A_PING) {
518 command = "Ping";
519 command_len = 1;
520 } else if (text[0] == S2C_CHALLENGE) {
521 command = "Challenge";
522 command_len = 1;
523 /* string, conversion */
524 } else {
525 command = "Unknown";
526 command_len = len - 1;
530 col_append_fstr(pinfo->cinfo, COL_INFO, " %s", command);
532 if (!command_finished) {
533 proto_tree_add_string(text_tree, hf_quakeworld_connectionless_command,
534 tvb, offset, command_len, command);
536 /*offset += len;*/
540 static void
541 dissect_quakeworld_client_commands(tvbuff_t *tvb, packet_info *pinfo,
542 proto_tree *tree)
544 /* If I have too much time at hand, I'll fill it with all
545 the information from my QWD specs:
546 http://www.planetquake.com/demospecs/qwd/
548 call_data_dissector(tvb, pinfo, tree);
552 static void
553 dissect_quakeworld_server_commands(tvbuff_t *tvb, packet_info *pinfo,
554 proto_tree *tree)
556 /* If I have too much time at hand, I'll fill it with all
557 the information from my QWD specs:
558 http://www.planetquake.com/demospecs/qwd/
560 call_data_dissector(tvb, pinfo, tree);
564 static const value_string names_reliable[] = {
565 { 0, "Non Reliable" },
566 { 1, "Reliable" },
567 { 0, NULL }
571 static void
572 dissect_quakeworld_GamePacket(tvbuff_t *tvb, packet_info *pinfo,
573 proto_tree *tree, int direction)
575 proto_tree *game_tree = NULL;
576 uint32_t seq1;
577 uint32_t seq2;
578 int rel1;
579 int rel2;
580 int offset;
581 unsigned rest_length;
583 direction = value_is_in_range(gbl_quakeworldServerPorts, pinfo->destport) ?
584 DIR_C2S : DIR_S2C;
586 game_tree = proto_tree_add_subtree(tree, tvb, 0, -1, ett_quakeworld_game, NULL, "Game");
588 offset = 0;
590 seq1 = tvb_get_letohl(tvb, offset);
591 rel1 = seq1 & 0x80000000 ? 1 : 0;
592 seq1 &= ~0x80000000;
593 if (game_tree) {
594 proto_tree *seq1_tree = proto_tree_add_subtree_format(game_tree,
595 tvb, offset, 4, ett_quakeworld_game_seq1, NULL, "Current Sequence: %u (%s)",
596 seq1, val_to_str(rel1,names_reliable,"%u"));
597 proto_tree_add_uint(seq1_tree, hf_quakeworld_game_seq1,
598 tvb, offset, 4, seq1);
599 proto_tree_add_boolean(seq1_tree, hf_quakeworld_game_rel1,
600 tvb, offset+3, 1, rel1);
602 offset += 4;
604 seq2 = tvb_get_letohl(tvb, offset);
605 rel2 = seq2 & 0x80000000 ? 1 : 0;
606 seq2 &= ~0x80000000;
607 if (game_tree) {
608 proto_tree *seq2_tree = proto_tree_add_subtree_format(game_tree,
609 tvb, offset, 4, ett_quakeworld_game_seq2, NULL, "Acknowledge Sequence: %u (%s)",
610 seq2, val_to_str(rel2,names_reliable,"%u"));
611 proto_tree_add_uint(seq2_tree, hf_quakeworld_game_seq2, tvb, offset, 4, seq2);
612 proto_tree_add_boolean(seq2_tree, hf_quakeworld_game_rel2, tvb, offset+3, 1, rel2);
614 offset += 4;
616 if (direction == DIR_C2S) {
617 /* client to server */
618 uint16_t qport = tvb_get_letohs(tvb, offset);
619 if (game_tree) {
620 proto_tree_add_uint(game_tree, hf_quakeworld_game_qport, tvb, offset, 2, qport);
622 offset +=2;
625 /* all the rest is pure game data */
626 rest_length = tvb_reported_length(tvb) - offset;
627 if (rest_length) {
628 tvbuff_t *next_tvb = tvb_new_subset_remaining(tvb, offset);
629 proto_tree *c_tree;
631 if (direction == DIR_C2S) {
632 c_tree = proto_tree_add_subtree(game_tree, next_tvb,
633 0, -1, ett_quakeworld_game_clc, NULL, "Client Commands");
634 dissect_quakeworld_client_commands(next_tvb, pinfo, c_tree);
636 else {
637 c_tree = proto_tree_add_subtree(game_tree, next_tvb,
638 0, -1, ett_quakeworld_game_svc, NULL, "Server Commands");
640 dissect_quakeworld_server_commands(next_tvb, pinfo, c_tree);
646 static int
647 dissect_quakeworld(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
649 proto_tree *quakeworld_tree = NULL;
650 int direction;
652 direction = value_is_in_range(gbl_quakeworldServerPorts, pinfo->destport) ?
653 DIR_C2S : DIR_S2C;
655 col_set_str(pinfo->cinfo, COL_PROTOCOL, "QUAKEWORLD");
656 col_add_str(pinfo->cinfo, COL_INFO, val_to_str(direction,
657 names_direction, "%u"));
659 if (tree) {
660 proto_item *quakeworld_item;
661 quakeworld_item = proto_tree_add_item(tree, proto_quakeworld,
662 tvb, 0, -1, ENC_NA);
663 quakeworld_tree = proto_item_add_subtree(quakeworld_item, ett_quakeworld);
664 proto_tree_add_uint_format(quakeworld_tree,
665 direction == DIR_S2C ?
666 hf_quakeworld_s2c :
667 hf_quakeworld_c2s,
668 tvb, 0, 0, 1,
669 "Direction: %s", val_to_str(direction, names_direction, "%u"));
672 if (tvb_get_ntohl(tvb, 0) == 0xffffffff) {
673 col_append_str(pinfo->cinfo, COL_INFO, " Connectionless");
674 proto_tree_add_uint_format(quakeworld_tree,
675 hf_quakeworld_connectionless,
676 tvb, 0, 0, 1,
677 "Type: Connectionless");
678 dissect_quakeworld_ConnectionlessPacket(
679 tvb, pinfo, quakeworld_tree, direction);
681 else {
682 col_append_str(pinfo->cinfo, COL_INFO, " Game");
683 proto_tree_add_uint_format(quakeworld_tree,
684 hf_quakeworld_game,
685 tvb, 0, 0, 1,
686 "Type: Game");
687 dissect_quakeworld_GamePacket(
688 tvb, pinfo, quakeworld_tree, direction);
690 return tvb_captured_length(tvb);
693 static void
694 apply_quakeworld_prefs(void)
696 /* Port preference used to determine client/server */
697 gbl_quakeworldServerPorts = prefs_get_range_value("quakeworld", "udp.port");
700 void
701 proto_register_quakeworld(void)
703 expert_module_t* expert_quakeworld;
705 static hf_register_info hf[] = {
706 { &hf_quakeworld_c2s,
707 { "Client to Server", "quakeworld.c2s",
708 FT_UINT32, BASE_DEC, NULL, 0x0,
709 NULL, HFILL }},
710 { &hf_quakeworld_s2c,
711 { "Server to Client", "quakeworld.s2c",
712 FT_UINT32, BASE_DEC, NULL, 0x0,
713 NULL, HFILL }},
714 { &hf_quakeworld_connectionless,
715 { "Connectionless", "quakeworld.connectionless",
716 FT_UINT32, BASE_DEC, NULL, 0x0,
717 NULL, HFILL }},
718 { &hf_quakeworld_game,
719 { "Game", "quakeworld.game",
720 FT_UINT32, BASE_DEC, NULL, 0x0,
721 NULL, HFILL }},
722 { &hf_quakeworld_connectionless_marker,
723 { "Marker", "quakeworld.connectionless.marker",
724 FT_UINT32, BASE_HEX, NULL, 0x0,
725 NULL, HFILL }},
726 { &hf_quakeworld_connectionless_text,
727 { "Text", "quakeworld.connectionless.text",
728 FT_STRING, BASE_NONE, NULL, 0x0,
729 NULL, HFILL }},
730 { &hf_quakeworld_connectionless_command,
731 { "Command", "quakeworld.connectionless.command",
732 FT_STRING, BASE_NONE, NULL, 0x0,
733 NULL, HFILL }},
734 { &hf_quakeworld_connectionless_arguments,
735 { "Arguments", "quakeworld.connectionless.arguments",
736 FT_STRING, BASE_NONE, NULL, 0x0,
737 NULL, HFILL }},
738 { &hf_quakeworld_connectionless_connect_version,
739 { "Version", "quakeworld.connectionless.connect.version",
740 FT_UINT32, BASE_DEC, NULL, 0x0,
741 "Protocol Version", HFILL }},
742 { &hf_quakeworld_connectionless_connect_qport,
743 { "QPort", "quakeworld.connectionless.connect.qport",
744 FT_UINT32, BASE_DEC, NULL, 0x0,
745 "QPort of the client", HFILL }},
746 { &hf_quakeworld_connectionless_connect_challenge,
747 { "Challenge", "quakeworld.connectionless.connect.challenge",
748 FT_INT32, BASE_DEC, NULL, 0x0,
749 "Challenge from the server", HFILL }},
750 { &hf_quakeworld_connectionless_connect_infostring,
751 { "Infostring", "quakeworld.connectionless.connect.infostring",
752 FT_STRING, BASE_NONE, NULL, 0x0,
753 "Infostring with additional variables", HFILL }},
754 { &hf_quakeworld_connectionless_connect_infostring_key_value,
755 { "Key/Value", "quakeworld.connectionless.connect.infostring.key_value",
756 FT_STRING, BASE_NONE, NULL, 0x0,
757 "Key and Value", HFILL }},
758 { &hf_quakeworld_connectionless_connect_infostring_key,
759 { "Key", "quakeworld.connectionless.connect.infostring.key",
760 FT_STRING, BASE_NONE, NULL, 0x0,
761 "Infostring Key", HFILL }},
762 { &hf_quakeworld_connectionless_connect_infostring_value,
763 { "Value", "quakeworld.connectionless.connect.infostring.value",
764 FT_STRING, BASE_NONE, NULL, 0x0,
765 "Infostring Value", HFILL }},
766 { &hf_quakeworld_connectionless_rcon_password,
767 { "Password", "quakeworld.connectionless.rcon.password",
768 FT_STRING, BASE_NONE, NULL, 0x0,
769 "Rcon Password", HFILL }},
770 { &hf_quakeworld_connectionless_rcon_command,
771 { "Command", "quakeworld.connectionless.rcon.command",
772 FT_STRING, BASE_NONE, NULL, 0x0,
773 NULL, HFILL }},
774 { &hf_quakeworld_game_seq1,
775 { "Sequence Number", "quakeworld.game.seq1",
776 FT_UINT32, BASE_DEC, NULL, 0x0,
777 "Sequence number of the current packet", HFILL }},
778 { &hf_quakeworld_game_rel1,
779 { "Reliable", "quakeworld.game.rel1",
780 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
781 "Packet is reliable and may be retransmitted", HFILL }},
782 { &hf_quakeworld_game_seq2,
783 { "Sequence Number", "quakeworld.game.seq2",
784 FT_UINT32, BASE_DEC, NULL, 0x0,
785 "Sequence number of the last received packet", HFILL }},
786 { &hf_quakeworld_game_rel2,
787 { "Reliable", "quakeworld.game.rel2",
788 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
789 "Packet was reliable and may be retransmitted", HFILL }},
790 { &hf_quakeworld_game_qport,
791 { "QPort", "quakeworld.game.qport",
792 FT_UINT32, BASE_DEC, NULL, 0x0,
793 "QuakeWorld Client Port", HFILL }}
795 static int *ett[] = {
796 &ett_quakeworld,
797 &ett_quakeworld_connectionless,
798 &ett_quakeworld_connectionless_text,
799 &ett_quakeworld_connectionless_arguments,
800 &ett_quakeworld_connectionless_connect_infostring,
801 &ett_quakeworld_connectionless_connect_infostring_key_value,
802 &ett_quakeworld_game,
803 &ett_quakeworld_game_seq1,
804 &ett_quakeworld_game_seq2,
805 &ett_quakeworld_game_clc,
806 &ett_quakeworld_game_svc
809 static ei_register_info ei[] = {
810 { &ei_quakeworld_connectionless_command_invalid, { "quakeworld.connectionless.command.invalid",
811 PI_MALFORMED, PI_ERROR, "Invalid connectionless command", EXPFILL }}
814 proto_quakeworld = proto_register_protocol("QuakeWorld Network Protocol", "QUAKEWORLD", "quakeworld");
815 proto_register_field_array(proto_quakeworld, hf, array_length(hf));
816 proto_register_subtree_array(ett, array_length(ett));
818 /* Register the dissector handle */
819 quakeworld_handle = register_dissector("quakeworld", dissect_quakeworld, proto_quakeworld);
821 /* Register a configuration option for port */
822 prefs_register_protocol(proto_quakeworld, apply_quakeworld_prefs);
824 expert_quakeworld = expert_register_protocol(proto_quakeworld);
825 expert_register_field_array(expert_quakeworld, ei, array_length(ei));
829 void
830 proto_reg_handoff_quakeworld(void)
832 dissector_add_uint_with_preference("udp.port", PORT_MASTER, quakeworld_handle);
833 apply_quakeworld_prefs();
837 * Editor modelines - https://www.wireshark.org/tools/modelines.html
839 * Local variables:
840 * c-basic-offset: 8
841 * tab-width: 8
842 * indent-tabs-mode: t
843 * End:
845 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
846 * :indentSize=8:tabSize=8:noTabs=false: