2 * Routines for Quake II packet dissection
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
10 * $Id: packet-quake2.c,v 1.3 2001/11/25 22:19:24 hagbard Exp $
12 * Ethereal - Network traffic analyzer
13 * By Gerald Combs <gerald@zing.org>
14 * Copyright 1998 Gerald Combs
16 * Copied from packet-quakeworld.c
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.
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.
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.
37 #ifdef HAVE_SYS_TYPES_H
38 # include <sys/types.h>
45 static int proto_quake2 = -1;
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;
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;
67 static dissector_handle_t data_handle;
69 #define PORT_MASTER 27910
70 static unsigned int gbl_quake2ServerPort=PORT_MASTER;
74 dissect_quake2_ConnectionlessPacket(tvbuff_t *tvb, packet_info *pinfo,
75 proto_tree *tree, int direction)
77 proto_tree *cl_tree = NULL;
78 proto_item *cl_item = NULL;
86 marker = tvb_get_ntohl(tvb, 0);
88 cl_item = proto_tree_add_text(tree, tvb,
89 0, tvb_length(tvb), "Connectionless");
91 cl_tree = proto_item_add_subtree(
92 cl_item, ett_quake2_connectionless);
96 proto_tree_add_uint(cl_tree, hf_quake2_connectionless_marker,
100 /* all the rest of the packet is just text */
103 maxbufsize = MIN((gint)sizeof(text), tvb_length_remaining(tvb, offset));
104 len = tvb_get_nstringz0(tvb, offset, maxbufsize, text);
106 proto_tree_add_string(cl_tree, hf_quake2_connectionless_text,
107 tvb, offset, len + 1, text);
111 /* we should analyse the result 'text' a bit further */
112 /* for this we need the direction parameter */
117 dissect_quake2_client_commands(tvbuff_t *tvb, packet_info *pinfo,
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/
124 call_dissector(data_handle,tvb, pinfo, tree);
129 dissect_quake2_server_commands(tvbuff_t *tvb, packet_info *pinfo,
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/
136 call_dissector(data_handle,tvb, pinfo, tree);
140 static const value_string names_reliable[] = {
141 { 0, "Non Reliable" },
147 static const value_string names_direction[] = {
149 { DIR_C2S, "Client to Server" },
151 { DIR_S2C, "Server to Client" },
157 dissect_quake2_GamePacket(tvbuff_t *tvb, packet_info *pinfo,
158 proto_tree *tree, int direction)
160 proto_tree *game_tree = NULL;
161 proto_item *game_item = NULL;
169 direction = (pinfo->destport == gbl_quake2ServerPort) ?
173 game_item = proto_tree_add_text(tree, tvb,
174 0, tvb_length(tvb), "Game");
176 game_tree = proto_item_add_subtree(
177 game_item, ett_quake2_game);
182 seq1 = tvb_get_letohl(tvb, offset);
183 rel1 = seq1 & 0x80000000 ? 1 : 0;
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"));
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);
200 seq2 = tvb_get_letohl(tvb, offset);
201 rel2 = seq2 & 0x80000000 ? 1 : 0;
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"));;
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);
218 if (direction == DIR_C2S) {
219 /* client to server */
220 guint16 qport = tvb_get_letohs(tvb, offset);
222 proto_tree_add_uint(game_tree, hf_quake2_game_qport,
223 tvb, offset, 2, qport);
228 /* all the rest is pure game data */
229 rest_length = tvb_reported_length(tvb) - offset;
232 tvb_new_subset(tvb, offset, rest_length , rest_length);
234 if (direction == DIR_C2S) {
235 proto_item *c_item = NULL;
236 proto_tree *c_tree = NULL;
238 c_item = proto_tree_add_text(game_tree, next_tvb,
239 0, tvb_length(next_tvb),
242 c_tree = proto_item_add_subtree(
243 c_item, ett_quake2_game_clc);
246 dissect_quake2_client_commands(next_tvb, pinfo, c_tree);
249 proto_item *c_item = NULL;
250 proto_tree *c_tree = NULL;
252 c_item = proto_tree_add_text(game_tree, next_tvb,
253 0, tvb_length(next_tvb),
256 c_tree = proto_item_add_subtree(
257 c_item, ett_quake2_game_svc);
260 dissect_quake2_server_commands(next_tvb, pinfo, c_tree);
267 dissect_quake2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
269 proto_tree *quake2_tree = NULL;
270 proto_item *quake2_item = NULL;
274 * XXX - this is a conversation dissector, and the code to
275 * call a conversation dissector doesn't check for disabled
276 * protocols or set "pinfo->current_proto".
278 CHECK_DISPLAY_AS_DATA(proto_quake2, tvb, pinfo, tree);
280 direction = (pinfo->destport == gbl_quake2ServerPort) ?
283 pinfo->current_proto = "QUAKE2";
285 if (check_col(pinfo->fd, COL_PROTOCOL))
286 col_set_str(pinfo->fd, COL_PROTOCOL, "QUAKE2");
287 if (check_col(pinfo->fd, COL_INFO))
288 col_set_str(pinfo->fd, COL_INFO, val_to_str(direction,
289 names_direction, "%u"));
292 quake2_item = proto_tree_add_item(tree, proto_quake2,
293 tvb, 0, tvb_length(tvb), FALSE);
295 quake2_tree = proto_item_add_subtree(
296 quake2_item, ett_quake2);
298 proto_tree_add_uint_format(quake2_tree,
299 direction == DIR_S2C ?
303 "Direction: %s", val_to_str(direction, names_direction, "%u"));
307 if (tvb_get_ntohl(tvb, 0) == 0xffffffff) {
308 if (check_col(pinfo->fd, COL_INFO)) {
309 col_append_str(pinfo->fd, COL_INFO, " Connectionless");
312 proto_tree_add_uint_format(quake2_tree,
313 hf_quake2_connectionless,
315 "Type: Connectionless");
316 dissect_quake2_ConnectionlessPacket(
317 tvb, pinfo, quake2_tree, direction);
320 if (check_col(pinfo->fd, COL_INFO)) {
321 col_append_str(pinfo->fd, COL_INFO, " Game");
324 proto_tree_add_uint_format(quake2_tree,
328 dissect_quake2_GamePacket(
329 tvb, pinfo, quake2_tree, direction);
335 proto_reg_handoff_quake2(void)
337 static int Initialized=FALSE;
338 static int ServerPort=0;
341 dissector_delete("udp.port", ServerPort, dissect_quake2);
346 /* set port for future deletes */
347 ServerPort=gbl_quake2ServerPort;
349 dissector_add("udp.port", gbl_quake2ServerPort,
350 dissect_quake2, proto_quake2);
351 data_handle = find_dissector("data");
356 proto_register_quake2(void)
358 static hf_register_info hf[] = {
360 { "Client to Server", "quake2.c2s",
361 FT_UINT32, BASE_DEC, NULL, 0x0,
362 "Client to Server", HFILL }},
364 { "Server to Client", "quake2.s2c",
365 FT_UINT32, BASE_DEC, NULL, 0x0,
366 "Server to Client", HFILL }},
367 { &hf_quake2_connectionless,
368 { "Connectionless", "quake2.connectionless",
369 FT_UINT32, BASE_DEC, NULL, 0x0,
370 "Connectionless", HFILL }},
372 { "Game", "quake2.game",
373 FT_UINT32, BASE_DEC, NULL, 0x0,
375 { &hf_quake2_connectionless_marker,
376 { "Marker", "quake2.connectionless.marker",
377 FT_UINT32, BASE_HEX, NULL, 0x0,
379 { &hf_quake2_connectionless_text,
380 { "Text", "quake2.connectionless.text",
381 FT_STRING, BASE_DEC, NULL, 0x0,
383 { &hf_quake2_game_seq1,
384 { "Sequence Number", "quake2.game.seq1",
385 FT_UINT32, BASE_DEC, NULL, 0x0,
386 "Sequence number of the current packet", HFILL }},
387 { &hf_quake2_game_rel1,
388 { "Reliable", "quake2.game.rel1",
389 FT_BOOLEAN, BASE_DEC, NULL, 0x0,
390 "Packet is reliable and may be retransmitted", HFILL }},
391 { &hf_quake2_game_seq2,
392 { "Sequence Number", "quake2.game.seq2",
393 FT_UINT32, BASE_DEC, NULL, 0x0,
394 "Sequence number of the last received packet", HFILL }},
395 { &hf_quake2_game_rel2,
396 { "Reliable", "quake2.game.rel2",
397 FT_BOOLEAN, BASE_DEC, NULL, 0x0,
398 "Packet was reliable and may be retransmitted", HFILL }},
399 { &hf_quake2_game_qport,
400 { "QPort", "quake2.game.qport",
401 FT_UINT32, BASE_DEC, NULL, 0x0,
402 "Quake II Client Port", HFILL }}
404 static gint *ett[] = {
406 &ett_quake2_connectionless,
408 &ett_quake2_game_seq1,
409 &ett_quake2_game_seq2,
410 &ett_quake2_game_clc,
413 module_t *quake2_module;
415 proto_quake2 = proto_register_protocol("Quake II Network Protocol",
417 proto_register_field_array(proto_quake2, hf, array_length(hf));
418 proto_register_subtree_array(ett, array_length(ett));
420 /* Register a configuration option for port */
421 quake2_module = prefs_register_protocol(proto_quake2,
422 proto_reg_handoff_quake2);
423 prefs_register_uint_preference(quake2_module, "udp.port",
424 "Quake II Server UDP Port",
425 "Set the UDP port for the Quake II Server",
426 10, &gbl_quake2ServerPort);