Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / file-rtpdump.c
blob21063ac0a16f502b72342fe234822eae87565da1
1 /* file-rtpdump.c
3 * Routines for rtpdump file dissection
4 * Copyright 2023, David Perry <boolean263@protonmail.com>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * This dissects the rtpdump file format as generated by Wireshark.
11 * See also https://wiki.wireshark.org/rtpdump
12 * This file format was created as part of rtptools:
13 * https://github.com/irtlab/rtptools
15 * SPDX-License-Identifier: GPL-2.0-or-later
18 #include "config.h"
20 #include <epan/packet.h>
21 #include <epan/expert.h>
22 #include <wsutil/strtoi.h>
23 #include <wsutil/inet_addr.h>
24 #include <wsutil/array.h>
26 void proto_register_rtpdump(void);
27 void proto_reg_handoff_rtpdump(void);
29 /* Initialize the protocol and registered fields */
30 static int proto_rtpdump;
32 static int hf_rtpdump_text_header;
33 static int hf_rtpdump_play_program;
34 static int hf_rtpdump_version;
35 static int hf_rtpdump_txt_ipv4;
36 static int hf_rtpdump_txt_ipv6;
37 static int hf_rtpdump_txt_port;
39 static int hf_rtpdump_binary_header;
40 static int hf_rtpdump_ts_sec;
41 static int hf_rtpdump_ts_usec;
42 static int hf_rtpdump_ts;
43 static int hf_rtpdump_bin_addr;
44 static int hf_rtpdump_bin_port;
45 static int hf_rtpdump_padding;
47 static int hf_rtpdump_pkt;
48 static int hf_rtpdump_pkt_len;
49 static int hf_rtpdump_pkt_plen;
50 static int hf_rtpdump_pkt_offset;
51 static int hf_rtpdump_pkt_data;
53 /* Initialize the subtree pointers */
54 static int ett_rtpdump;
55 static int ett_rtpdump_text_header;
56 static int ett_rtpdump_binary_header;
57 static int ett_rtpdump_pkt;
59 static expert_field ei_rtpdump_unknown_program;
60 static expert_field ei_rtpdump_unknown_version;
61 static expert_field ei_rtpdump_bad_txt_addr;
62 static expert_field ei_rtpdump_bad_txt_port;
63 static expert_field ei_rtpdump_bin_ipv6;
64 static expert_field ei_rtpdump_addrs_match;
65 static expert_field ei_rtpdump_addrs_mismatch;
66 static expert_field ei_rtpdump_caplen;
68 /* Reasonable minimum length for the RTP header (including the magic):
69 * - 13 for "#!rtpplay1.0 "
70 * - WS_INET_ADDRSTRLEN characters for a destination IPv4 address
71 * - 1 for a slash
72 * - 3 characters for a destination port number
73 * - 1 character for a newline
74 * - 4 bytes for each of start seconds, start useconds, source IPv4
75 * - 2 bytes for each of source port, padding
77 #define RTP_HEADER_MIN_LEN 24+WS_INET_ADDRSTRLEN
79 static int
80 dissect_rtpdump(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_)
82 proto_tree *tree, *subtree;
83 proto_item *ti;
84 int tvb_len = tvb_captured_length(tvb);
85 unsigned pkt_num = 1;
86 static const uint8_t shebang[] = {'#', '!'};
87 static const char rtpplay[] = "rtpplay";
88 static const char rtpver[] = "1.0";
89 int offset = 0;
90 int i = 0;
91 int slash = 0;
92 int eol = 0;
93 int space = 0;
94 uint8_t *str = NULL;
95 uint16_t txt_port = 0;
96 uint32_t bin_port = 0;
97 ws_in4_addr txt_ipv4 = 0;
98 ws_in6_addr txt_ipv6 = {0};
99 ws_in4_addr bin_ipv4 = 0;
100 bool txt_is_ipv6 = false;
101 nstime_t start_time = NSTIME_INIT_ZERO;
102 int pkt_length;
103 int data_length;
105 if (tvb_len < RTP_HEADER_MIN_LEN)
106 return 0;
107 if (0 != tvb_memeql(tvb, 0, shebang, sizeof(shebang)))
108 return 0;
109 if (-1 == (eol = tvb_find_uint8(tvb, 0, -1, '\n')) ||
110 -1 == (slash = tvb_find_uint8(tvb, 0, eol, '/')) ||
111 -1 == (space = tvb_find_uint8(tvb, 0, slash, ' '))) {
112 return 0;
115 ti = proto_tree_add_item(parent_tree, proto_rtpdump, tvb, offset, -1, ENC_NA);
116 tree = proto_item_add_subtree(ti, ett_rtpdump);
118 /* Handle the text header */
119 ti = proto_tree_add_item(tree, hf_rtpdump_text_header, tvb, offset, eol+1, ENC_ASCII);
120 subtree = proto_item_add_subtree(ti, ett_rtpdump_text_header);
122 /* Get the program name */
123 offset += 2;
124 for (i = offset; g_ascii_isalpha(tvb_get_uint8(tvb, i)); i++)
125 /* empty loop */ ;
126 ti = proto_tree_add_item_ret_string(subtree, hf_rtpdump_play_program,
127 tvb, offset, i-offset, ENC_ASCII,
128 pinfo->pool, (const uint8_t **)&str);
129 if (0 != g_strcmp0(str, rtpplay)) {
130 expert_add_info(pinfo, ti, &ei_rtpdump_unknown_program);
133 /* Get the program version */
134 offset = i;
135 ti = proto_tree_add_item_ret_string(subtree, hf_rtpdump_version,
136 tvb, offset, space-offset, ENC_ASCII,
137 pinfo->pool, (const uint8_t **)&str);
138 if (0 != g_strcmp0(str, rtpver)) {
139 expert_add_info(pinfo, ti, &ei_rtpdump_unknown_version);
142 /* Get the text IP */
143 offset = space + 1;
144 str = tvb_get_string_enc(pinfo->pool, tvb, offset, slash-offset, ENC_ASCII);
145 if (ws_inet_pton4(str, &txt_ipv4)) {
146 proto_tree_add_ipv4(subtree, hf_rtpdump_txt_ipv4, tvb, offset, slash-offset, txt_ipv4);
148 else if (ws_inet_pton6(str, &txt_ipv6)) {
149 txt_is_ipv6 = true;
150 proto_tree_add_ipv6(subtree, hf_rtpdump_txt_ipv6, tvb, offset, slash-offset, &txt_ipv6);
152 else {
153 proto_tree_add_expert(subtree, pinfo, &ei_rtpdump_bad_txt_addr,
154 tvb, offset, eol-offset);
157 /* Get the text port */
158 offset = slash + 1;
159 str = tvb_get_string_enc(pinfo->pool, tvb, offset, eol-offset, ENC_ASCII);
160 if (ws_strtou16(str, NULL, &txt_port)) {
161 proto_tree_add_uint(subtree, hf_rtpdump_txt_port, tvb, offset, eol-offset, txt_port);
163 else {
164 proto_tree_add_expert(subtree, pinfo, &ei_rtpdump_bad_txt_port,
165 tvb, offset, eol-offset);
168 /* Handle the binary header */
169 offset = eol + 1;
170 ti = proto_tree_add_item(tree, hf_rtpdump_binary_header, tvb, offset, 16, ENC_NA);
171 subtree = proto_item_add_subtree(ti, ett_rtpdump_binary_header);
173 proto_tree_add_item_ret_uint(subtree, hf_rtpdump_ts_sec, tvb, offset, 4, ENC_BIG_ENDIAN,
174 (uint32_t *)&start_time.secs);
175 proto_tree_add_item_ret_uint(subtree, hf_rtpdump_ts_usec, tvb, offset+4, 4, ENC_BIG_ENDIAN,
176 &start_time.nsecs);
177 start_time.nsecs *= 1000;
178 ti = proto_tree_add_time(subtree, hf_rtpdump_ts, tvb, offset, 8, &start_time);
179 proto_item_set_generated(ti);
180 offset += 8;
182 ti = proto_tree_add_item(subtree, hf_rtpdump_bin_addr, tvb, offset, 4, ENC_BIG_ENDIAN);
183 /* Force internal representation to big-endian as per wsutil/inet_ipv4.h */
184 bin_ipv4 = g_htonl(tvb_get_uint32(tvb, offset, ENC_BIG_ENDIAN));
185 offset += 4;
186 proto_tree_add_item_ret_uint(subtree, hf_rtpdump_bin_port, tvb, offset, 2, ENC_BIG_ENDIAN, &bin_port);
187 offset += 2;
188 proto_tree_add_item(subtree, hf_rtpdump_padding, tvb, offset, 2, ENC_NA);
189 offset += 2;
191 if (txt_is_ipv6) {
192 expert_add_info(pinfo, ti, &ei_rtpdump_bin_ipv6);
193 expert_add_info(pinfo, subtree, &ei_rtpdump_addrs_mismatch);
195 else if(bin_ipv4 == txt_ipv4 && bin_port == txt_port) {
196 expert_add_info(pinfo, subtree, &ei_rtpdump_addrs_match);
198 else {
199 expert_add_info(pinfo, subtree, &ei_rtpdump_addrs_mismatch);
202 /* Handle individual packets */
203 while (offset < tvb_len) {
204 pkt_length = tvb_get_ntohs(tvb, offset);
205 ti = proto_tree_add_item(tree, hf_rtpdump_pkt, tvb, offset, pkt_length, ENC_NA);
206 subtree = proto_item_add_subtree(ti, ett_rtpdump_pkt);
207 proto_item_set_text(subtree, "Packet %d", pkt_num++);
209 pkt_length -= 8;
211 proto_tree_add_item(subtree, hf_rtpdump_pkt_len, tvb, offset, 2, ENC_BIG_ENDIAN);
212 offset += 2;
214 ti = proto_tree_add_item_ret_uint(subtree, hf_rtpdump_pkt_plen, tvb, offset, 2, ENC_BIG_ENDIAN,
215 &data_length);
216 if (data_length > pkt_length) {
217 expert_add_info(pinfo, ti, &ei_rtpdump_caplen);
219 offset += 2;
221 proto_tree_add_item(subtree, hf_rtpdump_pkt_offset, tvb, offset, 4, ENC_BIG_ENDIAN);
222 offset += 4;
224 proto_tree_add_item(subtree, hf_rtpdump_pkt_data, tvb, offset, pkt_length, ENC_NA);
225 offset += pkt_length;
228 return tvb_len;
231 static bool
232 dissect_rtpdump_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data)
234 return dissect_rtpdump(tvb, pinfo, parent_tree, data) > 0;
237 /****************** Register the protocol with Wireshark ******************/
239 void
240 proto_register_rtpdump(void)
242 static hf_register_info hf[] = {
243 { &hf_rtpdump_text_header,
244 { "Text header", "rtpdump.text_header",
245 FT_STRING, BASE_NONE, NULL, 0x0,
246 NULL, HFILL }
248 { &hf_rtpdump_play_program,
249 { "Play program", "rtpdump.play_program",
250 FT_STRING, BASE_NONE, NULL, 0x0,
251 "Program to be used to play this stream", HFILL }
253 { &hf_rtpdump_version,
254 { "File format version", "rtpdump.version",
255 FT_STRING, BASE_NONE, NULL, 0x0,
256 NULL, HFILL }
258 { &hf_rtpdump_txt_ipv4,
259 { "Text IPv4 address", "rtpdump.txt_addr",
260 FT_IPv4, BASE_NONE, NULL, 0x0,
261 NULL, HFILL }
263 { &hf_rtpdump_txt_ipv6,
264 { "Text IPv6 address", "rtpdump.txt_addr",
265 FT_IPv6, BASE_NONE, NULL, 0x0,
266 NULL, HFILL }
268 { &hf_rtpdump_txt_port,
269 { "Text port", "rtpdump.txt_port",
270 FT_UINT16, BASE_DEC, NULL, 0x0,
271 NULL, HFILL }
273 { &hf_rtpdump_binary_header,
274 { "Binary header", "rtpdump.binary_header",
275 FT_BYTES, BASE_NONE|BASE_NO_DISPLAY_VALUE, NULL, 0x0,
276 NULL, HFILL }
278 { &hf_rtpdump_ts_sec,
279 { "Start time (seconds)", "rtpdump.ts.sec",
280 FT_UINT32, BASE_DEC, NULL, 0x0,
281 NULL, HFILL }
283 { &hf_rtpdump_ts_usec,
284 { "Start time (microseconds)", "rtpdump.ts_usec",
285 FT_UINT32, BASE_DEC, NULL, 0x0,
286 NULL, HFILL }
288 { &hf_rtpdump_ts,
289 { "Start time", "rtpdump.ts",
290 FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0,
291 NULL, HFILL }
293 { &hf_rtpdump_bin_addr,
294 { "Binary IPv4 address", "rtpdump.bin_addr",
295 FT_IPv4, BASE_NONE, NULL, 0x0,
296 NULL, HFILL }
298 { &hf_rtpdump_bin_port,
299 { "Binary port", "rtpdump.bin_port",
300 FT_UINT16, BASE_DEC, NULL, 0x0,
301 NULL, HFILL }
303 { &hf_rtpdump_padding,
304 { "Padding", "rtpdump.padding",
305 FT_BYTES, BASE_NONE, NULL, 0x0,
306 NULL, HFILL }
308 { &hf_rtpdump_pkt,
309 { "Packet", "rtpdump.packet",
310 FT_BYTES, BASE_NONE|BASE_NO_DISPLAY_VALUE, NULL, 0x0,
311 NULL, HFILL }
313 { &hf_rtpdump_pkt_len,
314 { "Packet length", "rtpdump.pkt_len",
315 FT_UINT16, BASE_DEC, NULL, 0x0,
316 "Total packet length", HFILL }
318 { &hf_rtpdump_pkt_plen,
319 { "Data length", "rtpdump.pkt_plen",
320 FT_UINT16, BASE_DEC, NULL, 0x0,
321 NULL, HFILL }
323 { &hf_rtpdump_pkt_offset,
324 { "Time offset (milliseconds)", "rtpdump.pkt_offset",
325 FT_UINT32, BASE_DEC, NULL, 0x0,
326 "Time from start of capture", HFILL }
328 { &hf_rtpdump_pkt_data,
329 { "Data", "rtpdump.pkt_data",
330 FT_BYTES, BASE_NONE|BASE_NO_DISPLAY_VALUE, NULL, 0x0,
331 NULL, HFILL }
335 /* Setup protocol subtree array */
336 static int *ett[] = {
337 &ett_rtpdump,
338 &ett_rtpdump_text_header,
339 &ett_rtpdump_binary_header,
340 &ett_rtpdump_pkt,
343 static ei_register_info ei[] = {
344 { &ei_rtpdump_unknown_program,
345 { "rtpdump.play_program.unknown", PI_PROTOCOL, PI_WARN,
346 "Playback program not the expected 'rtpplay', dissection may be incorrect", EXPFILL }},
347 { &ei_rtpdump_unknown_version,
348 { "rtpdump.version.unknown", PI_PROTOCOL, PI_WARN,
349 "Version not recognized, dissection may be incorrect", EXPFILL }},
350 { &ei_rtpdump_bad_txt_addr,
351 { "rtpdump.txt_addr.bad", PI_PROTOCOL, PI_WARN,
352 "Unparseable text address", EXPFILL }},
353 { &ei_rtpdump_bad_txt_port,
354 { "rtpdump.txt_port.bad", PI_PROTOCOL, PI_WARN,
355 "Unparseable text port", EXPFILL }},
356 { &ei_rtpdump_bin_ipv6,
357 { "rtpdump.bin_addr.ipv6", PI_PROTOCOL, PI_NOTE,
358 "Binary IPv4 address may be a truncated IPv6 address", EXPFILL }},
359 { &ei_rtpdump_addrs_match,
360 { "rtpdump.address.match", PI_PROTOCOL, PI_CHAT,
361 "Text and binary addresses and ports match -- file likely generated by rtpdump", EXPFILL }},
362 { &ei_rtpdump_addrs_mismatch,
363 { "rtpdump.address.mismatch", PI_PROTOCOL, PI_CHAT,
364 "Text and binary addresses and ports do not match -- file likely generated by wireshark", EXPFILL }},
365 { &ei_rtpdump_caplen,
366 { "rtpdump.pkt_plen.truncated", PI_PROTOCOL, PI_NOTE,
367 "Data was truncated during capture", EXPFILL }},
370 expert_module_t* expert_rtpdump;
372 /* Register the protocol name and description */
373 proto_rtpdump = proto_register_protocol("RTPDump file format", "rtpdump", "rtpdump");
375 /* Required function calls to register the header fields
376 * and subtrees used */
377 proto_register_field_array(proto_rtpdump, hf, array_length(hf));
378 proto_register_subtree_array(ett, array_length(ett));
380 expert_rtpdump = expert_register_protocol(proto_rtpdump);
381 expert_register_field_array(expert_rtpdump, ei, array_length(ei));
383 register_dissector("rtpdump", dissect_rtpdump, proto_rtpdump);
386 void
387 proto_reg_handoff_rtpdump(void)
389 heur_dissector_add("wtap_file", dissect_rtpdump_heur, "RTPDump file", "rtpdump_wtap", proto_rtpdump, HEURISTIC_ENABLE);