446c9d5e064b22e76db0e67655de3fe1eda773ef
[obnox/wireshark/wip.git] / epan / dissectors / packet-quakeworld.c
1 /* packet-quakeworld.c
2  * Routines for QuakeWorld packet dissection
3  *
4  * Uwe Girlich <uwe@planetquake.com>
5  *      http://www.idsoftware.com/q1source/q1source.zip
6  *
7  * $Id$
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * Copied from packet-quake.c
14  *
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28  */
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <glib.h>
35 #include <string.h>
36 #include <epan/packet.h>
37 #include <epan/prefs.h>
38 #include <epan/strutil.h>
39
40 static int proto_quakeworld = -1;
41
42 static int hf_quakeworld_s2c = -1;
43 static int hf_quakeworld_c2s = -1;
44 static int hf_quakeworld_connectionless = -1;
45 static int hf_quakeworld_game = -1;
46 static int hf_quakeworld_connectionless_marker = -1;
47 static int hf_quakeworld_connectionless_text = -1;
48 static int hf_quakeworld_connectionless_command = -1;
49 static int hf_quakeworld_connectionless_arguments = -1;
50 static int hf_quakeworld_connectionless_connect_version = -1;
51 static int hf_quakeworld_connectionless_connect_qport = -1;
52 static int hf_quakeworld_connectionless_connect_challenge = -1;
53 static int hf_quakeworld_connectionless_connect_infostring = -1;
54 static int hf_quakeworld_connectionless_connect_infostring_key_value = -1;
55 static int hf_quakeworld_connectionless_connect_infostring_key = -1;
56 static int hf_quakeworld_connectionless_connect_infostring_value = -1;
57 static int hf_quakeworld_connectionless_rcon_password = -1;
58 static int hf_quakeworld_connectionless_rcon_command = -1;
59 static int hf_quakeworld_game_seq1 = -1;
60 static int hf_quakeworld_game_rel1 = -1;
61 static int hf_quakeworld_game_seq2 = -1;
62 static int hf_quakeworld_game_rel2 = -1;
63 static int hf_quakeworld_game_qport = -1;
64
65 static gint ett_quakeworld = -1;
66 static gint ett_quakeworld_connectionless = -1;
67 static gint ett_quakeworld_connectionless_text = -1;
68 static gint ett_quakeworld_connectionless_arguments = -1;
69 static gint ett_quakeworld_connectionless_connect_infostring = -1;
70 static gint ett_quakeworld_connectionless_connect_infostring_key_value = -1;
71 static gint ett_quakeworld_game = -1;
72 static gint ett_quakeworld_game_seq1 = -1;
73 static gint ett_quakeworld_game_seq2 = -1;
74 static gint ett_quakeworld_game_clc = -1;
75 static gint ett_quakeworld_game_svc = -1;
76
77 static dissector_handle_t data_handle;
78
79 /*
80    helper functions, they may ave to go somewhere else
81    they are mostly copied without change from
82         quakeworldsource/client/cmd.c
83         quakeworldsource/client/common.c
84 */
85
86 #define MAX_TEXT_SIZE   2048
87
88 static  char            com_token[MAX_TEXT_SIZE+1];
89 static  int             com_token_start;
90 static  int             com_token_length;
91
92 static char *
93 COM_Parse (char *data)
94 {
95         int     c;
96         int     len;
97
98         len = 0;
99         com_token[0] = 0;
100         com_token_start = 0;
101         com_token_length = 0;
102
103         if (data == NULL)
104                 return NULL;
105
106         /* skip whitespace */
107 skipwhite:
108         while ( (c = *data) <= ' ') {
109                 if (c == 0)
110                         return NULL;    /* end of file; */
111                 data++;
112                 com_token_start++;
113         }
114
115         /* skip // comments */
116         if (c=='/' && data[1] == '/') {
117                 while (*data && *data != '\n')
118                         data++;
119                         com_token_start++;
120                 goto skipwhite;
121         }
122
123         /* handle quoted strings specially */
124         if (c == '\"') {
125                 data++;
126                 com_token_start++;
127                 while (1) {
128                         c = *data++;
129                         if (c=='\"' || c==0) {
130                                 com_token[len] = 0;
131                                 return data;
132                         }
133                         com_token[len] = c;
134                         len++;
135                         com_token_length++;
136                 }
137         }
138
139         /* parse a regular word */
140         do {
141                 com_token[len] = c;
142                 data++;
143                 len++;
144                 com_token_length++;
145                 c = *data;
146         } while (c>32);
147
148         com_token[len] = 0;
149         return data;
150 }
151
152
153 #define                 MAX_ARGS 80
154 static  int             cmd_argc = 0;
155 static  char            *cmd_argv[MAX_ARGS];
156 static  const char      *cmd_null_string = "";
157 static  int             cmd_argv_start[MAX_ARGS];
158 static  int             cmd_argv_length[MAX_ARGS];
159
160
161
162 static int
163 Cmd_Argc(void)
164 {
165         return cmd_argc;
166 }
167
168
169 static const char*
170 Cmd_Argv(int arg)
171 {
172         if ( arg >= cmd_argc )
173                 return cmd_null_string;
174         return cmd_argv[arg];
175 }
176
177
178 static int
179 Cmd_Argv_start(int arg)
180 {
181         if ( arg >= cmd_argc )
182                 return 0;
183         return cmd_argv_start[arg];
184 }
185
186
187 static int
188 Cmd_Argv_length(int arg)
189 {
190         if ( arg >= cmd_argc )
191                 return 0;
192         return cmd_argv_length[arg];
193 }
194
195
196 static void
197 Cmd_TokenizeString(char* text)
198 {
199         int i;
200         int start;
201         int length;
202
203
204         /* clear the args from the last string */
205         for (i=0 ; i<cmd_argc ; i++)
206                 g_free(cmd_argv[i]);
207
208         cmd_argc = 0;
209
210         start = 0;
211         while (TRUE) {
212
213                 /* skip whitespace up to a \n */
214                 while (*text && *text <= ' ' && *text != '\n') {
215                         text++;
216                         start++;
217                 }
218
219                 length = 0;
220
221                 if (*text == '\n') {
222                         /* a newline seperates commands in the buffer */
223                         text++;
224                         break;
225                 }
226
227                 if (!*text)
228                         return;
229
230                 text = COM_Parse (text);
231                 if (!text)
232                         return;
233
234                 if (cmd_argc < MAX_ARGS) {
235                         cmd_argv[cmd_argc] = g_strdup(com_token);
236                         cmd_argv_start[cmd_argc] = start + com_token_start;
237                         cmd_argv_length[cmd_argc] = com_token_length;
238                         cmd_argc++;
239                 }
240
241                 start += com_token_start + com_token_length;
242         }
243 }
244
245
246 static void
247 dissect_id_infostring(tvbuff_t *tvb, proto_tree* tree,
248         int offset, char* infostring,
249         gint ett_key_value, int hf_key_value, int hf_key, int hf_value)
250 {
251         char * newpos = infostring;
252         int end_of_info = FALSE;
253
254         /* to look at all the key/value pairs, we destroy infostring */
255         while(!end_of_info) {
256                 char* keypos;
257                 char* valuepos;
258                 int keylength;
259                 char* keyvaluesep;
260                 int valuelength;
261                 char* valueend;
262
263                 keypos = newpos;
264                 if (*keypos == 0) break;
265                 if (*keypos == '\\') keypos++;
266
267                 for (keylength = 0
268                         ;
269                         *(keypos + keylength) != '\\' &&
270                         *(keypos + keylength) != 0
271                         ;
272                         keylength++) ;
273                 keyvaluesep = keypos + keylength;
274                 if (*keyvaluesep == 0) break;
275                 valuepos = keyvaluesep+1;
276                 for (valuelength = 0
277                         ;
278                         *(valuepos + valuelength) != '\\' &&
279                         *(valuepos + valuelength) != 0
280                         ;
281                         valuelength++) ;
282                 valueend = valuepos + valuelength;
283                 if (*valueend == 0) {
284                         end_of_info = TRUE;
285                 }
286                 *(keyvaluesep) = '=';
287                 *(valueend) = 0;
288
289                 if (tree) {
290                         proto_item* sub_item = NULL;
291                         proto_tree* sub_tree = NULL;
292
293                         sub_item = proto_tree_add_string(tree,
294                                 hf_key_value,
295                                 tvb,
296                                 offset + (gint)(keypos-infostring),
297                                 keylength + 1 + valuelength, keypos);
298                         if (sub_item)
299                                 sub_tree =
300                                         proto_item_add_subtree(
301                                         sub_item,
302                                         ett_key_value);
303                         *(keyvaluesep) = 0;
304                         if (sub_tree) {
305                                 proto_tree_add_string(sub_tree,
306                                         hf_key,
307                                         tvb,
308                                         offset + (gint)(keypos-infostring),
309                                         keylength, keypos);
310                                 proto_tree_add_string(sub_tree,
311                                         hf_value,
312                                         tvb,
313                                         offset + (gint)(valuepos-infostring),
314                                         valuelength, valuepos);
315                         }
316                 }
317                 newpos = valueend + 1;
318         }
319 }
320
321
322 static const value_string names_direction[] = {
323 #define DIR_C2S 0
324         { DIR_C2S, "Client to Server" },
325 #define DIR_S2C 1
326         { DIR_S2C, "Server to Client" },
327         { 0, NULL }
328 };
329
330
331 /* I took this name and value directly out of the QW source. */
332 #define PORT_MASTER 27500
333 static guint gbl_quakeworldServerPort=PORT_MASTER;
334
335
336 /* out of band message id bytes (taken out of quakeworldsource/client/protocol.h */
337
338 /* M = master, S = server, C = client, A = any */
339 /* the second character will allways be \n if the message isn't a single */
340 /* byte long (?? not true anymore?) */
341
342 #define S2C_CHALLENGE           'c'
343 #define S2C_CONNECTION          'j'
344 #define A2A_PING                'k'     /* respond with an A2A_ACK */
345 #define A2A_ACK                 'l'     /* general acknowledgement without info */
346 #define A2A_NACK                'm'     /* [+ comment] general failure */
347 #define A2A_ECHO                'e'     /* for echoing */
348 #define A2C_PRINT               'n'     /* print a message on client */
349
350 #define S2M_HEARTBEAT           'a'     /* + serverinfo + userlist + fraglist */
351 #define A2C_CLIENT_COMMAND      'B'     /* + command line */
352 #define S2M_SHUTDOWN            'C'
353
354
355 static void
356 dissect_quakeworld_ConnectionlessPacket(tvbuff_t *tvb, packet_info *pinfo,
357         proto_tree *tree, int direction)
358 {
359         proto_tree      *cl_tree = NULL;
360         proto_item      *cl_item = NULL;
361         proto_item      *text_item = NULL;
362         proto_tree      *text_tree = NULL;
363         guint8          text[MAX_TEXT_SIZE+1];
364         int             len;
365         int             offset;
366         guint32 marker;
367         int command_len;
368         char *command="";
369         int command_finished = FALSE;
370
371
372         marker = tvb_get_ntohl(tvb, 0);
373         if (tree) {
374                 cl_item = proto_tree_add_text(tree, tvb,
375                                 0, -1, "Connectionless");
376                 if (cl_item)
377                         cl_tree = proto_item_add_subtree(
378                                 cl_item, ett_quakeworld_connectionless);
379         }
380
381         if (cl_tree) {
382                 proto_tree_add_uint(cl_tree, hf_quakeworld_connectionless_marker,
383                                 tvb, 0, 4, marker);
384         }
385
386         /* all the rest of the packet is just text */
387         offset = 4;
388
389         len = tvb_get_nstringz0(tvb, offset, sizeof(text), text);
390         /* actually, we should look for a eol char and stop already there */
391
392         if (cl_tree) {
393                 text_item = proto_tree_add_string(cl_tree, hf_quakeworld_connectionless_text,
394                         tvb, offset, len + 1, text);
395                 if (text_item) {
396                         text_tree = proto_item_add_subtree(
397                                 text_item, ett_quakeworld_connectionless_text);
398                 }
399         }
400
401         if (direction == DIR_C2S) {
402                 /* client to server commands */
403                 const char *c;
404
405                 Cmd_TokenizeString(text);
406                 c = Cmd_Argv(0);
407
408                 /* client to sever commands */
409                 if (strcmp(c,"ping") == 0) {
410                         command="Ping";
411                         command_len = 4;
412                 } else if (strcmp(c,"status") == 0) {
413                         command="Status";
414                         command_len = 6;
415                 } else if (strcmp(c,"log") == 0) {
416                         command="Log";
417                         command_len = 3;
418                 } else if (strcmp(c,"connect") == 0) {
419                         int version;
420                         int qport;
421                         int challenge;
422                         const char *infostring;
423                         proto_item *argument_item = NULL;
424                         proto_tree *argument_tree = NULL;
425                         proto_item *info_item = NULL;
426                         proto_tree *info_tree = NULL;
427                         command="Connect";
428                         command_len = Cmd_Argv_length(0);
429                         if (text_tree) {
430                                 proto_tree_add_string(text_tree, hf_quakeworld_connectionless_command,
431                                         tvb, offset, command_len, command);
432                                 argument_item = proto_tree_add_string(text_tree,
433                                         hf_quakeworld_connectionless_arguments,
434                                         tvb, offset + Cmd_Argv_start(1), len + 1 - Cmd_Argv_start(1),
435                                         text + Cmd_Argv_start(1));
436                                 if (argument_item) {
437                                         argument_tree =
438                                                 proto_item_add_subtree(argument_item,
439                                                         ett_quakeworld_connectionless_arguments);
440                                 }
441                                 command_finished=TRUE;
442                         }
443                         version = atoi(Cmd_Argv(1));
444                         qport = atoi(Cmd_Argv(2));
445                         challenge = atoi(Cmd_Argv(3));
446                         infostring = Cmd_Argv(4);
447                         if (argument_tree) {
448                                 proto_tree_add_uint(argument_tree,
449                                         hf_quakeworld_connectionless_connect_version,
450                                         tvb,
451                                         offset + Cmd_Argv_start(1),
452                                         Cmd_Argv_length(1), version);
453                                 proto_tree_add_uint(argument_tree,
454                                         hf_quakeworld_connectionless_connect_qport,
455                                         tvb,
456                                         offset + Cmd_Argv_start(2),
457                                         Cmd_Argv_length(2), qport);
458                                 proto_tree_add_int(argument_tree,
459                                         hf_quakeworld_connectionless_connect_challenge,
460                                         tvb,
461                                         offset + Cmd_Argv_start(3),
462                                         Cmd_Argv_length(3), challenge);
463                                 info_item = proto_tree_add_string(argument_tree,
464                                         hf_quakeworld_connectionless_connect_infostring,
465                                         tvb,
466                                         offset + Cmd_Argv_start(4),
467                                         Cmd_Argv_length(4), infostring);
468                                 if (info_item)
469                                         info_tree = proto_item_add_subtree(
470                                                 info_item, ett_quakeworld_connectionless_connect_infostring);
471                                 dissect_id_infostring(tvb, info_tree, offset + Cmd_Argv_start(4),
472                                         ep_strdup(infostring),
473                                         ett_quakeworld_connectionless_connect_infostring_key_value,
474                                         hf_quakeworld_connectionless_connect_infostring_key_value,
475                                         hf_quakeworld_connectionless_connect_infostring_key,
476                                         hf_quakeworld_connectionless_connect_infostring_value);
477                         }
478                 } else if (strcmp(c,"getchallenge") == 0) {
479                         command="Get Challenge";
480                         command_len = Cmd_Argv_length(0);
481                 } else if (strcmp(c,"rcon") == 0) {
482                         const char* password;
483                         int i;
484                         char remaining[MAX_TEXT_SIZE+1];
485                         proto_item *argument_item = NULL;
486                         proto_tree *argument_tree = NULL;
487                         command="Remote Command";
488                         command_len = Cmd_Argv_length(0);
489                         if (text_tree) {
490                                 proto_tree_add_string(text_tree, hf_quakeworld_connectionless_command,
491                                         tvb, offset, command_len, command);
492                                 argument_item = proto_tree_add_string(text_tree,
493                                         hf_quakeworld_connectionless_arguments,
494                                         tvb, offset + Cmd_Argv_start(1), len + 1 - Cmd_Argv_start(1),
495                                         text + Cmd_Argv_start(1));
496                                 if (argument_item) {
497                                         argument_tree =
498                                                 proto_item_add_subtree(argument_item,
499                                                         ett_quakeworld_connectionless_arguments);
500                                 }
501                                 command_finished=TRUE;
502                         }
503                         password = Cmd_Argv(1);
504                         if (argument_tree) {
505                                 proto_tree_add_string(argument_tree,
506                                         hf_quakeworld_connectionless_rcon_password,
507                                         tvb,
508                                         offset + Cmd_Argv_start(1),
509                                         Cmd_Argv_length(1), password);
510                         }
511                         remaining[0] = 0;
512                         for (i=2; i<Cmd_Argc() ; i++) {
513                                 g_strlcat (remaining, Cmd_Argv(i), MAX_TEXT_SIZE+1);
514                                 g_strlcat (remaining, " ", MAX_TEXT_SIZE+1);
515                         }
516                         if (text_tree) {
517                                 proto_tree_add_string(argument_tree,
518                                         hf_quakeworld_connectionless_rcon_command,
519                                         tvb, offset + Cmd_Argv_start(2),
520                                         Cmd_Argv_start(Cmd_Argc()-1) + Cmd_Argv_length(Cmd_Argc()-1) -
521                                         Cmd_Argv_start(2),
522                                         remaining);
523                         }
524                 } else if (c[0]==A2A_PING && ( c[1]==0 || c[1]=='\n')) {
525                         command="Ping";
526                         command_len = 1;
527                 } else if (c[0]==A2A_ACK && ( c[1]==0 || c[1]=='\n')) {
528                         command="Ack";
529                         command_len = 1;
530                 } else {
531                         command="Unknown";
532                         command_len = len;
533                 }
534         }
535         else {
536                 /* server to client commands */
537                 if (text[0] == S2C_CONNECTION) {
538                         command="Connected";
539                         command_len = 1;
540                 } else if (text[0] == A2C_CLIENT_COMMAND) {
541                         command="Client Command";
542                         command_len = 1;
543                         /* stringz (command), stringz (localid) */
544                 } else if (text[0] == A2C_PRINT) {
545                         command="Print";
546                         command_len = 1;
547                         /* string */
548                 } else if (text[0] == A2A_PING) {
549                         command="Ping";
550                         command_len = 1;
551                 } else if (text[0] == S2C_CHALLENGE) {
552                         command="Challenge";
553                         command_len = 1;
554                         /* string, atoi */
555                 } else {
556                         command="Unknown";
557                         command_len = len;
558                 }
559         }
560
561         if (check_col(pinfo->cinfo, COL_INFO)) {
562                 col_append_fstr(pinfo->cinfo, COL_INFO, " %s", command);
563         }
564
565         if (text_tree && !command_finished) {
566                 proto_tree_add_string(text_tree, hf_quakeworld_connectionless_command,
567                         tvb, offset, command_len, command);
568         }
569         offset += len + 1;
570 }
571
572
573 static void
574 dissect_quakeworld_client_commands(tvbuff_t *tvb, packet_info *pinfo,
575         proto_tree *tree)
576 {
577         /* If I have too much time at hand, I'll fill it with all
578            the information from my QWD specs:
579                 http://www.planetquake.com/demospecs/qwd/
580         */
581         call_dissector(data_handle,tvb, pinfo, tree);
582 }
583
584
585 static void
586 dissect_quakeworld_server_commands(tvbuff_t *tvb, packet_info *pinfo,
587         proto_tree *tree)
588 {
589         /* If I have too much time at hand, I'll fill it with all
590            the information from my QWD specs:
591                 http://www.planetquake.com/demospecs/qwd/
592         */
593         call_dissector(data_handle,tvb, pinfo, tree);
594 }
595
596
597 static const value_string names_reliable[] = {
598         { 0, "Non Reliable" },
599         { 1, "Reliable" },
600         { 0, NULL }
601 };
602
603
604 static void
605 dissect_quakeworld_GamePacket(tvbuff_t *tvb, packet_info *pinfo,
606         proto_tree *tree, int direction)
607 {
608         proto_tree      *game_tree = NULL;
609         proto_item      *game_item = NULL;
610         guint32 seq1;
611         guint32 seq2;
612         int rel1;
613         int rel2;
614         int offset;
615         guint           rest_length;
616
617         direction = (pinfo->destport == gbl_quakeworldServerPort) ?
618                         DIR_C2S : DIR_S2C;
619
620         if (tree) {
621                 game_item = proto_tree_add_text(tree, tvb,
622                                 0, -1, "Game");
623                 if (game_item)
624                         game_tree = proto_item_add_subtree(
625                                 game_item, ett_quakeworld_game);
626         }
627
628         offset = 0;
629
630         seq1 = tvb_get_letohl(tvb, offset);
631         rel1 = seq1 & 0x80000000 ? 1 : 0;
632         seq1 &= ~0x80000000;
633         if (game_tree) {
634                 proto_item *seq1_item = proto_tree_add_text(game_tree,
635                         tvb, offset, 4, "Current Sequence: %u (%s)",
636                         seq1, val_to_str(rel1,names_reliable,"%u"));
637                 if (seq1_item) {
638                         proto_tree *seq1_tree = proto_item_add_subtree(
639                                 seq1_item, ett_quakeworld_game_seq1);
640                         proto_tree_add_uint(seq1_tree, hf_quakeworld_game_seq1,
641                                         tvb, offset, 4, seq1);
642                         proto_tree_add_boolean(seq1_tree, hf_quakeworld_game_rel1,
643                                         tvb, offset+3, 1, rel1);
644                 }
645         }
646         offset += 4;
647
648         seq2 = tvb_get_letohl(tvb, offset);
649         rel2 = seq2 & 0x80000000 ? 1 : 0;
650         seq2 &= ~0x80000000;
651         if (game_tree) {
652                 proto_item *seq2_item = proto_tree_add_text(game_tree,
653                         tvb, offset, 4, "Acknowledge Sequence: %u (%s)",
654                         seq2, val_to_str(rel2,names_reliable,"%u"));
655                 if (seq2_item) {
656                         proto_tree *seq2_tree = proto_item_add_subtree(
657                                 seq2_item, ett_quakeworld_game_seq2);
658                         proto_tree_add_uint(seq2_tree, hf_quakeworld_game_seq2,
659                                         tvb, offset, 4, seq2);
660                         proto_tree_add_boolean(seq2_tree, hf_quakeworld_game_rel2,
661                                         tvb, offset+3, 1, rel2);
662                 }
663         }
664         offset += 4;
665
666         if (direction == DIR_C2S) {
667                 /* client to server */
668                 guint16 qport = tvb_get_letohs(tvb, offset);
669                 if (game_tree) {
670                         proto_tree_add_uint(game_tree, hf_quakeworld_game_qport,
671                                 tvb, offset, 2, qport);
672                 }
673                 offset +=2;
674         }
675
676         /* all the rest is pure game data */
677         rest_length = tvb_reported_length(tvb) - offset;
678         if (rest_length) {
679                 tvbuff_t *next_tvb =
680                 tvb_new_subset(tvb, offset, rest_length , rest_length);
681
682                 if (direction == DIR_C2S) {
683                         proto_item *c_item = NULL;
684                         proto_tree *c_tree = NULL;
685                         if (tree) {
686                                 c_item = proto_tree_add_text(game_tree, next_tvb,
687                                 0, -1, "Client Commands");
688                                 if (c_item) {
689                                         c_tree = proto_item_add_subtree(
690                                                 c_item, ett_quakeworld_game_clc);
691                                 }
692                         }
693                         dissect_quakeworld_client_commands(next_tvb, pinfo, c_tree);
694                 }
695                 else {
696                         proto_item *c_item = NULL;
697                         proto_tree *c_tree = NULL;
698                         if (tree) {
699                                 c_item = proto_tree_add_text(game_tree, next_tvb,
700                                 0, -1, "Server Commands");
701                                 if (c_item) {
702                                         c_tree = proto_item_add_subtree(
703                                         c_item, ett_quakeworld_game_svc);
704                                 }
705                         }
706                         dissect_quakeworld_server_commands(next_tvb, pinfo, c_tree);
707                 }
708         }
709 }
710
711
712 static void
713 dissect_quakeworld(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
714 {
715         proto_tree      *quakeworld_tree = NULL;
716         proto_item      *quakeworld_item = NULL;
717         int             direction;
718
719         direction = (pinfo->destport == gbl_quakeworldServerPort) ?
720                         DIR_C2S : DIR_S2C;
721
722         col_set_str(pinfo->cinfo, COL_PROTOCOL, "QUAKEWORLD");
723         if (check_col(pinfo->cinfo, COL_INFO))
724                 col_add_str(pinfo->cinfo, COL_INFO, val_to_str(direction,
725                         names_direction, "%u"));
726
727         if (tree) {
728                 quakeworld_item = proto_tree_add_item(tree, proto_quakeworld,
729                                 tvb, 0, -1, FALSE);
730                 if (quakeworld_item)
731                         quakeworld_tree = proto_item_add_subtree(
732                                 quakeworld_item, ett_quakeworld);
733                         if (quakeworld_tree) {
734                                 proto_tree_add_uint_format(quakeworld_tree,
735                                         direction == DIR_S2C ?
736                                         hf_quakeworld_s2c :
737                                         hf_quakeworld_c2s,
738                                         tvb, 0, 0, 1,
739                                         "Direction: %s", val_to_str(direction, names_direction, "%u"));
740                         }
741         }
742
743         if (tvb_get_ntohl(tvb, 0) == 0xffffffff) {
744                 if (check_col(pinfo->cinfo, COL_INFO)) {
745                         col_append_str(pinfo->cinfo, COL_INFO, " Connectionless");
746                 }
747                 if (quakeworld_tree)
748                         proto_tree_add_uint_format(quakeworld_tree,
749                                 hf_quakeworld_connectionless,
750                                 tvb, 0, 0, 1,
751                                 "Type: Connectionless");
752                 dissect_quakeworld_ConnectionlessPacket(
753                         tvb, pinfo, quakeworld_tree, direction);
754         }
755         else {
756                 if (check_col(pinfo->cinfo, COL_INFO)) {
757                         col_append_str(pinfo->cinfo, COL_INFO, " Game");
758                 }
759                 if (quakeworld_tree)
760                         proto_tree_add_uint_format(quakeworld_tree,
761                                 hf_quakeworld_game,
762                                 tvb, 0, 0, 1,
763                                 "Type: Game");
764                 dissect_quakeworld_GamePacket(
765                         tvb, pinfo, quakeworld_tree, direction);
766         }
767 }
768
769
770 void
771 proto_reg_handoff_quakeworld(void)
772 {
773         static gboolean Initialized=FALSE;
774         static dissector_handle_t quakeworld_handle;
775         static guint ServerPort;
776
777         if (!Initialized) {
778                 quakeworld_handle = create_dissector_handle(dissect_quakeworld,
779                                 proto_quakeworld);
780                 data_handle = find_dissector("data");
781                 Initialized=TRUE;
782         } else {
783                 dissector_delete("udp.port", ServerPort, quakeworld_handle);
784         }
785
786         /* set port for future deletes */
787         ServerPort=gbl_quakeworldServerPort;
788
789         dissector_add("udp.port", gbl_quakeworldServerPort, quakeworld_handle);
790 }
791
792
793 void
794 proto_register_quakeworld(void)
795 {
796         static hf_register_info hf[] = {
797                 { &hf_quakeworld_c2s,
798                         { "Client to Server", "quakeworld.c2s",
799                         FT_UINT32, BASE_DEC, NULL, 0x0,
800                         NULL, HFILL }},
801                 { &hf_quakeworld_s2c,
802                         { "Server to Client", "quakeworld.s2c",
803                         FT_UINT32, BASE_DEC, NULL, 0x0,
804                         NULL, HFILL }},
805                 { &hf_quakeworld_connectionless,
806                         { "Connectionless", "quakeworld.connectionless",
807                         FT_UINT32, BASE_DEC, NULL, 0x0,
808                         NULL, HFILL }},
809                 { &hf_quakeworld_game,
810                         { "Game", "quakeworld.game",
811                         FT_UINT32, BASE_DEC, NULL, 0x0,
812                         NULL, HFILL }},
813                 { &hf_quakeworld_connectionless_marker,
814                         { "Marker", "quakeworld.connectionless.marker",
815                         FT_UINT32, BASE_HEX, NULL, 0x0,
816                         NULL, HFILL }},
817                 { &hf_quakeworld_connectionless_text,
818                         { "Text", "quakeworld.connectionless.text",
819                         FT_STRING, BASE_NONE, NULL, 0x0,
820                         NULL, HFILL }},
821                 { &hf_quakeworld_connectionless_command,
822                         { "Command", "quakeworld.connectionless.command",
823                         FT_STRING, BASE_NONE, NULL, 0x0,
824                         NULL, HFILL }},
825                 { &hf_quakeworld_connectionless_arguments,
826                         { "Arguments", "quakeworld.connectionless.arguments",
827                         FT_STRING, BASE_NONE, NULL, 0x0,
828                         NULL, HFILL }},
829                 { &hf_quakeworld_connectionless_connect_version,
830                         { "Version", "quakeworld.connectionless.connect.version",
831                         FT_UINT32, BASE_DEC, NULL, 0x0,
832                         "Protocol Version", HFILL }},
833                 { &hf_quakeworld_connectionless_connect_qport,
834                         { "QPort", "quakeworld.connectionless.connect.qport",
835                         FT_UINT32, BASE_DEC, NULL, 0x0,
836                         "QPort of the client", HFILL }},
837                 { &hf_quakeworld_connectionless_connect_challenge,
838                         { "Challenge", "quakeworld.connectionless.connect.challenge",
839                         FT_INT32, BASE_DEC, NULL, 0x0,
840                         "Challenge from the server", HFILL }},
841                 { &hf_quakeworld_connectionless_connect_infostring,
842                         { "Infostring", "quakeworld.connectionless.connect.infostring",
843                         FT_STRING, BASE_NONE, NULL, 0x0,
844                         "Infostring with additional variables", HFILL }},
845                 { &hf_quakeworld_connectionless_connect_infostring_key_value,
846                         { "Key/Value", "quakeworld.connectionless.connect.infostring.key_value",
847                         FT_STRING, BASE_NONE, NULL, 0x0,
848                         "Key and Value", HFILL }},
849                 { &hf_quakeworld_connectionless_connect_infostring_key,
850                         { "Key", "quakeworld.connectionless.connect.infostring.key",
851                         FT_STRING, BASE_NONE, NULL, 0x0,
852                         "Infostring Key", HFILL }},
853                 { &hf_quakeworld_connectionless_connect_infostring_value,
854                         { "Value", "quakeworld.connectionless.connect.infostring.value",
855                         FT_STRING, BASE_NONE, NULL, 0x0,
856                         "Infostring Value", HFILL }},
857                 { &hf_quakeworld_connectionless_rcon_password,
858                         { "Password", "quakeworld.connectionless.rcon.password",
859                         FT_STRING, BASE_NONE, NULL, 0x0,
860                         "Rcon Password", HFILL }},
861                 { &hf_quakeworld_connectionless_rcon_command,
862                         { "Command", "quakeworld.connectionless.rcon.command",
863                         FT_STRING, BASE_NONE, NULL, 0x0,
864                         NULL, HFILL }},
865                 { &hf_quakeworld_game_seq1,
866                         { "Sequence Number", "quakeworld.game.seq1",
867                         FT_UINT32, BASE_DEC, NULL, 0x0,
868                         "Sequence number of the current packet", HFILL }},
869                 { &hf_quakeworld_game_rel1,
870                         { "Reliable", "quakeworld.game.rel1",
871                         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
872                         "Packet is reliable and may be retransmitted", HFILL }},
873                 { &hf_quakeworld_game_seq2,
874                         { "Sequence Number", "quakeworld.game.seq2",
875                         FT_UINT32, BASE_DEC, NULL, 0x0,
876                         "Sequence number of the last received packet", HFILL }},
877                 { &hf_quakeworld_game_rel2,
878                         { "Reliable", "quakeworld.game.rel2",
879                         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
880                         "Packet was reliable and may be retransmitted", HFILL }},
881                 { &hf_quakeworld_game_qport,
882                         { "QPort", "quakeworld.game.qport",
883                         FT_UINT32, BASE_DEC, NULL, 0x0,
884                         "QuakeWorld Client Port", HFILL }}
885         };
886         static gint *ett[] = {
887                 &ett_quakeworld,
888                 &ett_quakeworld_connectionless,
889                 &ett_quakeworld_connectionless_text,
890                 &ett_quakeworld_connectionless_arguments,
891                 &ett_quakeworld_connectionless_connect_infostring,
892                 &ett_quakeworld_connectionless_connect_infostring_key_value,
893                 &ett_quakeworld_game,
894                 &ett_quakeworld_game_seq1,
895                 &ett_quakeworld_game_seq2,
896                 &ett_quakeworld_game_clc,
897                 &ett_quakeworld_game_svc
898         };
899         module_t *quakeworld_module;
900
901         proto_quakeworld = proto_register_protocol("QuakeWorld Network Protocol",
902                                                 "QUAKEWORLD", "quakeworld");
903         proto_register_field_array(proto_quakeworld, hf, array_length(hf));
904         proto_register_subtree_array(ett, array_length(ett));
905
906         /* Register a configuration option for port */
907         quakeworld_module = prefs_register_protocol(proto_quakeworld,
908                 proto_reg_handoff_quakeworld);
909         prefs_register_uint_preference(quakeworld_module, "udp.port",
910                                         "QuakeWorld Server UDP Port",
911                                         "Set the UDP port for the QuakeWorld Server",
912                                         10, &gbl_quakeworldServerPort);
913 }
914