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.9 2002/01/24 09:20:50 guy Exp $
12 * Ethereal - Network traffic analyzer
13 * By Gerald Combs <gerald@ethereal.com>
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>
42 #include <epan/packet.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, -1, "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,
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, -1, "Client Commands");
241 c_tree = proto_item_add_subtree(
242 c_item, ett_quake2_game_clc);
245 dissect_quake2_client_commands(next_tvb, pinfo, c_tree);
248 proto_item *c_item = NULL;
249 proto_tree *c_tree = NULL;
251 c_item = proto_tree_add_text(game_tree, next_tvb,
252 0, -1, "Server Commands");
254 c_tree = proto_item_add_subtree(
255 c_item, ett_quake2_game_svc);
258 dissect_quake2_server_commands(next_tvb, pinfo, c_tree);
265 dissect_quake2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
267 proto_tree *quake2_tree = NULL;
268 proto_item *quake2_item = NULL;
271 direction = (pinfo->destport == gbl_quake2ServerPort) ?
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"));
281 quake2_item = proto_tree_add_item(tree, proto_quake2,
284 quake2_tree = proto_item_add_subtree(
285 quake2_item, ett_quake2);
287 proto_tree_add_uint_format(quake2_tree,
288 direction == DIR_S2C ?
292 "Direction: %s", val_to_str(direction, names_direction, "%u"));
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");
301 proto_tree_add_uint_format(quake2_tree,
302 hf_quake2_connectionless,
304 "Type: Connectionless");
305 dissect_quake2_ConnectionlessPacket(
306 tvb, pinfo, quake2_tree, direction);
309 if (check_col(pinfo->cinfo, COL_INFO)) {
310 col_append_str(pinfo->cinfo, COL_INFO, " Game");
313 proto_tree_add_uint_format(quake2_tree,
317 dissect_quake2_GamePacket(
318 tvb, pinfo, quake2_tree, direction);
324 proto_reg_handoff_quake2(void)
326 static int Initialized=FALSE;
327 static dissector_handle_t quake2_handle;
328 static int ServerPort=0;
331 quake2_handle = create_dissector_handle(dissect_quake2,
335 dissector_delete("udp.port", ServerPort, quake2_handle);
338 /* set port for future deletes */
339 ServerPort=gbl_quake2ServerPort;
341 dissector_add("udp.port", gbl_quake2ServerPort, quake2_handle);
342 data_handle = find_dissector("data");
347 proto_register_quake2(void)
349 static hf_register_info hf[] = {
351 { "Client to Server", "quake2.c2s",
352 FT_UINT32, BASE_DEC, NULL, 0x0,
353 "Client to Server", HFILL }},
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 }},
363 { "Game", "quake2.game",
364 FT_UINT32, BASE_DEC, NULL, 0x0,
366 { &hf_quake2_connectionless_marker,
367 { "Marker", "quake2.connectionless.marker",
368 FT_UINT32, BASE_HEX, NULL, 0x0,
370 { &hf_quake2_connectionless_text,
371 { "Text", "quake2.connectionless.text",
372 FT_STRING, BASE_DEC, NULL, 0x0,
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 }}
395 static gint *ett[] = {
397 &ett_quake2_connectionless,
399 &ett_quake2_game_seq1,
400 &ett_quake2_game_seq2,
401 &ett_quake2_game_clc,
404 module_t *quake2_module;
406 proto_quake2 = proto_register_protocol("Quake II Network Protocol",
408 proto_register_field_array(proto_quake2, hf, array_length(hf));
409 proto_register_subtree_array(ett, array_length(ett));
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);