Change "conversation_set_dissector()" to take a dissector handle, rather
[obnox/wireshark/wip.git] / packet-quake2.c
1 /* packet-quake2.c
2  * Routines for Quake II packet dissection
3  *
4  * Uwe Girlich <uwe@planetquake.com>
5  *      http://www.idsoftware.com/q1source/q1source.zip
6  *      http://www.planetquake.com/demospecs/dm2
7  *      http://www.dgs.monash.edu.au/~timf/bottim/
8  *      http://www.opt-sci.Arizona.EDU/Pandora/default.asp
9  *
10  * $Id: packet-quake2.c,v 1.5 2001/11/27 07:13:26 guy Exp $
11  *
12  * Ethereal - Network traffic analyzer
13  * By Gerald Combs <gerald@ethereal.com>
14  * Copyright 1998 Gerald Combs
15  *
16  * Copied from packet-quakeworld.c
17  * 
18  * This program is free software; you can redistribute it and/or
19  * modify it under the terms of the GNU General Public License
20  * as published by the Free Software Foundation; either version 2
21  * of the License, or (at your option) any later version.
22  * 
23  * This program is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  * GNU General Public License for more details.
27  * 
28  * You should have received a copy of the GNU General Public License
29  * along with this program; if not, write to the Free Software
30  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
31  */
32
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #ifdef HAVE_SYS_TYPES_H
38 # include <sys/types.h>
39 #endif
40
41 #include <glib.h>
42 #include "packet.h"
43 #include "prefs.h"
44
45 static int proto_quake2 = -1;
46
47 static int hf_quake2_s2c = -1;
48 static int hf_quake2_c2s = -1;
49 static int hf_quake2_connectionless = -1;
50 static int hf_quake2_game = -1;
51 static int hf_quake2_connectionless_marker = -1;
52 static int hf_quake2_connectionless_text = -1;
53 static int hf_quake2_game_seq1 = -1;
54 static int hf_quake2_game_rel1 = -1;
55 static int hf_quake2_game_seq2 = -1;
56 static int hf_quake2_game_rel2 = -1;
57 static int hf_quake2_game_qport = -1;
58
59 static gint ett_quake2 = -1;
60 static gint ett_quake2_connectionless = -1;
61 static gint ett_quake2_game = -1;
62 static gint ett_quake2_game_seq1 = -1;
63 static gint ett_quake2_game_seq2 = -1;
64 static gint ett_quake2_game_clc = -1;
65 static gint ett_quake2_game_svc = -1;
66
67 static dissector_handle_t data_handle;
68
69 #define PORT_MASTER 27910
70 static unsigned int gbl_quake2ServerPort=PORT_MASTER;
71
72
73 static void
74 dissect_quake2_ConnectionlessPacket(tvbuff_t *tvb, packet_info *pinfo,
75         proto_tree *tree, int direction)
76 {
77         proto_tree      *cl_tree = NULL;
78         proto_item      *cl_item = NULL;
79         guint8          text[2048];
80         int             maxbufsize = 0;
81         int             len;
82         int             offset;
83
84         guint32 marker;
85
86         marker = tvb_get_ntohl(tvb, 0);
87         if (tree) {
88                 cl_item = proto_tree_add_text(tree, tvb,
89                                 0, tvb_length(tvb), "Connectionless");
90                 if (cl_item)
91                         cl_tree = proto_item_add_subtree(
92                                 cl_item, ett_quake2_connectionless);
93         }
94
95         if (cl_tree) {
96                 proto_tree_add_uint(cl_tree, hf_quake2_connectionless_marker,
97                                 tvb, 0, 4, marker);
98         }
99
100         /* all the rest of the packet is just text */
101         offset = 4;
102
103         maxbufsize = MIN((gint)sizeof(text), tvb_length_remaining(tvb, offset));
104         len = tvb_get_nstringz0(tvb, offset, maxbufsize, text);
105         if (cl_tree) {
106                 proto_tree_add_string(cl_tree, hf_quake2_connectionless_text,
107                         tvb, offset, len + 1, text);
108         }
109         offset += len + 1;
110
111         /* we should analyse the result 'text' a bit further */
112         /* for this we need the direction parameter */
113 }
114
115
116 static void
117 dissect_quake2_client_commands(tvbuff_t *tvb, packet_info *pinfo,
118         proto_tree *tree)
119 {
120         /* If I have too much time at hand, I'll fill it with all
121            the information from my DM2 specs:
122                 http://www.planetquake.com/demospecs/dm2/
123         */
124         call_dissector(data_handle,tvb, pinfo, tree);
125 }
126
127
128 static void
129 dissect_quake2_server_commands(tvbuff_t *tvb, packet_info *pinfo,
130         proto_tree *tree)
131 {
132         /* If I have too much time at hand, I'll fill it with all
133            the information from my DM2 specs:
134                 http://www.planetquake.com/demospecs/dm2/
135         */
136         call_dissector(data_handle,tvb, pinfo, tree);
137 }
138
139
140 static const value_string names_reliable[] = {
141         { 0, "Non Reliable" },
142         { 1, "Reliable" },
143         { 0, NULL }
144 };
145
146
147 static const value_string names_direction[] = {
148 #define DIR_C2S 0
149         { DIR_C2S, "Client to Server" },
150 #define DIR_S2C 1
151         { DIR_S2C, "Server to Client" },
152         { 0, NULL }
153 };
154
155
156 static void
157 dissect_quake2_GamePacket(tvbuff_t *tvb, packet_info *pinfo,
158         proto_tree *tree, int direction)
159 {
160         proto_tree      *game_tree = NULL;
161         proto_item      *game_item = NULL;
162         guint32 seq1;
163         guint32 seq2;
164         int rel1;
165         int rel2;
166         int offset;
167         guint           rest_length;
168
169         direction = (pinfo->destport == gbl_quake2ServerPort) ?
170                         DIR_C2S : DIR_S2C;
171
172         if (tree) {
173                 game_item = proto_tree_add_text(tree, tvb,
174                                 0, tvb_length(tvb), "Game");
175                 if (game_item)
176                         game_tree = proto_item_add_subtree(
177                                 game_item, ett_quake2_game);
178         }
179
180         offset = 0;
181
182         seq1 = tvb_get_letohl(tvb, offset);
183         rel1 = seq1 & 0x80000000 ? 1 : 0;
184         seq1 &= ~0x80000000;
185         if (game_tree) {
186                 proto_item *seq1_item = proto_tree_add_text(game_tree,
187                         tvb, offset, 4, "Current Sequence: %u (%s)",
188                         seq1, val_to_str(rel1,names_reliable,"%u"));
189                 if (seq1_item) {
190                         proto_tree *seq1_tree = proto_item_add_subtree(
191                                 seq1_item, ett_quake2_game_seq1);
192                         proto_tree_add_uint(seq1_tree, hf_quake2_game_seq1,
193                                         tvb, offset, 4, seq1);
194                         proto_tree_add_boolean(seq1_tree, hf_quake2_game_rel1,
195                                         tvb, offset+3, 1, rel1);
196                 }
197         }
198         offset += 4;
199
200         seq2 = tvb_get_letohl(tvb, offset);
201         rel2 = seq2 & 0x80000000 ? 1 : 0;
202         seq2 &= ~0x80000000;
203         if (game_tree) {
204                 proto_item *seq2_item = proto_tree_add_text(game_tree,
205                         tvb, offset, 4, "Acknowledge Sequence: %u (%s)",
206                         seq2, val_to_str(rel2,names_reliable,"%u"));;
207                 if (seq2_item) {
208                         proto_tree *seq2_tree = proto_item_add_subtree(
209                                 seq2_item, ett_quake2_game_seq2);
210                         proto_tree_add_uint(seq2_tree, hf_quake2_game_seq2,
211                                         tvb, offset, 4, seq2);
212                         proto_tree_add_boolean(seq2_tree, hf_quake2_game_rel2,
213                                         tvb, offset+3, 1, rel2);
214                 }
215         }
216         offset += 4;
217
218         if (direction == DIR_C2S) {
219                 /* client to server */
220                 guint16 qport = tvb_get_letohs(tvb, offset);
221                 if (game_tree) {
222                         proto_tree_add_uint(game_tree, hf_quake2_game_qport, 
223                                 tvb, offset, 2, qport);
224                 }
225                 offset +=2;
226         }
227
228         /* all the rest is pure game data */
229         rest_length = tvb_reported_length(tvb) - offset;
230         if (rest_length) {
231                 tvbuff_t *next_tvb =
232                 tvb_new_subset(tvb, offset, rest_length , rest_length);
233
234                 if (direction == DIR_C2S) {
235                         proto_item *c_item = NULL;
236                         proto_tree *c_tree = NULL;
237                         if (tree) {
238                                 c_item = proto_tree_add_text(game_tree, next_tvb,
239                                 0, tvb_length(next_tvb),
240                                 "Client Commands");
241                                 if (c_item) {
242                                         c_tree = proto_item_add_subtree(
243                                                 c_item, ett_quake2_game_clc);
244                                 }
245                         }
246                         dissect_quake2_client_commands(next_tvb, pinfo, c_tree);
247                 }
248                 else {
249                         proto_item *c_item = NULL;
250                         proto_tree *c_tree = NULL;
251                         if (tree) {
252                                 c_item = proto_tree_add_text(game_tree, next_tvb,
253                                 0, tvb_length(next_tvb),
254                                 "Server Commands");
255                                 if (c_item) {
256                                         c_tree = proto_item_add_subtree(
257                                         c_item, ett_quake2_game_svc);
258                                 }
259                         }
260                         dissect_quake2_server_commands(next_tvb, pinfo, c_tree);
261                 }
262         }
263 }
264
265
266 static void
267 dissect_quake2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
268 {
269         proto_tree      *quake2_tree = NULL;
270         proto_item      *quake2_item = NULL;
271         int             direction;
272
273         direction = (pinfo->destport == gbl_quake2ServerPort) ?
274                         DIR_C2S : DIR_S2C;
275
276         if (check_col(pinfo->fd, COL_PROTOCOL))
277                 col_set_str(pinfo->fd, COL_PROTOCOL, "QUAKE2");
278         if (check_col(pinfo->fd, COL_INFO))
279                 col_set_str(pinfo->fd, COL_INFO, val_to_str(direction,
280                         names_direction, "%u"));
281
282         if (tree) {
283                 quake2_item = proto_tree_add_item(tree, proto_quake2,
284                                 tvb, 0, tvb_length(tvb), FALSE);
285                 if (quake2_item)
286                         quake2_tree = proto_item_add_subtree(
287                                 quake2_item, ett_quake2);
288                         if (quake2_tree) {
289                                 proto_tree_add_uint_format(quake2_tree,
290                                         direction == DIR_S2C ?
291                                         hf_quake2_s2c :
292                                         hf_quake2_c2s,
293                                         tvb, 0, 0, 1,
294                                         "Direction: %s", val_to_str(direction, names_direction, "%u"));
295                         }
296         }
297
298         if (tvb_get_ntohl(tvb, 0) == 0xffffffff) {
299                 if (check_col(pinfo->fd, COL_INFO)) {
300                         col_append_str(pinfo->fd, COL_INFO, " Connectionless");
301                 }
302                 if (quake2_tree)
303                         proto_tree_add_uint_format(quake2_tree,
304                                 hf_quake2_connectionless,
305                                 tvb, 0, 0, 1,
306                                 "Type: Connectionless");
307                 dissect_quake2_ConnectionlessPacket(
308                         tvb, pinfo, quake2_tree, direction);
309         }
310         else {
311                 if (check_col(pinfo->fd, COL_INFO)) {
312                         col_append_str(pinfo->fd, COL_INFO, " Game");
313                 }
314                 if (quake2_tree)
315                         proto_tree_add_uint_format(quake2_tree,
316                                 hf_quake2_game,
317                                 tvb, 0, 0, 1,
318                                 "Type: Game");
319                 dissect_quake2_GamePacket(
320                         tvb, pinfo, quake2_tree, direction);
321         }
322 }
323
324
325 void
326 proto_reg_handoff_quake2(void)
327 {
328         static int Initialized=FALSE;
329         static int ServerPort=0;
330  
331         if (Initialized) {
332                 dissector_delete("udp.port", ServerPort, dissect_quake2);
333         } else {
334                 Initialized=TRUE;
335         }
336  
337         /* set port for future deletes */
338         ServerPort=gbl_quake2ServerPort;
339  
340         dissector_add("udp.port", gbl_quake2ServerPort,
341                         dissect_quake2, proto_quake2);
342         data_handle = find_dissector("data");
343 }
344
345
346 void
347 proto_register_quake2(void)
348 {
349         static hf_register_info hf[] = {
350                 { &hf_quake2_c2s,
351                         { "Client to Server", "quake2.c2s",
352                         FT_UINT32, BASE_DEC, NULL, 0x0,
353                         "Client to Server", HFILL }},
354                 { &hf_quake2_s2c,
355                         { "Server to Client", "quake2.s2c",
356                         FT_UINT32, BASE_DEC, NULL, 0x0,
357                         "Server to Client", HFILL }},
358                 { &hf_quake2_connectionless,
359                         { "Connectionless", "quake2.connectionless",
360                         FT_UINT32, BASE_DEC, NULL, 0x0,
361                         "Connectionless", HFILL }},
362                 { &hf_quake2_game,
363                         { "Game", "quake2.game",
364                         FT_UINT32, BASE_DEC, NULL, 0x0,
365                         "Game", HFILL }},
366                 { &hf_quake2_connectionless_marker,
367                         { "Marker", "quake2.connectionless.marker",
368                         FT_UINT32, BASE_HEX, NULL, 0x0,
369                         "Marker", HFILL }},
370                 { &hf_quake2_connectionless_text,
371                         { "Text", "quake2.connectionless.text",
372                         FT_STRING, BASE_DEC, NULL, 0x0,
373                         "Text", HFILL }},
374                 { &hf_quake2_game_seq1,
375                         { "Sequence Number", "quake2.game.seq1",
376                         FT_UINT32, BASE_DEC, NULL, 0x0,
377                         "Sequence number of the current packet", HFILL }},
378                 { &hf_quake2_game_rel1,
379                         { "Reliable", "quake2.game.rel1",
380                         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
381                         "Packet is reliable and may be retransmitted", HFILL }},
382                 { &hf_quake2_game_seq2,
383                         { "Sequence Number", "quake2.game.seq2",
384                         FT_UINT32, BASE_DEC, NULL, 0x0,
385                         "Sequence number of the last received packet", HFILL }},
386                 { &hf_quake2_game_rel2,
387                         { "Reliable", "quake2.game.rel2",
388                         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
389                         "Packet was reliable and may be retransmitted", HFILL }},
390                 { &hf_quake2_game_qport,
391                         { "QPort", "quake2.game.qport",
392                         FT_UINT32, BASE_DEC, NULL, 0x0,
393                         "Quake II Client Port", HFILL }}
394         };
395         static gint *ett[] = {
396                 &ett_quake2,
397                 &ett_quake2_connectionless,
398                 &ett_quake2_game,
399                 &ett_quake2_game_seq1,
400                 &ett_quake2_game_seq2,
401                 &ett_quake2_game_clc,
402                 &ett_quake2_game_svc
403         };
404         module_t *quake2_module;
405
406         proto_quake2 = proto_register_protocol("Quake II Network Protocol",
407                                                 "QUAKE2", "quake2");
408         proto_register_field_array(proto_quake2, hf, array_length(hf));
409         proto_register_subtree_array(ett, array_length(ett));
410
411         /* Register a configuration option for port */
412         quake2_module = prefs_register_protocol(proto_quake2,
413                 proto_reg_handoff_quake2);
414         prefs_register_uint_preference(quake2_module, "udp.port",
415                                         "Quake II Server UDP Port",
416                                         "Set the UDP port for the Quake II Server",
417                                         10, &gbl_quake2ServerPort);
418 }
419