Add in some heuristics to try to detect AIX libpcap format. (This works
[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.2 2001/07/22 18:52:38 girlich Exp $
11  *
12  * Ethereal - Network traffic analyzer
13  * By Gerald Combs <gerald@zing.org>
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
68 #define PORT_MASTER 27910
69 static unsigned int gbl_quake2ServerPort=PORT_MASTER;
70
71
72 static void
73 dissect_quake2_ConnectionlessPacket(tvbuff_t *tvb, packet_info *pinfo,
74         proto_tree *tree, int direction)
75 {
76         proto_tree      *cl_tree = NULL;
77         proto_item      *cl_item = NULL;
78         guint8          text[2048];
79         int             maxbufsize = 0;
80         int             len;
81         int             offset;
82
83         guint32 marker;
84
85         marker = tvb_get_ntohl(tvb, 0);
86         if (tree) {
87                 cl_item = proto_tree_add_text(tree, tvb,
88                                 0, tvb_length(tvb), "Connectionless");
89                 if (cl_item)
90                         cl_tree = proto_item_add_subtree(
91                                 cl_item, ett_quake2_connectionless);
92         }
93
94         if (cl_tree) {
95                 proto_tree_add_uint(cl_tree, hf_quake2_connectionless_marker,
96                                 tvb, 0, 4, marker);
97         }
98
99         /* all the rest of the packet is just text */
100         offset = 4;
101
102         maxbufsize = MIN((gint)sizeof(text), tvb_length_remaining(tvb, offset));
103         len = tvb_get_nstringz0(tvb, offset, maxbufsize, text);
104         if (cl_tree) {
105                 proto_tree_add_string(cl_tree, hf_quake2_connectionless_text,
106                         tvb, offset, len + 1, text);
107         }
108         offset += len + 1;
109
110         /* we should analyse the result 'text' a bit further */
111         /* for this we need the direction parameter */
112 }
113
114
115 static void
116 dissect_quake2_client_commands(tvbuff_t *tvb, packet_info *pinfo,
117         proto_tree *tree)
118 {
119         /* If I have too much time at hand, I'll fill it with all
120            the information from my DM2 specs:
121                 http://www.planetquake.com/demospecs/dm2/
122         */
123         dissect_data(tvb, 0, pinfo, tree);
124 }
125
126
127 static void
128 dissect_quake2_server_commands(tvbuff_t *tvb, packet_info *pinfo,
129         proto_tree *tree)
130 {
131         /* If I have too much time at hand, I'll fill it with all
132            the information from my DM2 specs:
133                 http://www.planetquake.com/demospecs/dm2/
134         */
135         dissect_data(tvb, 0, pinfo, tree);
136 }
137
138
139 static const value_string names_reliable[] = {
140         { 0, "Non Reliable" },
141         { 1, "Reliable" },
142         { 0, NULL }
143 };
144
145
146 static const value_string names_direction[] = {
147 #define DIR_C2S 0
148         { DIR_C2S, "Client to Server" },
149 #define DIR_S2C 1
150         { DIR_S2C, "Server to Client" },
151         { 0, NULL }
152 };
153
154
155 static void
156 dissect_quake2_GamePacket(tvbuff_t *tvb, packet_info *pinfo,
157         proto_tree *tree, int direction)
158 {
159         proto_tree      *game_tree = NULL;
160         proto_item      *game_item = NULL;
161         guint32 seq1;
162         guint32 seq2;
163         int rel1;
164         int rel2;
165         int offset;
166         guint           rest_length;
167
168         direction = (pinfo->destport == gbl_quake2ServerPort) ?
169                         DIR_C2S : DIR_S2C;
170
171         if (tree) {
172                 game_item = proto_tree_add_text(tree, tvb,
173                                 0, tvb_length(tvb), "Game");
174                 if (game_item)
175                         game_tree = proto_item_add_subtree(
176                                 game_item, ett_quake2_game);
177         }
178
179         offset = 0;
180
181         seq1 = tvb_get_letohl(tvb, offset);
182         rel1 = seq1 & 0x80000000 ? 1 : 0;
183         seq1 &= ~0x80000000;
184         if (game_tree) {
185                 proto_item *seq1_item = proto_tree_add_text(game_tree,
186                         tvb, offset, 4, "Current Sequence: %u (%s)",
187                         seq1, val_to_str(rel1,names_reliable,"%u"));
188                 if (seq1_item) {
189                         proto_tree *seq1_tree = proto_item_add_subtree(
190                                 seq1_item, ett_quake2_game_seq1);
191                         proto_tree_add_uint(seq1_tree, hf_quake2_game_seq1,
192                                         tvb, offset, 4, seq1);
193                         proto_tree_add_boolean(seq1_tree, hf_quake2_game_rel1,
194                                         tvb, offset+3, 1, rel1);
195                 }
196         }
197         offset += 4;
198
199         seq2 = tvb_get_letohl(tvb, offset);
200         rel2 = seq2 & 0x80000000 ? 1 : 0;
201         seq2 &= ~0x80000000;
202         if (game_tree) {
203                 proto_item *seq2_item = proto_tree_add_text(game_tree,
204                         tvb, offset, 4, "Acknowledge Sequence: %u (%s)",
205                         seq2, val_to_str(rel2,names_reliable,"%u"));;
206                 if (seq2_item) {
207                         proto_tree *seq2_tree = proto_item_add_subtree(
208                                 seq2_item, ett_quake2_game_seq2);
209                         proto_tree_add_uint(seq2_tree, hf_quake2_game_seq2,
210                                         tvb, offset, 4, seq2);
211                         proto_tree_add_boolean(seq2_tree, hf_quake2_game_rel2,
212                                         tvb, offset+3, 1, rel2);
213                 }
214         }
215         offset += 4;
216
217         if (direction == DIR_C2S) {
218                 /* client to server */
219                 guint16 qport = tvb_get_letohs(tvb, offset);
220                 if (game_tree) {
221                         proto_tree_add_uint(game_tree, hf_quake2_game_qport, 
222                                 tvb, offset, 2, qport);
223                 }
224                 offset +=2;
225         }
226
227         /* all the rest is pure game data */
228         rest_length = tvb_reported_length(tvb) - offset;
229         if (rest_length) {
230                 tvbuff_t *next_tvb =
231                 tvb_new_subset(tvb, offset, rest_length , rest_length);
232
233                 if (direction == DIR_C2S) {
234                         proto_item *c_item = NULL;
235                         proto_tree *c_tree = NULL;
236                         if (tree) {
237                                 c_item = proto_tree_add_text(game_tree, next_tvb,
238                                 0, tvb_length(next_tvb),
239                                 "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, tvb_length(next_tvb),
253                                 "Server Commands");
254                                 if (c_item) {
255                                         c_tree = proto_item_add_subtree(
256                                         c_item, ett_quake2_game_svc);
257                                 }
258                         }
259                         dissect_quake2_server_commands(next_tvb, pinfo, c_tree);
260                 }
261         }
262 }
263
264
265 static void
266 dissect_quake2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
267 {
268         proto_tree      *quake2_tree = NULL;
269         proto_item      *quake2_item = NULL;
270         int             direction;
271
272         /*
273          * XXX - this is a conversation dissector, and the code to
274          * call a conversation dissector doesn't check for disabled
275          * protocols or set "pinfo->current_proto".
276          */
277         CHECK_DISPLAY_AS_DATA(proto_quake2, tvb, pinfo, tree);
278
279         direction = (pinfo->destport == gbl_quake2ServerPort) ?
280                         DIR_C2S : DIR_S2C;
281
282         pinfo->current_proto = "QUAKE2";
283
284         if (check_col(pinfo->fd, COL_PROTOCOL))
285                 col_set_str(pinfo->fd, COL_PROTOCOL, "QUAKE2");
286         if (check_col(pinfo->fd, COL_INFO))
287                 col_set_str(pinfo->fd, COL_INFO, val_to_str(direction,
288                         names_direction, "%u"));
289
290         if (tree) {
291                 quake2_item = proto_tree_add_item(tree, proto_quake2,
292                                 tvb, 0, tvb_length(tvb), FALSE);
293                 if (quake2_item)
294                         quake2_tree = proto_item_add_subtree(
295                                 quake2_item, ett_quake2);
296                         if (quake2_tree) {
297                                 proto_tree_add_uint_format(quake2_tree,
298                                         direction == DIR_S2C ?
299                                         hf_quake2_s2c :
300                                         hf_quake2_c2s,
301                                         tvb, 0, 0, 1,
302                                         "Direction: %s", val_to_str(direction, names_direction, "%u"));
303                         }
304         }
305
306         if (tvb_get_ntohl(tvb, 0) == 0xffffffff) {
307                 if (check_col(pinfo->fd, COL_INFO)) {
308                         col_append_str(pinfo->fd, COL_INFO, " Connectionless");
309                 }
310                 if (quake2_tree)
311                         proto_tree_add_uint_format(quake2_tree,
312                                 hf_quake2_connectionless,
313                                 tvb, 0, 0, 1,
314                                 "Type: Connectionless");
315                 dissect_quake2_ConnectionlessPacket(
316                         tvb, pinfo, quake2_tree, direction);
317         }
318         else {
319                 if (check_col(pinfo->fd, COL_INFO)) {
320                         col_append_str(pinfo->fd, COL_INFO, " Game");
321                 }
322                 if (quake2_tree)
323                         proto_tree_add_uint_format(quake2_tree,
324                                 hf_quake2_game,
325                                 tvb, 0, 0, 1,
326                                 "Type: Game");
327                 dissect_quake2_GamePacket(
328                         tvb, pinfo, quake2_tree, direction);
329         }
330 }
331
332
333 void
334 proto_reg_handoff_quake2(void)
335 {
336         static int Initialized=FALSE;
337         static int ServerPort=0;
338  
339         if (Initialized) {
340                 dissector_delete("udp.port", ServerPort, dissect_quake2);
341         } else {
342                 Initialized=TRUE;
343         }
344  
345         /* set port for future deletes */
346         ServerPort=gbl_quake2ServerPort;
347  
348         dissector_add("udp.port", gbl_quake2ServerPort,
349                         dissect_quake2, proto_quake2);
350 }
351
352
353 void
354 proto_register_quake2(void)
355 {
356         static hf_register_info hf[] = {
357                 { &hf_quake2_c2s,
358                         { "Client to Server", "quake2.c2s",
359                         FT_UINT32, BASE_DEC, NULL, 0x0,
360                         "Client to Server", HFILL }},
361                 { &hf_quake2_s2c,
362                         { "Server to Client", "quake2.s2c",
363                         FT_UINT32, BASE_DEC, NULL, 0x0,
364                         "Server to Client", HFILL }},
365                 { &hf_quake2_connectionless,
366                         { "Connectionless", "quake2.connectionless",
367                         FT_UINT32, BASE_DEC, NULL, 0x0,
368                         "Connectionless", HFILL }},
369                 { &hf_quake2_game,
370                         { "Game", "quake2.game",
371                         FT_UINT32, BASE_DEC, NULL, 0x0,
372                         "Game", HFILL }},
373                 { &hf_quake2_connectionless_marker,
374                         { "Marker", "quake2.connectionless.marker",
375                         FT_UINT32, BASE_HEX, NULL, 0x0,
376                         "Marker", HFILL }},
377                 { &hf_quake2_connectionless_text,
378                         { "Text", "quake2.connectionless.text",
379                         FT_STRING, BASE_DEC, NULL, 0x0,
380                         "Text", HFILL }},
381                 { &hf_quake2_game_seq1,
382                         { "Sequence Number", "quake2.game.seq1",
383                         FT_UINT32, BASE_DEC, NULL, 0x0,
384                         "Sequence number of the current packet", HFILL }},
385                 { &hf_quake2_game_rel1,
386                         { "Reliable", "quake2.game.rel1",
387                         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
388                         "Packet is reliable and may be retransmitted", HFILL }},
389                 { &hf_quake2_game_seq2,
390                         { "Sequence Number", "quake2.game.seq2",
391                         FT_UINT32, BASE_DEC, NULL, 0x0,
392                         "Sequence number of the last received packet", HFILL }},
393                 { &hf_quake2_game_rel2,
394                         { "Reliable", "quake2.game.rel2",
395                         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
396                         "Packet was reliable and may be retransmitted", HFILL }},
397                 { &hf_quake2_game_qport,
398                         { "QPort", "quake2.game.qport",
399                         FT_UINT32, BASE_DEC, NULL, 0x0,
400                         "Quake II Client Port", HFILL }}
401         };
402         static gint *ett[] = {
403                 &ett_quake2,
404                 &ett_quake2_connectionless,
405                 &ett_quake2_game,
406                 &ett_quake2_game_seq1,
407                 &ett_quake2_game_seq2,
408                 &ett_quake2_game_clc,
409                 &ett_quake2_game_svc
410         };
411         module_t *quake2_module;
412
413         proto_quake2 = proto_register_protocol("Quake II Network Protocol",
414                                                 "QUAKE2", "quake2");
415         proto_register_field_array(proto_quake2, hf, array_length(hf));
416         proto_register_subtree_array(ett, array_length(ett));
417
418         /* Register a configuration option for port */
419         quake2_module = prefs_register_protocol(proto_quake2,
420                 proto_reg_handoff_quake2);
421         prefs_register_uint_preference(quake2_module, "udp.port",
422                                         "Quake II Server UDP Port",
423                                         "Set the UDP port for the Quake II Server",
424                                         10, &gbl_quake2ServerPort);
425 }
426