Move dissectors to epan/dissectors directory.
[obnox/wireshark/wip.git] / epan / dissectors / packet-quake3.c
1 /* packet-quake3.c
2  * Routines for Quake III Arena packet dissection
3  *
4  * Uwe Girlich <uwe@planetquake.com>
5  *
6  * $Id$
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@ethereal.com>
10  * Copyright 1998 Gerald Combs
11  *
12  * Copied from packet-quake2.c
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27  */
28
29
30 /*
31    All informations used in this decoding were gathered from
32         * some own captures of a private server,
33         * the "Server commands howto" document written by id Software
34                 (http://www.quake3arena.com/tech/ServerCommandsHowto.html)
35         * source code of the game server browser tool qstat
36                 (http://www.qstat.org).
37 */
38
39
40 #ifdef HAVE_CONFIG_H
41 # include "config.h"
42 #endif
43
44 #include <string.h>
45 #include <glib.h>
46 #include <epan/packet.h>
47 #include "prefs.h"
48 #include <epan/resolv.h>
49
50 static int proto_quake3 = -1;
51
52 static int hf_quake3_direction = -1;
53 static int hf_quake3_connectionless = -1;
54 static int hf_quake3_game = -1;
55 static int hf_quake3_connectionless_marker = -1;
56 static int hf_quake3_connectionless_text = -1;
57 static int hf_quake3_connectionless_command = -1;
58 static int hf_quake3_server_addr = -1;
59 static int hf_quake3_server_port = -1;
60 static int hf_quake3_game_seq1 = -1;
61 static int hf_quake3_game_rel1 = -1;
62 static int hf_quake3_game_seq2 = -1;
63 static int hf_quake3_game_rel2 = -1;
64 static int hf_quake3_game_qport = -1;
65
66 static gint ett_quake3 = -1;
67 static gint ett_quake3_connectionless = -1;
68 static gint ett_quake3_connectionless_text = -1;
69 static gint ett_quake3_server = -1;
70 static gint ett_quake3_game = -1;
71 static gint ett_quake3_game_seq1 = -1;
72 static gint ett_quake3_game_seq2 = -1;
73 static gint ett_quake3_game_clc = -1;
74 static gint ett_quake3_game_svc = -1;
75
76 static dissector_handle_t data_handle;
77
78 #define QUAKE3_SERVER_PORT 27960
79 #define QUAKE3_MASTER_PORT 27950
80 static unsigned int gbl_quake3_server_port=QUAKE3_SERVER_PORT;
81 static unsigned int gbl_quake3_master_port=QUAKE3_MASTER_PORT;
82
83
84 static const value_string names_direction[] = {
85 #define DIR_UNKNOWN 0
86         { DIR_UNKNOWN, "Unknown" },
87 #define DIR_C2S 1
88         { DIR_C2S, "Client to Server" },
89 #define DIR_S2C 2
90         { DIR_S2C, "Server to Client" },
91 #define DIR_C2M 3
92         { DIR_C2M, "Client to Master" },
93 #define DIR_M2C 4
94         { DIR_M2C, "Master to Client" },
95         { 0, NULL }
96 };
97
98
99 #define string_starts_with(s1, s2) (strncmp((s1), (s2), strlen(s2)) == 0)
100
101
102 static const value_string names_command[] = {
103 #define COMMAND_UNKNOWN                 0
104         { COMMAND_UNKNOWN,              "Unknown" },
105 #define COMMAND_statusResponse          1
106         { COMMAND_statusResponse,       "Reply Status" },
107 #define COMMAND_getstatus               2
108         { COMMAND_getstatus,            "Request Status" },
109 #define COMMAND_infoResponse            3
110         { COMMAND_infoResponse,         "Reply Info" },
111 #define COMMAND_getinfo                 4
112         { COMMAND_getinfo,              "Request Info" },
113 #define COMMAND_challengeResponse       5
114         { COMMAND_challengeResponse,    "Reply Challenge" },
115 #define COMMAND_getchallenge            6
116         { COMMAND_getchallenge,         "Request Challenge" },
117 #define COMMAND_connectResponse         7
118         { COMMAND_connectResponse,      "Reply Connect" },
119 #define COMMAND_connect                 8
120         { COMMAND_connect,              "Request Connect" },
121 #define COMMAND_rconResponse            9
122         { COMMAND_rconResponse,         "Reply Remote Command" },
123 #define COMMAND_rcon                    10
124         { COMMAND_rcon,                 "Request Remote Command" },
125 #define COMMAND_getmotdResponse         11
126         { COMMAND_getmotdResponse,      "Reply Motto of the Day" },
127 #define COMMAND_getmotd                 12
128         { COMMAND_getmotd,              "Request Motto of the Day" },
129 #define COMMAND_getserversResponse      13
130         { COMMAND_getserversResponse,   "Reply Servers" },
131 #define COMMAND_getservers              14
132         { COMMAND_getservers,           "Request Servers" },
133 #define COMMAND_getKeyAuthorize         15
134         { COMMAND_getKeyAuthorize,      "Request Key Authorization" },
135 #define COMMAND_getIpAuthorize          16
136         { COMMAND_getIpAuthorize,       "Request IP Authorization" },
137 #define COMMAND_ipAuthorize             17
138         { COMMAND_ipAuthorize,          "Reply IP Authorization" },
139         { 0, NULL }
140 };
141
142
143 static void
144 dissect_quake3_ConnectionlessPacket(tvbuff_t *tvb, packet_info *pinfo _U_,
145         proto_tree *tree, int* direction)
146 {
147         proto_tree      *cl_tree = NULL;
148         proto_item      *cl_item = NULL;
149         proto_item      *text_item = NULL;
150         proto_tree      *text_tree = NULL;
151         guint8          text[2048];
152         int             len;
153         int             offset;
154         guint32         marker;
155         int             command;
156         int             command_len;
157         int             command_finished = FALSE;
158
159         marker = tvb_get_ntohl(tvb, 0);
160         if (tree) {
161                 cl_item = proto_tree_add_text(tree, tvb,
162                                 0, -1, "Connectionless");
163                 if (cl_item)
164                         cl_tree = proto_item_add_subtree(
165                                 cl_item, ett_quake3_connectionless);
166         }
167
168         if (cl_tree) {
169                 proto_tree_add_uint(cl_tree, hf_quake3_connectionless_marker,
170                                 tvb, 0, 4, marker);
171         }
172
173         /* all the rest of the packet is just text */
174         offset = 4;
175
176         len = tvb_get_nstringz0(tvb, offset, sizeof(text), text);
177         if (cl_tree) {
178                 text_item = proto_tree_add_string(cl_tree,
179                                 hf_quake3_connectionless_text,
180                                 tvb, offset, len + 1, text);
181                 if (text_item) {
182                         text_tree = proto_item_add_subtree(
183                                         text_item,
184                                         ett_quake3_connectionless_text);
185                 }
186         }
187
188         command = COMMAND_UNKNOWN;
189         command_len = 0;
190
191         if (strncmp(text, "statusResponse", 14) == 0) {
192                 command = COMMAND_statusResponse;
193                 *direction = DIR_S2C;
194                 command_len = 14;
195         }
196         else if (strncmp(text, "getstatus", 9) == 0) {
197                 command = COMMAND_getstatus;
198                 *direction = DIR_C2S;
199                 command_len = 9;
200         }
201         else if (strncmp(text, "infoResponse", 12) == 0) {
202                 command = COMMAND_infoResponse;
203                 *direction = DIR_S2C;
204                 command_len = 12;
205         }
206         else if (strncmp(text, "getinfo", 7) == 0) {
207                 command = COMMAND_getinfo;
208                 *direction = DIR_C2S;
209                 command_len = 7;
210         }
211         else if (strncmp(text, "challengeResponse", 17) == 0) {
212                 command = COMMAND_challengeResponse;
213                 *direction = DIR_S2C;
214                 command_len = 17;
215         }
216         else if (strncmp(text, "getchallenge", 12) == 0) {
217                 command = COMMAND_getchallenge;
218                 *direction = DIR_C2S;
219                 command_len = 12;
220         }
221         else if (strncmp(text, "connectResponse", 15) == 0) {
222                 command = COMMAND_connectResponse;
223                 *direction = DIR_S2C;
224                 command_len = 15;
225         }
226         else if (strncmp(text, "connect", 7) == 0) {
227                 command = COMMAND_connect;
228                 *direction = DIR_C2S;
229                 command_len = 7;
230         }
231         else if (strncmp(text, "rconResponse", 12) == 0) {
232                 command = COMMAND_rconResponse;
233                 *direction = DIR_S2C;
234                 command_len = 12;
235         }
236         else if (strncmp(text, "rcon", 4) == 0) {
237                 command = COMMAND_rcon;
238                 *direction = DIR_C2S;
239                 command_len = 4;
240         }
241         else if (strncmp(text, "getmotdResponse", 15) == 0) {
242                 command = COMMAND_getmotdResponse;
243                 *direction = DIR_M2C;
244                 command_len = 15;
245         }
246         else if (strncmp(text, "getmotd", 7) == 0) {
247                 command = COMMAND_getmotd;
248                 *direction = DIR_C2M;
249                 command_len = 7;
250         }
251         else if (strncmp(text, "getserversResponse", 18) == 0) {
252                 int base;
253                 command = COMMAND_getserversResponse;
254                 *direction = DIR_M2C;
255                 command_len = 18;
256                 /* The data can contain 0's, and the text string is binary
257                 anyway, so better replace the text string after the first
258                 \ with "<DATA>". */
259                 if (text_item) {
260                         /* first correct the length until the end of the packet */
261                         proto_item_set_len(text_item, tvb_length_remaining(tvb, offset));
262                         /* then replace the text */
263                         proto_item_set_text(text_item, "Text: getserversResponse<DATA>");
264                 }
265                 if (text_tree)
266                         proto_tree_add_string(text_tree, hf_quake3_connectionless_command,
267                                         tvb, offset, command_len,
268                                         val_to_str(command, names_command, "Unknown"));
269                 command_finished = TRUE;
270
271                 /* now we decode all the rest */
272                 base = offset + 18;
273                 /* '/', ip-address in network order, port in network order */
274                 while (tvb_reported_length_remaining(tvb, base) >= 7) {
275                         guint32         ip_addr;
276                         guint16         udp_port;
277                         proto_item      *server_item = NULL;
278                         proto_tree      *server_tree = NULL;
279
280                         tvb_memcpy(tvb, (guint8 *)&ip_addr, base + 1, 4);
281                         udp_port = tvb_get_ntohs(tvb, base + 5);
282
283                         /* It may be a good idea to create a conversation for
284                                 every server in this list, as we'll see at
285                                 least a getinfo request to each of them and they
286                                 may run on totally unusual ports.  */
287
288                         if (text_tree) {
289                                 server_item = proto_tree_add_text(text_tree,
290                                         tvb, base, 7,
291                                         "Server: %s:%u",
292                                                 get_hostname(ip_addr),
293                                                 udp_port);
294                                 if (server_item)
295                                         server_tree = proto_item_add_subtree(
296                                                 server_item,
297                                                 ett_quake3_server);
298                         }
299                         if (server_tree) {
300                                 proto_tree_add_ipv4(server_tree, hf_quake3_server_addr,
301                                         tvb, base + 1, 4, ip_addr);
302                                 proto_tree_add_uint(server_tree, hf_quake3_server_port,
303                                         tvb, base + 5, 2, udp_port);
304                         }
305
306                         base += 7;
307                 }
308         }
309         else if (strncmp(text, "getservers", 10) == 0) {
310                 command = COMMAND_getservers;
311                 *direction = DIR_C2M;
312                 command_len = 10;
313         }
314         else if (strncmp(text, "getKeyAuthorize", 15) == 0) {
315                 command = COMMAND_getKeyAuthorize;
316                 *direction = DIR_C2M;
317                 command_len = 15;
318         }
319         else if (strncmp(text, "getIpAuthorize", 14) == 0) {
320                 command = COMMAND_getIpAuthorize;
321                 *direction = DIR_C2M;
322                 command_len = 14;
323         }
324         else if (strncmp(text, "ipAuthorize", 11) == 0) {
325                 command = COMMAND_ipAuthorize;
326                 *direction = DIR_M2C;
327                 command_len = 11;
328         }
329         else {
330                 *direction = DIR_UNKNOWN;
331         }
332
333         if (text_tree && command_finished == FALSE) {
334                 proto_tree_add_string(text_tree, hf_quake3_connectionless_command,
335                                         tvb, offset, command_len,
336                                         val_to_str(command, names_command, "Unknown"));
337         }
338
339         offset += len + 1;
340
341 }
342
343
344 static void
345 dissect_quake3_client_commands(tvbuff_t *tvb, packet_info *pinfo,
346         proto_tree *tree)
347 {
348         /* this shouldn't be too difficult */
349         call_dissector(data_handle,tvb, pinfo, tree);
350 }
351
352
353 static void
354 dissect_quake3_server_commands(tvbuff_t *tvb, packet_info *pinfo,
355         proto_tree *tree)
356 {
357         /* It is totally forbidden to decode this any further,
358         I wont do it. */
359         call_dissector(data_handle,tvb, pinfo, tree);
360 }
361
362
363 static const value_string names_reliable[] = {
364         { 0, "Non Reliable" },
365         { 1, "Reliable" },
366         { 0, NULL }
367 };
368
369
370 static void
371 dissect_quake3_GamePacket(tvbuff_t *tvb, packet_info *pinfo,
372         proto_tree *tree, int *direction)
373 {
374         proto_tree      *game_tree = NULL;
375         proto_item      *game_item = NULL;
376         guint32         seq1;
377         guint32         seq2;
378         int             rel1;
379         int             rel2;
380         int             offset;
381         guint           rest_length;
382
383         *direction = (pinfo->destport == gbl_quake3_server_port) ?
384                         DIR_C2S : DIR_S2C;
385
386         if (tree) {
387                 game_item = proto_tree_add_text(tree, tvb,
388                                 0, -1, "Game");
389                 if (game_item)
390                         game_tree = proto_item_add_subtree(
391                                 game_item, ett_quake3_game);
392         }
393
394         offset = 0;
395
396         seq1 = tvb_get_letohs(tvb, offset);
397         rel1 = seq1 & 0x8000 ? 1 : 0;
398         seq1 &= ~0x8000;
399         if (game_tree) {
400                 proto_item *seq1_item = proto_tree_add_text(game_tree,
401                         tvb, offset, 2, "Current Sequence: %u (%s)",
402                         seq1, val_to_str(rel1,names_reliable,"%u"));
403                 if (seq1_item) {
404                         proto_tree *seq1_tree = proto_item_add_subtree(
405                                 seq1_item, ett_quake3_game_seq1);
406                         proto_tree_add_uint(seq1_tree, hf_quake3_game_seq1,
407                                         tvb, offset, 2, seq1);
408                         proto_tree_add_boolean(seq1_tree, hf_quake3_game_rel1,
409                                         tvb, offset+1, 1, rel1);
410                 }
411         }
412         offset += 2;
413
414         seq2 = tvb_get_letohs(tvb, offset);
415         rel2 = seq2 & 0x8000 ? 1 : 0;
416         seq2 &= ~0x8000;
417         if (game_tree) {
418                 proto_item *seq2_item = proto_tree_add_text(game_tree,
419                         tvb, offset, 2, "Acknowledge Sequence: %u (%s)",
420                         seq2, val_to_str(rel2,names_reliable,"%u"));
421                 if (seq2_item) {
422                         proto_tree *seq2_tree = proto_item_add_subtree(
423                                 seq2_item, ett_quake3_game_seq2);
424                         proto_tree_add_uint(seq2_tree, hf_quake3_game_seq2,
425                                         tvb, offset, 2, seq2);
426                         proto_tree_add_boolean(seq2_tree, hf_quake3_game_rel2,
427                                         tvb, offset+1, 1, rel2);
428                 }
429         }
430         offset += 2;
431
432         if (*direction == DIR_C2S) {
433                 /* client to server */
434                 guint16 qport = tvb_get_letohs(tvb, offset);
435                 if (game_tree) {
436                         proto_tree_add_uint(game_tree, hf_quake3_game_qport,
437                                 tvb, offset, 2, qport);
438                 }
439                 offset +=2;
440         }
441
442         /* all the rest is pure game data */
443         rest_length = tvb_reported_length(tvb) - offset;
444         if (rest_length) {
445                 tvbuff_t *next_tvb =
446                 tvb_new_subset(tvb, offset, rest_length , rest_length);
447
448                 if (*direction == DIR_C2S) {
449                         proto_item *c_item = NULL;
450                         proto_tree *c_tree = NULL;
451                         if (tree) {
452                                 c_item = proto_tree_add_text(game_tree, next_tvb,
453                                 0, -1, "Client Commands");
454                                 if (c_item) {
455                                         c_tree = proto_item_add_subtree(
456                                                 c_item, ett_quake3_game_clc);
457                                 }
458                         }
459                         dissect_quake3_client_commands(next_tvb, pinfo, c_tree);
460                 }
461                 else {
462                         proto_item *c_item = NULL;
463                         proto_tree *c_tree = NULL;
464                         if (tree) {
465                                 c_item = proto_tree_add_text(game_tree, next_tvb,
466                                 0, -1, "Server Commands");
467                                 if (c_item) {
468                                         c_tree = proto_item_add_subtree(
469                                         c_item, ett_quake3_game_svc);
470                                 }
471                         }
472                         dissect_quake3_server_commands(next_tvb, pinfo, c_tree);
473                 }
474         }
475 }
476
477
478 static void
479 dissect_quake3(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
480 {
481         proto_tree      *quake3_tree = NULL;
482         proto_item      *quake3_item = NULL;
483         proto_item      *dir_item = NULL;
484         int             direction;
485
486         direction = DIR_UNKNOWN;
487
488         if (check_col(pinfo->cinfo, COL_PROTOCOL))
489                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "QUAKE3");
490
491         if (tree) {
492                 quake3_item = proto_tree_add_item(tree, proto_quake3,
493                                 tvb, 0, -1, FALSE);
494                 if (quake3_item)
495                         quake3_tree = proto_item_add_subtree(
496                                 quake3_item, ett_quake3);
497
498                         if (quake3_tree) {
499                                 dir_item = proto_tree_add_none_format(
500                                         quake3_tree,
501                                         hf_quake3_direction, tvb, 0, 0,
502                                         "Direction: %s",
503                                         val_to_str(direction,
504                                                 names_direction, "%u"));
505                         }
506         }
507
508         if (tvb_get_ntohl(tvb, 0) == 0xffffffff) {
509                 if (check_col(pinfo->cinfo, COL_INFO)) {
510                         col_set_str(pinfo->cinfo, COL_INFO, "Connectionless ");
511                 }
512                 if (quake3_tree)
513                         proto_tree_add_uint_format(quake3_tree,
514                                 hf_quake3_connectionless,
515                                 tvb, 0, 0, 1,
516                                 "Type: Connectionless");
517                 dissect_quake3_ConnectionlessPacket(
518                         tvb, pinfo, quake3_tree, &direction);
519         }
520         else {
521                 if (check_col(pinfo->cinfo, COL_INFO)) {
522                         col_set_str(pinfo->cinfo, COL_INFO, "Game ");
523                 }
524                 if (quake3_tree)
525                         proto_tree_add_uint_format(quake3_tree,
526                                 hf_quake3_game,
527                                 tvb, 0, 0, 1,
528                                 "Type: Game");
529                 dissect_quake3_GamePacket(
530                         tvb, pinfo, quake3_tree, &direction);
531         }
532         if (direction != DIR_UNKNOWN && dir_item)
533                 proto_item_set_text(dir_item,
534                                         "Direction: %s",
535                                         val_to_str(direction,
536                                                 names_direction, "%u"));
537
538         if (check_col(pinfo->cinfo, COL_INFO))
539                 col_append_str(pinfo->cinfo, COL_INFO, val_to_str(direction,
540                         names_direction, "%u"));
541 }
542
543
544 void
545 proto_reg_handoff_quake3(void)
546 {
547         static int initialized=FALSE;
548         static dissector_handle_t quake3_handle;
549         static int server_port;
550         static int master_port;
551         int i;
552
553         if (!initialized) {
554                 quake3_handle = create_dissector_handle(dissect_quake3,
555                                 proto_quake3);
556                 initialized=TRUE;
557         } else {
558                 for (i=0;i<4;i++)
559                         dissector_delete("udp.port", server_port+i, quake3_handle);
560                 for (i=0;i<4;i++)
561                         dissector_delete("udp.port", master_port+i, quake3_handle);
562         }
563
564         /* set port for future deletes */
565         server_port = gbl_quake3_server_port;
566         master_port = gbl_quake3_master_port;
567
568         /* add dissectors */
569         for (i=0;i<4;i++)
570                 dissector_add("udp.port", gbl_quake3_server_port + i,
571                         quake3_handle);
572         for (i=0;i<4;i++)
573                 dissector_add("udp.port", gbl_quake3_master_port + i,
574                         quake3_handle);
575         data_handle = find_dissector("data");
576 }
577
578
579 void
580 proto_register_quake3(void)
581 {
582         static hf_register_info hf[] = {
583                 { &hf_quake3_direction,
584                         { "Direction", "quake3.direction",
585                         FT_NONE, BASE_DEC, NULL, 0x0,
586                         "Packet Direction", HFILL }},
587                 { &hf_quake3_connectionless,
588                         { "Connectionless", "quake3.connectionless",
589                         FT_UINT32, BASE_DEC, NULL, 0x0,
590                         "Connectionless", HFILL }},
591                 { &hf_quake3_game,
592                         { "Game", "quake3.game",
593                         FT_UINT32, BASE_DEC, NULL, 0x0,
594                         "Game", HFILL }},
595                 { &hf_quake3_connectionless_marker,
596                         { "Marker", "quake3.connectionless.marker",
597                         FT_UINT32, BASE_HEX, NULL, 0x0,
598                         "Marker", HFILL }},
599                 { &hf_quake3_connectionless_text,
600                         { "Text", "quake3.connectionless.text",
601                         FT_STRING, BASE_DEC, NULL, 0x0,
602                         "Text", HFILL }},
603                 { &hf_quake3_connectionless_command,
604                         { "Command", "quake3.connectionless.command",
605                         FT_STRING, BASE_DEC, NULL, 0x0,
606                         "Command", HFILL }},
607                 { &hf_quake3_server_addr,
608                         { "Server Address", "quake3.server.addr",
609                         FT_IPv4, BASE_DEC, NULL, 0x0,
610                         "Server IP Address", HFILL }},
611                 { &hf_quake3_server_port,
612                         { "Server Port", "quake3.server.port",
613                         FT_UINT16, BASE_DEC, NULL, 0x0,
614                         "Server UDP Port", HFILL }},
615                 { &hf_quake3_game_seq1,
616                         { "Sequence Number", "quake3.game.seq1",
617                         FT_UINT32, BASE_DEC, NULL, 0x0,
618                         "Sequence number of the current packet", HFILL }},
619                 { &hf_quake3_game_rel1,
620                         { "Reliable", "quake3.game.rel1",
621                         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
622                         "Packet is reliable and may be retransmitted", HFILL }},
623                 { &hf_quake3_game_seq2,
624                         { "Sequence Number", "quake3.game.seq2",
625                         FT_UINT32, BASE_DEC, NULL, 0x0,
626                         "Sequence number of the last received packet", HFILL }},
627                 { &hf_quake3_game_rel2,
628                         { "Reliable", "quake3.game.rel2",
629                         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
630                         "Packet was reliable and may be retransmitted", HFILL }},
631                 { &hf_quake3_game_qport,
632                         { "QPort", "quake3.game.qport",
633                         FT_UINT32, BASE_DEC, NULL, 0x0,
634                         "Quake III Arena Client Port", HFILL }}
635         };
636         static gint *ett[] = {
637                 &ett_quake3,
638                 &ett_quake3_connectionless,
639                 &ett_quake3_connectionless_text,
640                 &ett_quake3_server,
641                 &ett_quake3_game,
642                 &ett_quake3_game_seq1,
643                 &ett_quake3_game_seq2,
644                 &ett_quake3_game_clc,
645                 &ett_quake3_game_svc
646         };
647         module_t *quake3_module;
648
649         proto_quake3 = proto_register_protocol("Quake III Arena Network Protocol",
650                                                 "QUAKE3", "quake3");
651         proto_register_field_array(proto_quake3, hf, array_length(hf));
652         proto_register_subtree_array(ett, array_length(ett));
653
654         /* Register a configuration option for port */
655         quake3_module = prefs_register_protocol(proto_quake3,
656                 proto_reg_handoff_quake3);
657         prefs_register_uint_preference(quake3_module, "udp.arena_port",
658                                         "Quake III Arena Server UDP Base Port",
659                                         "Set the UDP base port for the Quake III Arena Server",
660                                         10, &gbl_quake3_server_port);
661         prefs_register_uint_preference(quake3_module, "udp.master_port",
662                                         "Quake III Arena Master Server UDP Base Port",
663                                         "Set the UDP base port for the Quake III Arena Master Server",
664                                         10, &gbl_quake3_master_port);
665 }