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