summaryrefslogtreecommitdiff
path: root/plugins/alot/packet-alot.c
blob: e2713d2db22984db683e6f54d491268da5534661 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
#include "config.h"
#include <epan/packet.h>

#define ALOT_PORT 4107

static int proto_alot = -1;

static int hf_roomid = -1;
static int hf_seq_no = -1;
static int hf_mcount = -1;
static int hf_alot_message = -1;
static int hf_alot_messages = -1;
static int hf_mtype = -1;
static int hf_msize = -1;
static int hf_mdata = -1;
static int hf_ip6addr = -1;
static gint ett_alot = -1;
static gint ett_alot_messages = -1;
static gint ett_alot_message = -1;
static gint ett_message_data = -1;

static const guint8 ALOT_MAGIC[] = { 'A', 'L', 'O', 'T' };

/* tr -d '(,' <protocol.txt | awk '/^Message type/{print "#define T_" $4 "\t" $3}' */
#define T_ROOM_BROADCAST	1
#define T_ROOM_JOIN_REQUEST	2
#define T_ROOM_JOIN_CONFIRM	3
#define T_ROOM_MEMBERS_UPDATE	4
#define T_ROOM_LEAVE	6
#define T_ROOM_CLOSED	7
#define T_GAME_JOIN_REQUEST	32
#define T_GAME_JOIN_DECLINE	33
#define T_GAME_JOIN_ACCEPT	34
#define T_GAME_SET_CREATURE	35
#define T_GAME_INIT	42
#define T_GAME_LEAVE	43
#define T_GAME_SPAWN_CREATURE	44
#define T_GAME_MOVE	45
#define T_GAME_ATTACK	46
#define T_GAME_ATTACK_HIT	47
#define T_GAME_PICKUP	48
#define T_TOKEN_OFFER	128

/* tr -d '(,' <protocol.txt | awk '/^Message type/{print "\t{T_" $4 ", \"" $4 "\"},"}' */
static const value_string message_typenames[] = {
	{T_ROOM_BROADCAST, "ROOM_BROADCAST"},
	{T_ROOM_JOIN_REQUEST, "ROOM_JOIN_REQUEST"},
	{T_ROOM_JOIN_CONFIRM, "ROOM_JOIN_CONFIRM"},
	{T_ROOM_MEMBERS_UPDATE, "ROOM_MEMBERS_UPDATE"},
	{T_ROOM_LEAVE, "ROOM_LEAVE"},
	{T_ROOM_CLOSED, "ROOM_CLOSED"},
	{T_GAME_JOIN_REQUEST, "GAME_JOIN_REQUEST"},
	{T_GAME_JOIN_DECLINE, "GAME_JOIN_DECLINE"},
	{T_GAME_JOIN_ACCEPT, "GAME_JOIN_ACCEPT"},
	{T_GAME_SET_CREATURE, "GAME_SET_CREATURE"},
	{T_GAME_INIT, "GAME_INIT"},
	{T_GAME_LEAVE, "GAME_LEAVE"},
	{T_GAME_SPAWN_CREATURE, "GAME_SPAWN_CREATURE"},
	{T_GAME_MOVE, "GAME_MOVE"},
	{T_GAME_ATTACK, "GAME_ATTACK"},
	{T_GAME_ATTACK_HIT, "GAME_ATTACK_HIT"},
	{T_GAME_PICKUP, "GAME_PICKUP"},
	{T_TOKEN_OFFER, "TOKEN_OFFER"},
	{0, NULL}
};

static void
parse_message_payload(proto_tree *mtree, tvbuff_t *tvb, int msgtype, gint pos,
	int msglen)
{
	gint strzsize;
#define M_ADD(len, ...) \
	proto_tree_add_text(mtree, tvb, pos, len, __VA_ARGS__)
#define M_STRZ(name) \
	({ \
		strzsize = tvb_strnlen(tvb, pos, msglen - pos); \
		if (strzsize == -1) { \
			M_ADD(msglen - pos, "MALFORMED: String end not found, pos%i len%i", pos, msglen - pos); \
			return; \
		} \
		M_ADD(strzsize, name ": %s", tvb_format_text(tvb, pos, strzsize)); \
	})
#define M_B8(name) \
	({ \
		guint8 u8; \
		M_ADD(1, name ": %1$#02x (%1$u)", u8 = tvb_get_guint8(tvb, pos)); \
		u8; \
	})
#define M_B16(name) \
	M_ADD(2, name ": %#04x", tvb_get_ntohs(tvb, pos))
#define M_B24(name) \
	M_ADD(3, name ": %#06x", tvb_get_ntoh24(tvb, pos))
#define M_B32(name) \
	M_ADD(4, name ": %#08x", tvb_get_ntohl(tvb, pos))
#define M_FLOAT(name) \
	M_ADD(4, name ": %f", tvb_get_ntohieee_float(tvb, pos))
#define M_IPADDR(desc) \
	({ \
		struct e_in6_addr addr; \
		tvb_get_ipv6(tvb, pos, &addr); \
		proto_tree_add_ipv6(mtree, hf_ip6addr, tvb, pos, 16, addr.bytes); \
	})

	/*
f=packet-alot.c;s=SWITCH_TYPES;b="BEGIN $s";e="END $s"
sed "/$b/,/$e/{/$b/p;/$e/p;d}" -i $f;sed "/$b/r /tmp/cases.txt" -i $f
	*/
	switch (msgtype) { /* BEGIN SWITCH_TYPES */
	} /* END SWITCH_TYPES */

	if (msglen - pos > 0)
		M_ADD(msglen - pos, "(unhandled data)");

#undef M_IPADDR
#undef M_FLOAT
#undef M_B32
#undef M_B24
#undef M_B16
#undef M_B8
#undef M_STRZ
#undef M_ADD
}

static void
parse_message(proto_tree *stree, int _hf_messages_root, tvbuff_t *tvb, gint pos,
	int msgtype, int msglen)
{
	proto_item *mt_item = NULL;
	proto_tree *message_tree = NULL;

	mt_item = proto_tree_add_item(stree, _hf_messages_root, tvb, pos, msglen + 4, ENC_NA);
	message_tree = proto_item_add_subtree(mt_item, ett_alot_message);
	proto_tree_add_item(message_tree, hf_mtype, tvb, pos, 1, ENC_BIG_ENDIAN);
	++pos;
	proto_tree_add_item(message_tree, hf_msize, tvb, pos, 3, ENC_BIG_ENDIAN);
	pos += 3;
	if (msglen) {
		proto_item *di = NULL;
		proto_tree *mtree = NULL;

		di = proto_tree_add_item(message_tree, hf_mdata, tvb, pos, msglen, ENC_BIG_ENDIAN);
		mtree = proto_item_add_subtree(di, ett_message_data);
		parse_message_payload(mtree, tvb, msgtype, pos, pos + msglen);
	}
}


static void
dissect_alot(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
	gint pktlen = tvb_length_remaining(tvb, 0);
	gint pos = 0;
#define CHECK(test, msg) \
	if (!(test)) { \
		proto_tree_add_text(tree, tvb, 0, -1, "MALFORMED: " msg); \
		return; \
	}
#define CHECK_LEN(extra, msg) \
	CHECK(pos + extra <= pktlen, msg)

	if (tvb_memeql(tvb, 0, ALOT_MAGIC, 4) != 0)
		return;

	col_set_str(pinfo->cinfo, COL_PROTOCOL, "ALOT");
	/* Clear out stuff in the info column */
	col_clear(pinfo->cinfo, COL_INFO);

	CHECK_LEN(8, "Packet header");

	if (tree) { /* we are being asked for details */
		proto_item *ti = NULL;
		proto_tree *alot_tree = NULL;
		int _hf_tree_type = hf_alot_message;
		proto_tree *messages_tree = NULL;
		guint8 mcount, i;

		/* root */
		ti = proto_tree_add_item(tree, proto_alot, tvb, 0, -1, ENC_NA);
		alot_tree = proto_item_add_subtree(ti, ett_alot);

		/* packet headers */
		proto_tree_add_item(alot_tree, hf_roomid, tvb, 4, 2, ENC_BIG_ENDIAN);
		proto_tree_add_item(alot_tree, hf_seq_no, tvb, 6, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(alot_tree, hf_mcount, tvb, 7, 1, ENC_BIG_ENDIAN);
		mcount = tvb_get_guint8(tvb, 7);
		col_add_fstr(pinfo->cinfo, COL_INFO, "#messages=%i", mcount);
		CHECK(mcount > 0, "Message count is zero");

		pos += 8;

		/* messages */
		if (mcount == 1) {
			messages_tree = alot_tree;
		} else {
			proto_item *mti = NULL;
			mti = proto_tree_add_item(alot_tree, hf_alot_messages, tvb, 0, -1, ENC_NA);
			messages_tree = proto_item_add_subtree(mti, ett_alot_messages);
			_hf_tree_type = hf_alot_messages;
		}
		for (i = 0; i < mcount; i++) {
			guint8 msgtype;
			int msglen;

			CHECK_LEN(4, "Message header");
			msgtype = tvb_get_guint8(tvb, pos);
			msglen = tvb_get_ntoh24(tvb, pos + 1);
			CHECK_LEN(4 + msglen, "Message payload");
			parse_message(messages_tree, _hf_tree_type, tvb, pos, msgtype, msglen);
			pos += 4 + msglen;
			if (i == 0) {
				col_append_fstr(pinfo->cinfo, COL_INFO, " mtype=%s",
					val_to_str(msgtype, message_typenames, "(unknown:%#02x)"));
			}
		}
	}
#undef CHECK_LEN
#undef CHECK
}

void
proto_register_alot(void)
{
	static hf_register_info hf[] = {
		{ &hf_roomid,
			{ "Room ID", "alot.roomid",
			FT_UINT16, BASE_HEX,
			NULL, 0x0,
			NULL, HFILL }
		},
		{ &hf_seq_no,
			{ "Sequence Number", "alot.seqno",
			FT_UINT8, BASE_HEX,
			NULL, 0x0,
			NULL, HFILL }
		},
		{ &hf_mcount,
			{ "Messages count", "alot.mcount",
			FT_UINT8, BASE_DEC,
			NULL, 0x0,
			NULL, HFILL }
		},
		{ &hf_alot_message,
			{ "Message", "alot.message",
			FT_NONE, BASE_NONE,
			NULL, 0x0,
			NULL, HFILL }
		},
		{ &hf_alot_messages,
			{ "Messages", "alot.messages",
			FT_NONE, BASE_NONE,
			NULL, 0x0,
			NULL, HFILL }
		},
		{ &hf_mtype,
			{ "Message type", "alot.mtype",
			FT_UINT8, BASE_DEC,
			VALS(message_typenames), 0x0,
			NULL, HFILL }
		},
		{ &hf_msize,
			{ "Message length", "alot.mlen",
			FT_UINT24, BASE_DEC,
			NULL, 0x0,
			NULL, HFILL }
		},
		{ &hf_mdata,
			{ "Message data", "alot.mdata",
			FT_BYTES, BASE_NONE,
			NULL, 0x0,
			NULL, HFILL }
		},
		{ &hf_ip6addr,
			{ "IP address", "alot.mdata.ipaddr",
			FT_IPv6, BASE_NONE,
			NULL, 0x0,
			NULL, HFILL }
		},
	};
	/* Setup protocol subtree array */
	static gint *ett[] = {
		&ett_alot,
		&ett_alot_messages,
		&ett_alot_message,
		&ett_message_data,
	};

	proto_alot = proto_register_protocol(
		"ALOT Application Layer Protocol",
		"AALP",
		"alot"
		);
	proto_register_field_array(proto_alot, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));
}

void
proto_reg_handoff_alot(void)
{
	static dissector_handle_t alot_handle;
	alot_handle = create_dissector_handle(dissect_alot, proto_alot);
	dissector_add_uint("udp.port", ALOT_PORT, alot_handle);
	dissector_add_uint("tcp.port", ALOT_PORT, alot_handle);
}