A correct programming practice is to save errno and restore its value
[obnox/wireshark/wip.git] / 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: packet-quakeworld.c,v 1.16 2002/08/28 21:00:28 jmayer Exp $
8  *
9  * Ethereal - Network traffic analyzer
10  * By Gerald Combs <gerald@ethereal.com>
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 "prefs.h"
38
39 static int proto_quakeworld = -1;
40
41 static int hf_quakeworld_s2c = -1;
42 static int hf_quakeworld_c2s = -1;
43 static int hf_quakeworld_connectionless = -1;
44 static int hf_quakeworld_game = -1;
45 static int hf_quakeworld_connectionless_marker = -1;
46 static int hf_quakeworld_connectionless_text = -1;
47 static int hf_quakeworld_connectionless_command = -1;
48 static int hf_quakeworld_connectionless_arguments = -1;
49 static int hf_quakeworld_connectionless_connect_version = -1;
50 static int hf_quakeworld_connectionless_connect_qport = -1;
51 static int hf_quakeworld_connectionless_connect_challenge = -1;
52 static int hf_quakeworld_connectionless_connect_infostring = -1;
53 static int hf_quakeworld_connectionless_connect_infostring_key_value = -1;
54 static int hf_quakeworld_connectionless_connect_infostring_key = -1;
55 static int hf_quakeworld_connectionless_connect_infostring_value = -1;
56 static int hf_quakeworld_connectionless_rcon_password = -1;
57 static int hf_quakeworld_connectionless_rcon_command = -1;
58 static int hf_quakeworld_game_seq1 = -1;
59 static int hf_quakeworld_game_rel1 = -1;
60 static int hf_quakeworld_game_seq2 = -1;
61 static int hf_quakeworld_game_rel2 = -1;
62 static int hf_quakeworld_game_qport = -1;
63
64 static gint ett_quakeworld = -1;
65 static gint ett_quakeworld_connectionless = -1;
66 static gint ett_quakeworld_connectionless_text = -1;
67 static gint ett_quakeworld_connectionless_arguments = -1;
68 static gint ett_quakeworld_connectionless_connect_infostring = -1;
69 static gint ett_quakeworld_connectionless_connect_infostring_key_value = -1;
70 static gint ett_quakeworld_game = -1;
71 static gint ett_quakeworld_game_seq1 = -1;
72 static gint ett_quakeworld_game_seq2 = -1;
73 static gint ett_quakeworld_game_clc = -1;
74 static gint ett_quakeworld_game_svc = -1;
75
76 static dissector_handle_t data_handle;
77
78 /*
79    helper functions, they may ave to go somewhere else
80    they are mostly copied without change from
81         quakeworldsource/client/cmd.c
82         quakeworldsource/client/common.c
83 */
84
85 #define MAX_TEXT_SIZE   2048
86
87 static  char            com_token[MAX_TEXT_SIZE+1];
88 static  int             com_token_start;
89 static  int             com_token_length;
90
91 static char *
92 COM_Parse (char *data)
93 {
94         int     c;
95         int     len;
96
97         len = 0;
98         com_token[0] = 0;
99         com_token_start = 0;
100         com_token_length = 0;
101
102         if (data == NULL)
103                 return NULL;
104
105         /* skip whitespace */
106 skipwhite:
107         while ( (c = *data) <= ' ') {
108                 if (c == 0)
109                         return NULL;    /* end of file; */
110                 data++;
111                 com_token_start++;
112         }
113
114         /* skip // comments */
115         if (c=='/' && data[1] == '/') {
116                 while (*data && *data != '\n')
117                         data++;
118                         com_token_start++;
119                 goto skipwhite;
120         }
121
122         /* handle quoted strings specially */
123         if (c == '\"') {
124                 data++;
125                 com_token_start++;
126                 while (1) {
127                         c = *data++;
128                         if (c=='\"' || c==0) {
129                                 com_token[len] = 0;
130                                 return data;
131                         }
132                         com_token[len] = c;
133                         len++;
134                         com_token_length++;
135                 }
136         }
137
138         /* parse a regular word */
139         do {
140                 com_token[len] = c;
141                 data++;
142                 len++;
143                 com_token_length++;
144                 c = *data;
145         } while (c>32);
146
147         com_token[len] = 0;
148         return data;
149 }
150
151
152 #define                 MAX_ARGS 80
153 static  int             cmd_argc = 0;
154 static  char            *cmd_argv[MAX_ARGS];
155 static  char            *cmd_null_string = "";
156 static  int             cmd_argv_start[MAX_ARGS];
157 static  int             cmd_argv_length[MAX_ARGS];
158
159
160
161 static int
162 Cmd_Argc(void)
163 {
164         return cmd_argc;
165 }
166
167
168 static char*
169 Cmd_Argv(int arg)
170 {
171         if ( arg >= cmd_argc )
172                 return cmd_null_string;
173         return cmd_argv[arg];
174 }
175
176
177 static int
178 Cmd_Argv_start(int arg)
179 {
180         if ( arg >= cmd_argc )
181                 return 0;
182         return cmd_argv_start[arg];
183 }
184
185
186 static int
187 Cmd_Argv_length(int arg)
188 {
189         if ( arg >= cmd_argc )
190                 return 0;
191         return cmd_argv_length[arg];
192 }
193
194
195 static void
196 Cmd_TokenizeString(char* text)
197 {
198         int i;
199         int start;
200         int length;
201
202
203         /* clear the args from the last string */
204         for (i=0 ; i<cmd_argc ; i++)
205                 g_free(cmd_argv[i]);
206
207         cmd_argc = 0;
208
209         start = 0;
210         while (TRUE) {
211
212                 /* skip whitespace up to a \n */
213                 while (*text && *text <= ' ' && *text != '\n') {
214                         text++;
215                         start++;
216                 }
217
218                 length = 0;
219
220                 if (*text == '\n') {
221                         /* a newline seperates commands in the buffer */
222                         text++;
223                         break;
224                 }
225
226                 if (!*text)
227                         return;
228
229                 text = COM_Parse (text);
230                 if (!text)
231                         return;
232
233                 if (cmd_argc < MAX_ARGS) {
234                         cmd_argv[cmd_argc] = g_strdup(com_token);
235                         cmd_argv_start[cmd_argc] = start + com_token_start;
236                         cmd_argv_length[cmd_argc] = com_token_length;
237                         cmd_argc++;
238                 }
239
240                 start += com_token_start + com_token_length;
241         }
242 }
243
244
245 static void
246 dissect_id_infostring(tvbuff_t *tvb, proto_tree* tree,
247         int offset, char* infostring,
248         gint ett_key_value, int hf_key_value, int hf_key, int hf_value)
249 {
250         char * newpos = infostring;
251         int end_of_info = FALSE;
252
253         /* to look at all the key/value pairs, we destroy infostring */
254         while(!end_of_info) {
255                 char* keypos;
256                 char* valuepos;
257                 int keylength;
258                 char* keyvaluesep;
259                 int valuelength;
260                 char* valueend;
261
262                 keypos = newpos;
263                 if (*keypos == 0) break;
264                 if (*keypos == '\\') keypos++;
265
266                 for (keylength = 0
267                         ;
268                         *(keypos + keylength) != '\\' &&
269                         *(keypos + keylength) != 0
270                         ;
271                         keylength++) ;
272                 keyvaluesep = keypos + keylength;
273                 if (*keyvaluesep == 0) break;
274                 valuepos = keyvaluesep+1;
275                 for (valuelength = 0
276                         ;
277                         *(valuepos + valuelength) != '\\' &&
278                         *(valuepos + valuelength) != 0
279                         ;
280                         valuelength++) ;
281                 valueend = valuepos + valuelength;
282                 if (*valueend == 0) {
283                         end_of_info = TRUE;
284                 }
285                 *(keyvaluesep) = '=';
286                 *(valueend) = 0;
287
288                 if (tree) {
289                         proto_item* sub_item = NULL;
290                         proto_tree* sub_tree = NULL;
291
292                         sub_item = proto_tree_add_string(tree,
293                                 hf_key_value,
294                                 tvb,
295                                 offset + (keypos-infostring),
296                                 keylength + 1 + valuelength, keypos);
297                         if (sub_item)
298                                 sub_tree =
299                                         proto_item_add_subtree(
300                                         sub_item,
301                                         ett_key_value);
302                         *(keyvaluesep) = 0;
303                         if (sub_tree) {
304                                 proto_tree_add_string(sub_tree,
305                                         hf_key,
306                                         tvb,
307                                         offset + (keypos-infostring),
308                                         keylength, keypos);
309                                 proto_tree_add_string(sub_tree,
310                                         hf_value,
311                                         tvb,
312                                         offset + (valuepos-infostring),
313                                         valuelength, valuepos);
314                         }
315                 }
316                 newpos = valueend + 1;
317         }
318 }
319
320
321 static const value_string names_direction[] = {
322 #define DIR_C2S 0
323         { DIR_C2S, "Client to Server" },
324 #define DIR_S2C 1
325         { DIR_S2C, "Server to Client" },
326         { 0, NULL }
327 };
328
329
330 /* I took this name and value directly out of the QW source. */
331 #define PORT_MASTER 27500
332 static unsigned int gbl_quakeworldServerPort=PORT_MASTER;
333
334
335 /* out of band message id bytes (taken out of quakeworldsource/client/protocol.h */
336
337 /* M = master, S = server, C = client, A = any */
338 /* the second character will allways be \n if the message isn't a single */
339 /* byte long (?? not true anymore?) */
340
341 #define S2C_CHALLENGE           'c'
342 #define S2C_CONNECTION          'j'
343 #define A2A_PING                'k'     /* respond with an A2A_ACK */
344 #define A2A_ACK                 'l'     /* general acknowledgement without info */
345 #define A2A_NACK                'm'     /* [+ comment] general failure */
346 #define A2A_ECHO                'e'     /* for echoing */
347 #define A2C_PRINT               'n'     /* print a message on client */
348
349 #define S2M_HEARTBEAT           'a'     /* + serverinfo + userlist + fraglist */
350 #define A2C_CLIENT_COMMAND      'B'     /* + command line */
351 #define S2M_SHUTDOWN            'C'
352
353
354 static void
355 dissect_quakeworld_ConnectionlessPacket(tvbuff_t *tvb, packet_info *pinfo,
356         proto_tree *tree, int direction)
357 {
358         proto_tree      *cl_tree = NULL;
359         proto_item      *cl_item = NULL;
360         proto_item      *text_item = NULL;
361         proto_tree      *text_tree = NULL;
362         guint8          text[MAX_TEXT_SIZE+1];
363         int             maxbufsize = 0;
364         int             len;
365         int             offset;
366         guint32 marker;
367         int command_len;
368         char command[MAX_TEXT_SIZE+1];
369         int command_finished = FALSE;
370
371         marker = tvb_get_ntohl(tvb, 0);
372         if (tree) {
373                 cl_item = proto_tree_add_text(tree, tvb,
374                                 0, -1, "Connectionless");
375                 if (cl_item)
376                         cl_tree = proto_item_add_subtree(
377                                 cl_item, ett_quakeworld_connectionless);
378         }
379
380         if (cl_tree) {
381                 proto_tree_add_uint(cl_tree, hf_quakeworld_connectionless_marker,
382                                 tvb, 0, 4, marker);
383         }
384
385         /* all the rest of the packet is just text */
386         offset = 4;
387
388         maxbufsize = MIN((gint)MAX_TEXT_SIZE, tvb_length_remaining(tvb, offset));
389         len = tvb_get_nstringz0(tvb, offset, maxbufsize, 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 sever commands */
403                 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                         strcpy(command, "Ping");
411                         command_len = 4;
412                 } else if (strcmp(c,"status") == 0) {
413                         strcpy(command, "Status");
414                         command_len = 6;
415                 } else if (strcmp(c,"log") == 0) {
416                         strcpy(command, "Status");
417                         command_len = 3;
418                 } else if (strcmp(c,"connect") == 0) {
419                         int version;
420                         int qport;
421                         int challenge;
422                         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                         strcpy(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                                         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                         strcpy(command, "Get Challenge");
480                         command_len = Cmd_Argv_length(0);
481                 } else if (strcmp(c,"rcon") == 0) {
482                         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                         strcpy(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                         for (i=2; i<Cmd_Argc() ; i++) {
512                                 remaining[0] = 0;
513                                 strcat (remaining, Cmd_Argv(i) );
514                                 strcat (remaining, " ");
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                         strcpy(command, "Ping");
526                         command_len = 1;
527                 } else if (c[0]==A2A_ACK && ( c[1]==0 || c[1]=='\n')) {
528                         strcpy(command, "Ack");
529                         command_len = 1;
530                 } else {
531                         strcpy(command, "Unknown");
532                         command_len = len;
533                 }
534         }
535         else {
536                 /* server to client commands */
537                 if (text[0] == S2C_CONNECTION) {
538                         strcpy(command, "Connected");
539                         command_len = 1;
540                 } else if (text[0] == A2C_CLIENT_COMMAND) {
541                         strcpy(command, "Client Command");
542                         command_len = 1;
543                         /* stringz (command), stringz (localid) */
544                 } else if (text[0] == A2C_PRINT) {
545                         strcpy(command, "Print");
546                         command_len = 1;
547                         /* string */
548                 } else if (text[0] == A2A_PING) {
549                         strcpy(command, "Ping");
550                         command_len = 1;
551                 } else if (text[0] == S2C_CHALLENGE) {
552                         strcpy(command, "Challenge");
553                         command_len = 1;
554                         /* string, atoi */
555                 } else {
556                         strcpy(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         if (check_col(pinfo->cinfo, COL_PROTOCOL))
723                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "QUAKEWORLD");
724         if (check_col(pinfo->cinfo, COL_INFO))
725                 col_set_str(pinfo->cinfo, COL_INFO, val_to_str(direction,
726                         names_direction, "%u"));
727
728         if (tree) {
729                 quakeworld_item = proto_tree_add_item(tree, proto_quakeworld,
730                                 tvb, 0, -1, FALSE);
731                 if (quakeworld_item)
732                         quakeworld_tree = proto_item_add_subtree(
733                                 quakeworld_item, ett_quakeworld);
734                         if (quakeworld_tree) {
735                                 proto_tree_add_uint_format(quakeworld_tree,
736                                         direction == DIR_S2C ?
737                                         hf_quakeworld_s2c :
738                                         hf_quakeworld_c2s,
739                                         tvb, 0, 0, 1,
740                                         "Direction: %s", val_to_str(direction, names_direction, "%u"));
741                         }
742         }
743
744         if (tvb_get_ntohl(tvb, 0) == 0xffffffff) {
745                 if (check_col(pinfo->cinfo, COL_INFO)) {
746                         col_append_str(pinfo->cinfo, COL_INFO, " Connectionless");
747                 }
748                 if (quakeworld_tree)
749                         proto_tree_add_uint_format(quakeworld_tree,
750                                 hf_quakeworld_connectionless,
751                                 tvb, 0, 0, 1,
752                                 "Type: Connectionless");
753                 dissect_quakeworld_ConnectionlessPacket(
754                         tvb, pinfo, quakeworld_tree, direction);
755         }
756         else {
757                 if (check_col(pinfo->cinfo, COL_INFO)) {
758                         col_append_str(pinfo->cinfo, COL_INFO, " Game");
759                 }
760                 if (quakeworld_tree)
761                         proto_tree_add_uint_format(quakeworld_tree,
762                                 hf_quakeworld_game,
763                                 tvb, 0, 0, 1,
764                                 "Type: Game");
765                 dissect_quakeworld_GamePacket(
766                         tvb, pinfo, quakeworld_tree, direction);
767         }
768 }
769
770
771 void
772 proto_reg_handoff_quakeworld(void)
773 {
774         static int Initialized=FALSE;
775         static dissector_handle_t quakeworld_handle;
776         static int ServerPort=0;
777
778         if (!Initialized) {
779                 quakeworld_handle = create_dissector_handle(dissect_quakeworld,
780                                 proto_quakeworld);
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         data_handle = find_dissector("data");
791 }
792
793
794 void
795 proto_register_quakeworld(void)
796 {
797         static hf_register_info hf[] = {
798                 { &hf_quakeworld_c2s,
799                         { "Client to Server", "quakeworld.c2s",
800                         FT_UINT32, BASE_DEC, NULL, 0x0,
801                         "Client to Server", HFILL }},
802                 { &hf_quakeworld_s2c,
803                         { "Server to Client", "quakeworld.s2c",
804                         FT_UINT32, BASE_DEC, NULL, 0x0,
805                         "Server to Client", HFILL }},
806                 { &hf_quakeworld_connectionless,
807                         { "Connectionless", "quakeworld.connectionless",
808                         FT_UINT32, BASE_DEC, NULL, 0x0,
809                         "Connectionless", HFILL }},
810                 { &hf_quakeworld_game,
811                         { "Game", "quakeworld.game",
812                         FT_UINT32, BASE_DEC, NULL, 0x0,
813                         "Game", HFILL }},
814                 { &hf_quakeworld_connectionless_marker,
815                         { "Marker", "quakeworld.connectionless.marker",
816                         FT_UINT32, BASE_HEX, NULL, 0x0,
817                         "Marker", HFILL }},
818                 { &hf_quakeworld_connectionless_text,
819                         { "Text", "quakeworld.connectionless.text",
820                         FT_STRING, BASE_DEC, NULL, 0x0,
821                         "Text", HFILL }},
822                 { &hf_quakeworld_connectionless_command,
823                         { "Command", "quakeworld.connectionless.command",
824                         FT_STRING, BASE_DEC, NULL, 0x0,
825                         "Command", HFILL }},
826                 { &hf_quakeworld_connectionless_arguments,
827                         { "Arguments", "quakeworld.connectionless.arguments",
828                         FT_STRING, BASE_DEC, NULL, 0x0,
829                         "Arguments", HFILL }},
830                 { &hf_quakeworld_connectionless_connect_version,
831                         { "Version", "quakeworld.connectionless.connect.version",
832                         FT_UINT32, BASE_DEC, NULL, 0x0,
833                         "Protocol Version", HFILL }},
834                 { &hf_quakeworld_connectionless_connect_qport,
835                         { "QPort", "quakeworld.connectionless.connect.qport",
836                         FT_UINT32, BASE_DEC, NULL, 0x0,
837                         "QPort of the client", HFILL }},
838                 { &hf_quakeworld_connectionless_connect_challenge,
839                         { "Challenge", "quakeworld.connectionless.connect.challenge",
840                         FT_INT32, BASE_DEC, NULL, 0x0,
841                         "Challenge from the server", HFILL }},
842                 { &hf_quakeworld_connectionless_connect_infostring,
843                         { "Infostring", "quakeworld.connectionless.connect.infostring",
844                         FT_STRING, BASE_DEC, NULL, 0x0,
845                         "Infostring with additional variables", HFILL }},
846                 { &hf_quakeworld_connectionless_connect_infostring_key_value,
847                         { "Key/Value", "quakeworld.connectionless.connect.infostring.key_value",
848                         FT_STRING, BASE_DEC, NULL, 0x0,
849                         "Key and Value", HFILL }},
850                 { &hf_quakeworld_connectionless_connect_infostring_key,
851                         { "Key", "quakeworld.connectionless.connect.infostring.key",
852                         FT_STRING, BASE_DEC, NULL, 0x0,
853                         "Infostring Key", HFILL }},
854                 { &hf_quakeworld_connectionless_connect_infostring_value,
855                         { "Value", "quakeworld.connectionless.connect.infostring.value",
856                         FT_STRING, BASE_DEC, NULL, 0x0,
857                         "Infostring Value", HFILL }},
858                 { &hf_quakeworld_connectionless_rcon_password,
859                         { "Password", "quakeworld.connectionless.rcon.password",
860                         FT_STRING, BASE_DEC, NULL, 0x0,
861                         "Rcon Password", HFILL }},
862                 { &hf_quakeworld_connectionless_rcon_command,
863                         { "Command", "quakeworld.connectionless.rcon.command",
864                         FT_STRING, BASE_DEC, NULL, 0x0,
865                         "Command", HFILL }},
866                 { &hf_quakeworld_game_seq1,
867                         { "Sequence Number", "quakeworld.game.seq1",
868                         FT_UINT32, BASE_DEC, NULL, 0x0,
869                         "Sequence number of the current packet", HFILL }},
870                 { &hf_quakeworld_game_rel1,
871                         { "Reliable", "quakeworld.game.rel1",
872                         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
873                         "Packet is reliable and may be retransmitted", HFILL }},
874                 { &hf_quakeworld_game_seq2,
875                         { "Sequence Number", "quakeworld.game.seq2",
876                         FT_UINT32, BASE_DEC, NULL, 0x0,
877                         "Sequence number of the last received packet", HFILL }},
878                 { &hf_quakeworld_game_rel2,
879                         { "Reliable", "quakeworld.game.rel2",
880                         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
881                         "Packet was reliable and may be retransmitted", HFILL }},
882                 { &hf_quakeworld_game_qport,
883                         { "QPort", "quakeworld.game.qport",
884                         FT_UINT32, BASE_DEC, NULL, 0x0,
885                         "QuakeWorld Client Port", HFILL }}
886         };
887         static gint *ett[] = {
888                 &ett_quakeworld,
889                 &ett_quakeworld_connectionless,
890                 &ett_quakeworld_connectionless_text,
891                 &ett_quakeworld_connectionless_arguments,
892                 &ett_quakeworld_connectionless_connect_infostring,
893                 &ett_quakeworld_connectionless_connect_infostring_key_value,
894                 &ett_quakeworld_game,
895                 &ett_quakeworld_game_seq1,
896                 &ett_quakeworld_game_seq2,
897                 &ett_quakeworld_game_clc,
898                 &ett_quakeworld_game_svc
899         };
900         module_t *quakeworld_module;
901
902         proto_quakeworld = proto_register_protocol("QuakeWorld Network Protocol",
903                                                 "QUAKEWORLD", "quakeworld");
904         proto_register_field_array(proto_quakeworld, hf, array_length(hf));
905         proto_register_subtree_array(ett, array_length(ett));
906
907         /* Register a configuration option for port */
908         quakeworld_module = prefs_register_protocol(proto_quakeworld,
909                 proto_reg_handoff_quakeworld);
910         prefs_register_uint_preference(quakeworld_module, "udp.port",
911                                         "QuakeWorld Server UDP Port",
912                                         "Set the UDP port for the QuakeWorld Server",
913                                         10, &gbl_quakeworldServerPort);
914 }
915