aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-rtcdc.c
blob: 843902303da5bd6b6c36e15949a02b78dbb2ec9e (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
/* packet-rtcdc.c
 * Routines for the RTCWeb Data Channel Protocol dissection
 * as specified in 
 * http://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-03
 *
 * Copyright 2012, Michael Tuexen <tuexen@wireshark.org>
 *
 * $Id$
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "config.h"

#include <glib.h>

#include <epan/expert.h>
#include <epan/packet.h>
#include <epan/prefs.h>

void proto_reg_handoff_rtcdc(void);

/* PPID used for this protocol */
static guint32 rtcdc_ppid = 50;

/* Initialize the protocol and registered fields */
static int proto_rtcdc = -1;
static int hf_message_type = -1;
static int hf_channel_type = -1;
static int hf_flags = -1;
static int hf_flags_reserved = -1;
static int hf_unordered_allowed = -1;
static int hf_reliability = -1;
static int hf_priority = -1;
static int hf_label = -1;
static int hf_error = -1;
static int hf_sid = -1;

/* Initialize the subtree pointers */
static gint ett_rtcdc = -1;
static gint ett_flags = -1;

#define DATA_CHANNEL_OPEN_REQUEST   0x00
#define DATA_CHANNEL_OPEN_RESPONSE  0x01
#define DATA_CHANNEL_ACK            0x02

static const value_string message_type_values[] = {
	{ DATA_CHANNEL_OPEN_REQUEST,  "DATA_CHANNEL_OPEN_REQUEST"  },
	{ DATA_CHANNEL_OPEN_RESPONSE, "DATA_CHANNEL_OPEN_RESPONSE" },
	{ DATA_CHANNEL_ACK,           "DATA_CHANNEL_ACK"           },
	{ 0,                          NULL                         }
};

#define DATA_CHANNEL_RELIABLE                0x00
#define DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT 0x01
#define DATA_CHANNEL_PARTIAL_RELIABLE_TIMED  0x02

static const value_string channel_type_values[] = {
	{ DATA_CHANNEL_RELIABLE,                "DATA_CHANNEL_RELIABLE"                },
	{ DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT, "DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT" },
	{ DATA_CHANNEL_PARTIAL_RELIABLE_TIMED,  "DATA_CHANNEL_PARTIAL_RELIABLE_TIMED"  },
	{ 0,                                    NULL                                   }
};

#define MESSAGE_TYPE_LENGTH 1
#define CHANNEL_TYPE_LENGTH 1
#define FLAGS_LENGTH        2
#define RELIABILITY_LENGTH  2
#define PRIORITY_LENGTH     2

#define MESSAGE_TYPE_OFFSET 0
#define CHANNEL_TYPE_OFFSET (MESSAGE_TYPE_OFFSET + MESSAGE_TYPE_LENGTH)
#define FLAGS_OFFSET        (CHANNEL_TYPE_OFFSET + CHANNEL_TYPE_LENGTH)
#define RELIABILITY_OFFSET  (FLAGS_OFFSET + FLAGS_LENGTH)
#define PRIORITY_OFFSET     (RELIABILITY_OFFSET + RELIABILITY_LENGTH)
#define LABEL_OFFSET        (PRIORITY_OFFSET + PRIORITY_LENGTH)

#define DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED_MASK 0x0001
#define DATA_CHANNEL_FLAG_RESERVED_MASK             0xFFFE

static void
dissect_open_request_message(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *rtcdc_tree, proto_item *rtcdc_item _U_) {
	if (rtcdc_tree) {
		proto_tree *flags_tree;
		proto_item *flags_item;

		proto_tree_add_item(rtcdc_tree, hf_channel_type, tvb, CHANNEL_TYPE_OFFSET, CHANNEL_TYPE_LENGTH, ENC_BIG_ENDIAN);
		flags_item = proto_tree_add_item(rtcdc_tree, hf_flags, tvb, FLAGS_OFFSET, FLAGS_LENGTH, ENC_BIG_ENDIAN);
		flags_tree = proto_item_add_subtree(flags_item, ett_flags);
		proto_tree_add_item(flags_tree, hf_flags_reserved, tvb, FLAGS_OFFSET, FLAGS_LENGTH, ENC_BIG_ENDIAN);
		proto_tree_add_item(flags_tree, hf_unordered_allowed, tvb, FLAGS_OFFSET, FLAGS_LENGTH, ENC_BIG_ENDIAN);
		proto_tree_add_item(rtcdc_tree, hf_reliability, tvb, RELIABILITY_OFFSET, RELIABILITY_LENGTH, ENC_BIG_ENDIAN);
		proto_tree_add_item(rtcdc_tree, hf_priority, tvb, PRIORITY_OFFSET, PRIORITY_LENGTH, ENC_BIG_ENDIAN);
		proto_tree_add_item(rtcdc_tree, hf_label, tvb, LABEL_OFFSET, -1, ENC_BIG_ENDIAN);
	}
	return;
}

#define ERROR_LENGTH                 1
#define SID_LENGTH                   2
#define DATA_CHANNEL_RESPONSE_LENGTH (MESSAGE_TYPE_LENGTH + ERROR_LENGTH + FLAGS_LENGTH + SID_LENGTH)

#define ERROR_OFFSET                 (MESSAGE_TYPE_OFFSET + MESSAGE_TYPE_LENGTH)
#define SID_OFFSET                   (FLAGS_OFFSET + FLAGS_LENGTH)

static void
dissect_open_response_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *rtcdc_tree, proto_item *rtcdc_item) {
	if (tvb_length(tvb) > DATA_CHANNEL_RESPONSE_LENGTH) {
		expert_add_info_format(pinfo, rtcdc_item, PI_MALFORMED, PI_ERROR, "Message too long");
	}
	if (rtcdc_tree) {
		proto_tree_add_item(rtcdc_tree, hf_error, tvb, ERROR_OFFSET, ERROR_LENGTH, ENC_BIG_ENDIAN);
		proto_tree_add_item(rtcdc_tree, hf_flags, tvb, FLAGS_OFFSET, FLAGS_LENGTH, ENC_BIG_ENDIAN);
		proto_tree_add_item(rtcdc_tree, hf_sid, tvb, SID_OFFSET, SID_LENGTH, ENC_BIG_ENDIAN);
	}
	return;
}

#define DATA_CHANNEL_ACK_LENGTH MESSAGE_TYPE_LENGTH

static void
dissect_open_ack_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *rtcdc_tree _U_, proto_item *rtcdc_item) {
	if (tvb_length(tvb) > DATA_CHANNEL_ACK_LENGTH) {
		expert_add_info_format(pinfo, rtcdc_item, PI_MALFORMED, PI_ERROR, "Message too long");
	}
	return;
}

static int
dissect_rtcdc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
	proto_item *rtcdc_item;
	proto_tree *rtcdc_tree;
	guint8 message_type;

	message_type  = tvb_get_guint8(tvb, MESSAGE_TYPE_OFFSET);

	col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTCDC");
	col_add_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str_const(message_type, message_type_values, "reserved"));
	if (tree) {
		rtcdc_item = proto_tree_add_item(tree, proto_rtcdc, tvb, 0, -1, ENC_NA);
		rtcdc_tree = proto_item_add_subtree(rtcdc_item, ett_rtcdc);
		proto_tree_add_item(rtcdc_tree, hf_message_type, tvb, MESSAGE_TYPE_OFFSET, MESSAGE_TYPE_LENGTH, ENC_BIG_ENDIAN);
	} else {
		rtcdc_item = NULL;
		rtcdc_tree = NULL;
	}
	switch (message_type) {
	case DATA_CHANNEL_OPEN_REQUEST:
		dissect_open_request_message(tvb, pinfo, rtcdc_tree, rtcdc_item);
		break;
	case DATA_CHANNEL_OPEN_RESPONSE:
		dissect_open_response_message(tvb, pinfo, rtcdc_tree, rtcdc_item);
		break;
	case DATA_CHANNEL_ACK:
		dissect_open_ack_message(tvb, pinfo, rtcdc_tree, rtcdc_item);
		break;
	default:
		expert_add_info_format(pinfo, rtcdc_item, PI_MALFORMED, PI_ERROR, "Unknown message type");
		break;
	}
	return tvb_length(tvb);
}

void
proto_register_rtcdc(void)
{
	module_t *rtcdc_module;
	static hf_register_info hf[] = {
		{ &hf_message_type,      { "Message type",              "rtcdc.message_type",            FT_UINT8,   BASE_DEC,  VALS(message_type_values), 0x0,                                         NULL, HFILL } },
		{ &hf_channel_type,      { "Channel type",              "rtcdc.channel_type",            FT_UINT8,   BASE_DEC,  VALS(channel_type_values), 0x0,                                         NULL, HFILL } },
		{ &hf_flags,             { "Flags",                     "rtcdc.flags",                   FT_UINT16,  BASE_HEX,  NULL,                      0x0,                                         NULL, HFILL } },
		{ &hf_flags_reserved,    { "Reserved",                  "rtcdc.flags_reserved",          FT_UINT16,  BASE_HEX,  NULL,                      DATA_CHANNEL_FLAG_RESERVED_MASK,             NULL, HFILL } },
		{ &hf_unordered_allowed, { "Unordered allowed",         "rtcdc.flags_unordered_allowed", FT_BOOLEAN, 16,        NULL,                      DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED_MASK, NULL, HFILL } },
		{ &hf_reliability,       { "Reliability parameter",     "rtcdc.reliability_parameter",   FT_UINT16,  BASE_DEC,  NULL,                      0x0,                                         NULL, HFILL } },
		{ &hf_priority,          { "Priority",                  "rtcdc.priority",                FT_UINT16,  BASE_DEC,  NULL,                      0x0,                                         NULL, HFILL } },
		{ &hf_label,             { "Label",                     "rtcdc.label",                   FT_STRING,  BASE_NONE, NULL,                      0x0,                                         NULL, HFILL } },
		{ &hf_error,             { "Error",                     "rtcdc.error",                   FT_UINT8,   BASE_DEC,  NULL,                      0x0,                                         NULL, HFILL } },
		{ &hf_sid,               { "Reverse stream identifier", "rtcdc.reverse_stream_id",       FT_UINT16,  BASE_DEC,  NULL,                      0x0,                                         NULL, HFILL } }
	};
	static gint *ett[] = {
		&ett_rtcdc,
		&ett_flags
	};

	proto_rtcdc = proto_register_protocol("WebRTC Datachannel Protocol", "RTCDC", "rtcdc");
	proto_register_field_array(proto_rtcdc, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));
	rtcdc_module = prefs_register_protocol(proto_rtcdc, proto_reg_handoff_rtcdc);
	prefs_register_uint_preference(rtcdc_module, "sctp.ppi", "RTCDC SCTP PPID", "RTCDC SCTP PPID if other than the default", 10, &rtcdc_ppid);
}

void
proto_reg_handoff_rtcdc(void)
{
	static gboolean initialized = FALSE;
	static dissector_handle_t rtcdc_handle;
	static guint32 current_ppid;

	if (!initialized) {
		rtcdc_handle = new_create_dissector_handle(dissect_rtcdc, proto_rtcdc);
		initialized = TRUE;
	} else {
		dissector_delete_uint("sctp.ppi", current_ppid, rtcdc_handle);
	}
	current_ppid = rtcdc_ppid;
	dissector_add_uint("sctp.ppi", current_ppid, rtcdc_handle);
}

/*
 * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 4
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=4 tabstop=8 expandtab:
 * :indentSize=4:tabSize=8:noTabs=true:
 */