Mark requests as such in the Info column.
[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.10 2002/04/02 06:28:16 girlich 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 <epan/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 _U_,
75         proto_tree *tree, int direction _U_)
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, -1, "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, -1, "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, -1, "Client Commands");
240                                 if (c_item) {
241                                         c_tree = proto_item_add_subtree(
242                                                 c_item, ett_quake2_game_clc);
243                                 }
244                         }
245                         dissect_quake2_client_commands(next_tvb, pinfo, c_tree);
246                 }
247                 else {
248                         proto_item *c_item = NULL;
249                         proto_tree *c_tree = NULL;
250                         if (tree) {
251                                 c_item = proto_tree_add_text(game_tree, next_tvb,
252                                 0, -1, "Server Commands");
253                                 if (c_item) {
254                                         c_tree = proto_item_add_subtree(
255                                         c_item, ett_quake2_game_svc);
256                                 }
257                         }
258                         dissect_quake2_server_commands(next_tvb, pinfo, c_tree);
259                 }
260         }
261 }
262
263
264 static void
265 dissect_quake2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
266 {
267         proto_tree      *quake2_tree = NULL;
268         proto_item      *quake2_item = NULL;
269         int             direction;
270
271         direction = (pinfo->destport == gbl_quake2ServerPort) ?
272                         DIR_C2S : DIR_S2C;
273
274         if (check_col(pinfo->cinfo, COL_PROTOCOL))
275                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "QUAKE2");
276         if (check_col(pinfo->cinfo, COL_INFO))
277                 col_set_str(pinfo->cinfo, COL_INFO, val_to_str(direction,
278                         names_direction, "%u"));
279
280         if (tree) {
281                 quake2_item = proto_tree_add_item(tree, proto_quake2,
282                                 tvb, 0, -1, FALSE);
283                 if (quake2_item)
284                         quake2_tree = proto_item_add_subtree(
285                                 quake2_item, ett_quake2);
286                         if (quake2_tree) {
287                                 proto_tree_add_uint_format(quake2_tree,
288                                         direction == DIR_S2C ?
289                                         hf_quake2_s2c :
290                                         hf_quake2_c2s,
291                                         tvb, 0, 0, 1,
292                                         "Direction: %s", val_to_str(direction, names_direction, "%u"));
293                         }
294         }
295
296         if (tvb_get_ntohl(tvb, 0) == 0xffffffff) {
297                 if (check_col(pinfo->cinfo, COL_INFO)) {
298                         col_append_str(pinfo->cinfo, COL_INFO, " Connectionless");
299                 }
300                 if (quake2_tree)
301                         proto_tree_add_uint_format(quake2_tree,
302                                 hf_quake2_connectionless,
303                                 tvb, 0, 0, 1,
304                                 "Type: Connectionless");
305                 dissect_quake2_ConnectionlessPacket(
306                         tvb, pinfo, quake2_tree, direction);
307         }
308         else {
309                 if (check_col(pinfo->cinfo, COL_INFO)) {
310                         col_append_str(pinfo->cinfo, COL_INFO, " Game");
311                 }
312                 if (quake2_tree)
313                         proto_tree_add_uint_format(quake2_tree,
314                                 hf_quake2_game,
315                                 tvb, 0, 0, 1,
316                                 "Type: Game");
317                 dissect_quake2_GamePacket(
318                         tvb, pinfo, quake2_tree, direction);
319         }
320 }
321
322
323 void
324 proto_reg_handoff_quake2(void)
325 {
326         static int Initialized=FALSE;
327         static dissector_handle_t quake2_handle;
328         static int ServerPort=0;
329  
330         if (!Initialized) {
331                 quake2_handle = create_dissector_handle(dissect_quake2,
332                                 proto_quake2);
333                 Initialized=TRUE;
334         } else {
335                 dissector_delete("udp.port", ServerPort, quake2_handle);
336         }
337  
338         /* set port for future deletes */
339         ServerPort=gbl_quake2ServerPort;
340  
341         dissector_add("udp.port", gbl_quake2ServerPort, quake2_handle);
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 }