Coverage Report

Created: 2026-09-28 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-armagetronad.c
Line
Count
Source
1
/* packet-armagetronad.c
2
 * Routines for the Armagetronad packet dissection
3
 * Copyright 2005, Guillaume Chazarain <guichaz@yahoo.fr>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
#include "config.h"
13
14
#include <epan/packet.h>
15
void proto_register_armagetronad(void);
16
void proto_reg_handoff_armagetronad(void);
17
18
/* Initialize the protocol and registered fields */
19
static int proto_armagetronad;
20
static int hf_armagetronad_descriptor_id;
21
static int hf_armagetronad_message_id;
22
static int hf_armagetronad_data_len;
23
static int hf_armagetronad_data;
24
static int hf_armagetronad_sender_id;
25
static int hf_armagetronad_msg_subtree;
26
27
/* Initialize the subtree pointers */
28
static int ett_armagetronad;
29
static int ett_message;
30
31
static dissector_handle_t armagetronad_handle;
32
33
16
#define ARMAGETRONAD_UDP_PORT_RANGE "4533-4534" /* 4533 is not IANA registered, 4534 is */
34
35
/*
36
 * The ACK packet is so common that we treat it
37
 * differently: it has no MessageID
38
 */
39
15
#define ACK 1
40
41
/*
42
 * armagetronad-0.2.8.2.1
43
 * The numbers and names were retrieved at runtime using the
44
 * 'nDescriptor* descriptors[MAXDESCRIPTORS]' array
45
 */
46
static const value_string descriptors[] = {
47
  {  1, "ack"},
48
  {  2, "req_info"},
49
  {  3, "login_deny"},
50
  {  4, "login_ignore"},
51
  {  5, "login_accept"},
52
  {  6, "login1"},
53
  {  7, "logout"},
54
  {  8, "sn_ConsoleOut"},
55
  {  9, "client_cen"},
56
  { 10, "version"},
57
  { 11, "login2"},
58
  { 20, "req_id"},
59
  { 21, "id_req_handler"},
60
  { 22, "net_destroy"},
61
  { 23, "net_control"},
62
  { 24, "net_sync"},
63
  { 25, "ready to get objects"},
64
  { 26, "net_clear"},
65
  { 27, "sync_ack"},
66
  { 28, "sync_msg"},
67
  { 40, "password_request"},
68
  { 41, "password_answer"},
69
  { 50, "small_server"},
70
  { 51, "big_server"},
71
  { 52, "small_request"},
72
  { 53, "big_request"},
73
  { 54, "big_server_master"},
74
  { 55, "big_request_master"},
75
  { 60, "transfer config"},
76
  {200, "Chat"},
77
  {201, "ePlayerNetID"},
78
  {202, "player_removed_from_game"},
79
  {203, "Chat Client"},
80
  {210, "eTimer"},
81
  {220, "eTeam"},
82
  {230, "vote cast"},
83
  {231, "Kick vote"},
84
  {232, "Server controlled vote"},
85
  {233, "Server controlled vote expired"},
86
  {300, "gNetPlayerWall"},
87
  {310, "game"},
88
  {311, "client_gamestate"},
89
  {320, "cycle"},
90
  {321, "destination"},
91
  {330, "gAIPlayer"},
92
  {331, "gAITeam"},
93
  {340, "zone"},
94
  {0, NULL}
95
};
96
97
static bool
98
is_armagetronad_packet(tvbuff_t * tvb)
99
40
{
100
40
  unsigned offset = 0;
101
102
  /* For each message in the frame */
103
116
  while (tvb_captured_length_remaining(tvb, offset) > 2) {
104
106
    int data_len = tvb_get_ntohs(tvb, offset + 4) * 2;
105
106
#if 0
107
    /*
108
     * If the descriptor_id is not in the table it's possibly
109
     * because the protocol evoluated, losing synchronization
110
     * with the table, that's why we don't consider that as
111
     * a heuristic
112
     */
113
    if (!try_val_to_str(tvb_get_ntohs(tvb, offset), descriptors))
114
      /* DescriptorID not found in the table */
115
      return false;
116
#endif
117
118
106
    if (!tvb_bytes_exist(tvb, offset + 6, data_len))
119
      /* Advertised length too long */
120
30
      return false;
121
122
76
    offset += 6 + data_len;
123
76
  }
124
125
  /* The packed should end with a 2 bytes ID */
126
10
  return tvb_captured_length_remaining(tvb, offset) == 2;
127
40
}
128
129
static void
130
add_message_data(tvbuff_t * tvb, unsigned offset, int data_len, proto_tree * tree)
131
15
{
132
15
  if (!tree)
133
0
    return;
134
135
  /*
136
   * XXX - if these are text strings, as the original dissector
137
   * treated them as, why the byte swapping?  That would make
138
   * sense only if they're UCS-2 strings, but the rest of the
139
   * code wasn't treating them as such.
140
   *
141
   * Just treat it as a byte array.  If that's wrong, submit
142
   * a change, with a sample packet, that treats it as whatever
143
   * it is, an with a comment that *states* what it is.
144
   *
145
   * XXX - this claimed that "Armagetronad swaps unconditionally",
146
   * but I'm not seeing any obvious unconditional byte-swapping
147
   * in the code, and if somebody's never seen a big-endian
148
   * machine in their life, they may well confuse "convert to
149
   * network byte order" with "unconditionally swap".  So
150
   * if you want to dump this out as a sequence of shorts,
151
   * fetch the shorts one at a time using ENC_BIG_ENDIAN.
152
   */
153
15
  proto_tree_add_item(tree, hf_armagetronad_data, tvb, offset,
154
15
            data_len, ENC_NA);
155
15
}
156
157
static int
158
add_message(tvbuff_t * tvb, packet_info* pinfo, unsigned offset, proto_tree * tree, wmem_strbuf_t * info)
159
15
{
160
15
  uint16_t     descriptor_id, message_id;
161
15
  int          data_len;
162
15
  proto_item  *msg;
163
15
  proto_tree  *msg_tree;
164
15
  const char *descriptor;
165
166
15
  descriptor_id = tvb_get_ntohs(tvb, offset);
167
15
  message_id = tvb_get_ntohs(tvb, offset + 2);
168
15
  data_len = tvb_get_ntohs(tvb, offset + 4) * 2;
169
170
  /* Message subtree */
171
15
  descriptor = val_to_str(pinfo->pool, descriptor_id, descriptors, "Unknown (%u)");
172
15
  if (descriptor_id == ACK)
173
0
    msg = proto_tree_add_none_format(tree,
174
0
             hf_armagetronad_msg_subtree,
175
0
             tvb, offset, data_len + 6,
176
0
             "ACK %d messages",
177
0
             data_len / 2);
178
15
  else
179
15
    msg = proto_tree_add_none_format(tree,
180
15
             hf_armagetronad_msg_subtree,
181
15
             tvb, offset, data_len + 6,
182
15
             "Message 0x%04x [%s]",
183
15
             message_id, descriptor);
184
185
15
  msg_tree = proto_item_add_subtree(msg, ett_message);
186
187
  /* DescriptorID field */
188
15
  proto_tree_add_item(msg_tree, hf_armagetronad_descriptor_id, tvb,
189
15
          offset, 2, ENC_BIG_ENDIAN);
190
15
  if (info)
191
15
    wmem_strbuf_append_printf(info, "%s, ", descriptor);
192
193
  /* MessageID field */
194
15
  proto_tree_add_item(msg_tree, hf_armagetronad_message_id, tvb,
195
15
          offset + 2, 2, ENC_BIG_ENDIAN);
196
197
  /* DataLen field */
198
15
  proto_tree_add_item(msg_tree, hf_armagetronad_data_len, tvb,
199
15
          offset + 4, 2, ENC_BIG_ENDIAN);
200
201
  /* Data field */
202
15
  add_message_data(tvb, offset + 6, data_len, msg_tree);
203
204
15
  return data_len + 6;
205
15
}
206
207
/* Code to actually dissect the packets */
208
static int
209
dissect_armagetronad(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, void * data _U_)
210
40
{
211
40
  proto_item    *ti;
212
40
  proto_tree    *armagetronad_tree;
213
40
  uint16_t       sender;
214
40
  int            offset = 0;
215
40
  wmem_strbuf_t *info;
216
40
  size_t         new_len;
217
218
40
  if (!is_armagetronad_packet(tvb))
219
33
    return 0;
220
221
7
  info = wmem_strbuf_new(pinfo->pool, "");
222
223
7
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "Armagetronad");
224
225
7
  col_clear(pinfo->cinfo, COL_INFO);
226
227
7
  ti = proto_tree_add_item(tree, proto_armagetronad, tvb, 0, -1, ENC_NA);
228
7
  armagetronad_tree = proto_item_add_subtree(ti, ett_armagetronad);
229
230
  /* For each message in the frame */
231
22
  while (tvb_reported_length_remaining(tvb, offset) > 2)
232
15
    offset += add_message(tvb, pinfo, offset, armagetronad_tree, info);
233
234
  /* After the messages, comes the SenderID */
235
7
  sender = tvb_get_ntohs(tvb, offset);
236
7
  proto_tree_add_item(ti, hf_armagetronad_sender_id, tvb, offset, 2,
237
7
          ENC_BIG_ENDIAN);
238
239
7
  new_len = wmem_strbuf_get_len(info) - 2;  /* Remove the trailing ", " */
240
7
  if (new_len > 0)
241
3
    wmem_strbuf_truncate(info, new_len);
242
4
  else
243
4
    info = wmem_strbuf_new(pinfo->pool, "No message");
244
245
7
  col_add_fstr(pinfo->cinfo, COL_INFO, "[%s] from 0x%04x",
246
7
         wmem_strbuf_get_str(info), sender);
247
248
7
  return offset + 2;
249
40
}
250
251
void proto_register_armagetronad(void)
252
16
{
253
16
  static hf_register_info hf[] = {
254
16
    {&hf_armagetronad_descriptor_id,
255
16
     {"Descriptor", "armagetronad.descriptor_id",
256
16
      FT_UINT16, BASE_DEC, VALS(descriptors), 0x0,
257
16
      "The ID of the descriptor (the command)", HFILL}
258
16
     },
259
16
    {&hf_armagetronad_message_id,
260
16
     {"MessageID", "armagetronad.message_id",
261
16
      FT_UINT16, BASE_HEX, NULL, 0x0,
262
16
      "The ID of the message (to ack it)", HFILL}
263
16
     },
264
16
    {&hf_armagetronad_data_len,
265
16
     {"DataLen", "armagetronad.data_len",
266
16
      FT_UINT16, BASE_DEC, NULL, 0x0,
267
16
      "The length of the data (in shorts)", HFILL}
268
16
     },
269
16
    {&hf_armagetronad_data,
270
16
     {"Data", "armagetronad.data",
271
16
      FT_BYTES, BASE_NONE, NULL, 0x0,
272
16
      "The actual data (array of shorts in network order)", HFILL}
273
16
     },
274
16
    {&hf_armagetronad_sender_id,
275
16
     {"SenderID", "armagetronad.sender_id",
276
16
      FT_UINT16, BASE_HEX, NULL, 0x0,
277
16
      "The ID of the sender (0x0000 for the server)", HFILL}
278
16
     },
279
16
    {&hf_armagetronad_msg_subtree,
280
16
     {"Message", "armagetronad.message",
281
16
      FT_NONE, BASE_NONE, NULL, 0x0,
282
16
      "A message", HFILL}
283
16
     }
284
16
  };
285
286
16
  static int *ett[] = {
287
16
    &ett_armagetronad,
288
16
    &ett_message
289
16
  };
290
291
16
  proto_armagetronad = proto_register_protocol("The Armagetron Advanced OpenGL Tron clone", "Armagetronad", "armagetronad");
292
293
16
  proto_register_field_array(proto_armagetronad, hf, array_length(hf));
294
16
  proto_register_subtree_array(ett, array_length(ett));
295
16
  armagetronad_handle = register_dissector("armagetronad", dissect_armagetronad, proto_armagetronad);
296
16
}
297
298
void proto_reg_handoff_armagetronad(void)
299
16
{
300
16
  dissector_add_uint_range_with_preference("udp.port", ARMAGETRONAD_UDP_PORT_RANGE, armagetronad_handle);
301
16
}
302
303
/*
304
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
305
 *
306
 * Local variables:
307
 * c-basic-offset: 8
308
 * tab-width: 8
309
 * indent-tabs-mode: t
310
 * End:
311
 *
312
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
313
 * :indentSize=8:tabSize=8:noTabs=false:
314
 */