Coverage Report

Created: 2026-01-02 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/file-rtpdump.c
Line
Count
Source
1
/* file-rtpdump.c
2
 *
3
 * Routines for rtpdump file dissection
4
 * Copyright 2023, David Perry <boolean263@protonmail.com>
5
 *
6
 * Wireshark - Network traffic analyzer
7
 * By Gerald Combs <gerald@wireshark.org>
8
 * Copyright 1998 Gerald Combs
9
 *
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
14
 *
15
 * SPDX-License-Identifier: GPL-2.0-or-later
16
 */
17
18
#include "config.h"
19
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>
25
26
void proto_register_rtpdump(void);
27
void proto_reg_handoff_rtpdump(void);
28
29
/* Initialize the protocol and registered fields */
30
static int proto_rtpdump;
31
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;
38
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;
46
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;
52
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_ts;
58
static int ett_rtpdump_pkt;
59
60
static expert_field ei_rtpdump_unknown_program;
61
static expert_field ei_rtpdump_unknown_version;
62
static expert_field ei_rtpdump_bad_txt_addr;
63
static expert_field ei_rtpdump_bad_txt_port;
64
static expert_field ei_rtpdump_bin_ipv6;
65
static expert_field ei_rtpdump_addrs_match;
66
static expert_field ei_rtpdump_addrs_mismatch;
67
static expert_field ei_rtpdump_caplen;
68
69
/* Reasonable minimum length for the RTP header (including the magic):
70
 * - 13 for "#!rtpplay1.0 "
71
 * - WS_INET_ADDRSTRLEN characters for a destination IPv4 address
72
 * - 1 for a slash
73
 * - 3 characters for a destination port number
74
 * - 1 character for a newline
75
 * - 4 bytes for each of start seconds, start useconds, source IPv4
76
 * - 2 bytes for each of source port, padding
77
 */
78
0
#define RTP_HEADER_MIN_LEN 24+WS_INET_ADDRSTRLEN
79
80
static int
81
dissect_rtpdump(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_)
82
0
{
83
0
    proto_tree *tree, *subtree, *timetree;
84
0
    proto_item *ti;
85
0
    int tvb_len = tvb_captured_length(tvb);
86
0
    unsigned pkt_num = 1;
87
0
    static const uint8_t shebang[] = {'#', '!'};
88
0
    static const char rtpplay[] = "rtpplay";
89
0
    static const char rtpver[] = "1.0";
90
0
    int offset = 0;
91
0
    int i = 0;
92
0
    int slash = 0;
93
0
    int eol = 0;
94
0
    int space = 0;
95
0
    const char *str = NULL;
96
0
    uint16_t txt_port = 0;
97
0
    uint32_t bin_port = 0;
98
0
    ws_in4_addr txt_ipv4 = 0;
99
0
    ws_in6_addr txt_ipv6 = {0};
100
0
    ws_in4_addr bin_ipv4 = 0;
101
0
    bool txt_is_ipv6 = false;
102
0
    uint32_t pkt_length;
103
0
    uint32_t data_length;
104
105
0
    if (tvb_len < RTP_HEADER_MIN_LEN)
106
0
        return 0;
107
0
    if (0 != tvb_memeql(tvb, 0, shebang, sizeof(shebang)))
108
0
        return 0;
109
0
    if (-1 == (eol = tvb_find_uint8(tvb, 0, -1, '\n')) ||
110
0
            -1 == (slash = tvb_find_uint8(tvb, 0, eol, '/')) ||
111
0
            -1 == (space = tvb_find_uint8(tvb, 0, slash, ' '))) {
112
0
        return 0;
113
0
    }
114
115
0
    ti = proto_tree_add_item(parent_tree, proto_rtpdump, tvb, offset, -1, ENC_NA);
116
0
    tree = proto_item_add_subtree(ti, ett_rtpdump);
117
118
    /* Handle the text header */
119
0
    ti = proto_tree_add_item(tree, hf_rtpdump_text_header, tvb, offset, eol+1, ENC_ASCII);
120
0
    subtree = proto_item_add_subtree(ti, ett_rtpdump_text_header);
121
122
    /* Get the program name */
123
0
    offset += 2;
124
0
    for (i = offset; g_ascii_isalpha(tvb_get_uint8(tvb, i)); i++)
125
0
        /* empty loop */ ;
126
0
    ti = proto_tree_add_item_ret_string(subtree, hf_rtpdump_play_program,
127
0
                                        tvb, offset, i-offset, ENC_ASCII,
128
0
                                        pinfo->pool, (const uint8_t **)&str);
129
0
    if (0 != g_strcmp0(str, rtpplay)) {
130
0
        expert_add_info(pinfo, ti, &ei_rtpdump_unknown_program);
131
0
    }
132
133
    /* Get the program version */
134
0
    offset = i;
135
0
    ti = proto_tree_add_item_ret_string(subtree, hf_rtpdump_version,
136
0
                                        tvb, offset, space-offset, ENC_ASCII,
137
0
                                        pinfo->pool, (const uint8_t **)&str);
138
0
    if (0 != g_strcmp0(str, rtpver)) {
139
0
        expert_add_info(pinfo, ti, &ei_rtpdump_unknown_version);
140
0
    }
141
142
    /* Get the text IP */
143
0
    offset = space + 1;
144
0
    str = (char *)tvb_get_string_enc(pinfo->pool, tvb, offset, slash-offset, ENC_ASCII);
145
0
    if (ws_inet_pton4(str, &txt_ipv4)) {
146
0
        proto_tree_add_ipv4(subtree, hf_rtpdump_txt_ipv4, tvb, offset, slash-offset, txt_ipv4);
147
0
    }
148
0
    else if (ws_inet_pton6(str, &txt_ipv6)) {
149
0
        txt_is_ipv6 = true;
150
0
        proto_tree_add_ipv6(subtree, hf_rtpdump_txt_ipv6, tvb, offset, slash-offset, &txt_ipv6);
151
0
    }
152
0
    else {
153
0
        proto_tree_add_expert(subtree, pinfo, &ei_rtpdump_bad_txt_addr,
154
0
                              tvb, offset, eol-offset);
155
0
    }
156
157
    /* Get the text port */
158
0
    offset = slash + 1;
159
0
    str = (char *)tvb_get_string_enc(pinfo->pool, tvb, offset, eol-offset, ENC_ASCII);
160
0
    if (ws_strtou16(str, NULL, &txt_port)) {
161
0
        proto_tree_add_uint(subtree, hf_rtpdump_txt_port, tvb, offset, eol-offset, txt_port);
162
0
    }
163
0
    else {
164
0
        proto_tree_add_expert(subtree, pinfo, &ei_rtpdump_bad_txt_port,
165
0
                              tvb, offset, eol-offset);
166
0
    }
167
168
    /* Handle the binary header */
169
0
    offset = eol + 1;
170
0
    ti = proto_tree_add_item(tree, hf_rtpdump_binary_header, tvb, offset, 16, ENC_NA);
171
0
    subtree = proto_item_add_subtree(ti, ett_rtpdump_binary_header);
172
173
0
    ti = proto_tree_add_item(subtree, hf_rtpdump_ts, tvb, offset, 8, ENC_TIME_SECS_USECS|ENC_BIG_ENDIAN);
174
0
    timetree = proto_item_add_subtree(ti, ett_rtpdump_ts);
175
0
    proto_tree_add_item(timetree, hf_rtpdump_ts_sec, tvb, offset, 4, ENC_BIG_ENDIAN);
176
0
    proto_tree_add_item(timetree, hf_rtpdump_ts_usec, tvb, offset+4, 4, ENC_BIG_ENDIAN);
177
0
    offset += 8;
178
179
0
    ti = proto_tree_add_item(subtree, hf_rtpdump_bin_addr, tvb, offset, 4, ENC_BIG_ENDIAN);
180
    /* Force internal representation to big-endian as per wsutil/inet_ipv4.h */
181
0
    bin_ipv4 = g_htonl(tvb_get_uint32(tvb, offset, ENC_BIG_ENDIAN));
182
0
    offset += 4;
183
0
    proto_tree_add_item_ret_uint(subtree, hf_rtpdump_bin_port, tvb, offset, 2, ENC_BIG_ENDIAN, &bin_port);
184
0
    offset += 2;
185
0
    proto_tree_add_item(subtree, hf_rtpdump_padding, tvb, offset, 2, ENC_NA);
186
0
    offset += 2;
187
188
0
    if (txt_is_ipv6) {
189
0
        expert_add_info(pinfo, ti, &ei_rtpdump_bin_ipv6);
190
0
        expert_add_info(pinfo, subtree, &ei_rtpdump_addrs_mismatch);
191
0
    }
192
0
    else if(bin_ipv4 == txt_ipv4 && bin_port == txt_port) {
193
0
        expert_add_info(pinfo, subtree, &ei_rtpdump_addrs_match);
194
0
    }
195
0
    else {
196
0
        expert_add_info(pinfo, subtree, &ei_rtpdump_addrs_mismatch);
197
0
    }
198
199
    /* Handle individual packets */
200
0
    while (offset < tvb_len) {
201
0
        pkt_length = tvb_get_ntohs(tvb, offset);
202
0
        ti = proto_tree_add_item(tree, hf_rtpdump_pkt, tvb, offset, pkt_length, ENC_NA);
203
0
        subtree = proto_item_add_subtree(ti, ett_rtpdump_pkt);
204
0
        proto_item_set_text(subtree, "Packet %d", pkt_num++);
205
206
0
        pkt_length -= 8;
207
208
0
        proto_tree_add_item(subtree, hf_rtpdump_pkt_len, tvb, offset, 2, ENC_BIG_ENDIAN);
209
0
        offset += 2;
210
211
0
        ti = proto_tree_add_item_ret_uint(subtree, hf_rtpdump_pkt_plen, tvb, offset, 2, ENC_BIG_ENDIAN,
212
0
                                     &data_length);
213
0
        if (data_length > pkt_length) {
214
0
            expert_add_info(pinfo, ti, &ei_rtpdump_caplen);
215
0
        }
216
0
        offset += 2;
217
218
0
        proto_tree_add_item(subtree, hf_rtpdump_pkt_offset, tvb, offset, 4, ENC_BIG_ENDIAN);
219
0
        offset += 4;
220
221
0
        proto_tree_add_item(subtree, hf_rtpdump_pkt_data, tvb, offset, pkt_length, ENC_NA);
222
0
        offset += pkt_length;
223
0
    }
224
225
0
    return tvb_len;
226
0
}
227
228
static bool
229
dissect_rtpdump_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data)
230
0
{
231
0
    return dissect_rtpdump(tvb, pinfo, parent_tree, data) > 0;
232
0
}
233
234
/****************** Register the protocol with Wireshark ******************/
235
236
void
237
proto_register_rtpdump(void)
238
14
{
239
14
    static hf_register_info hf[] = {
240
14
        { &hf_rtpdump_text_header,
241
14
            { "Text header", "rtpdump.text_header",
242
14
              FT_STRING, BASE_NONE, NULL, 0x0,
243
14
              NULL, HFILL }
244
14
        },
245
14
        { &hf_rtpdump_play_program,
246
14
            { "Play program", "rtpdump.play_program",
247
14
              FT_STRING, BASE_NONE, NULL, 0x0,
248
14
              "Program to be used to play this stream", HFILL }
249
14
        },
250
14
        { &hf_rtpdump_version,
251
14
            { "File format version", "rtpdump.version",
252
14
              FT_STRING, BASE_NONE, NULL, 0x0,
253
14
              NULL, HFILL }
254
14
        },
255
14
        { &hf_rtpdump_txt_ipv4,
256
14
            { "Text IPv4 address", "rtpdump.txt_addr_ipv4",
257
14
              FT_IPv4, BASE_NONE, NULL, 0x0,
258
14
              NULL, HFILL }
259
14
        },
260
14
        { &hf_rtpdump_txt_ipv6,
261
14
            { "Text IPv6 address", "rtpdump.txt_addr_ipv6",
262
14
              FT_IPv6, BASE_NONE, NULL, 0x0,
263
14
              NULL, HFILL }
264
14
        },
265
14
        { &hf_rtpdump_txt_port,
266
14
            { "Text port", "rtpdump.txt_port",
267
14
              FT_UINT16, BASE_DEC, NULL, 0x0,
268
14
              NULL, HFILL }
269
14
        },
270
14
        { &hf_rtpdump_binary_header,
271
14
            { "Binary header", "rtpdump.binary_header",
272
14
              FT_BYTES, BASE_NONE|BASE_NO_DISPLAY_VALUE, NULL, 0x0,
273
14
              NULL, HFILL }
274
14
        },
275
14
        { &hf_rtpdump_ts_sec,
276
14
            { "Start time (seconds)", "rtpdump.ts.sec",
277
14
              FT_UINT32, BASE_DEC, NULL, 0x0,
278
14
              NULL, HFILL }
279
14
        },
280
14
        { &hf_rtpdump_ts_usec,
281
14
            { "Start time (microseconds)", "rtpdump.ts_usec",
282
14
              FT_UINT32, BASE_DEC, NULL, 0x0,
283
14
              NULL, HFILL }
284
14
        },
285
14
        { &hf_rtpdump_ts,
286
14
            { "Start time", "rtpdump.ts",
287
14
              FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0,
288
14
              NULL, HFILL }
289
14
        },
290
14
        { &hf_rtpdump_bin_addr,
291
14
            { "Binary IPv4 address", "rtpdump.bin_addr",
292
14
              FT_IPv4, BASE_NONE, NULL, 0x0,
293
14
              NULL, HFILL }
294
14
        },
295
14
        { &hf_rtpdump_bin_port,
296
14
            { "Binary port", "rtpdump.bin_port",
297
14
              FT_UINT16, BASE_DEC, NULL, 0x0,
298
14
              NULL, HFILL }
299
14
        },
300
14
        { &hf_rtpdump_padding,
301
14
            { "Padding", "rtpdump.padding",
302
14
              FT_BYTES, BASE_NONE, NULL, 0x0,
303
14
              NULL, HFILL }
304
14
        },
305
14
        { &hf_rtpdump_pkt,
306
14
            { "Packet", "rtpdump.packet",
307
14
              FT_BYTES, BASE_NONE|BASE_NO_DISPLAY_VALUE, NULL, 0x0,
308
14
              NULL, HFILL }
309
14
        },
310
14
        { &hf_rtpdump_pkt_len,
311
14
            { "Packet length", "rtpdump.pkt_len",
312
14
              FT_UINT16, BASE_DEC, NULL, 0x0,
313
14
              "Total packet length", HFILL }
314
14
        },
315
14
        { &hf_rtpdump_pkt_plen,
316
14
            { "Data length", "rtpdump.pkt_plen",
317
14
              FT_UINT16, BASE_DEC, NULL, 0x0,
318
14
              NULL, HFILL }
319
14
        },
320
14
        { &hf_rtpdump_pkt_offset,
321
14
            { "Time offset (milliseconds)", "rtpdump.pkt_offset",
322
14
              FT_UINT32, BASE_DEC, NULL, 0x0,
323
14
              "Time from start of capture", HFILL }
324
14
        },
325
14
        { &hf_rtpdump_pkt_data,
326
14
            { "Data", "rtpdump.pkt_data",
327
14
              FT_BYTES, BASE_NONE|BASE_NO_DISPLAY_VALUE, NULL, 0x0,
328
14
              NULL, HFILL }
329
14
        },
330
14
    };
331
332
    /* Setup protocol subtree array */
333
14
    static int *ett[] = {
334
14
        &ett_rtpdump,
335
14
        &ett_rtpdump_text_header,
336
14
        &ett_rtpdump_binary_header,
337
14
        &ett_rtpdump_ts,
338
14
        &ett_rtpdump_pkt,
339
14
    };
340
341
14
    static ei_register_info ei[] = {
342
14
        { &ei_rtpdump_unknown_program,
343
14
          { "rtpdump.play_program.unknown", PI_PROTOCOL, PI_WARN,
344
14
            "Playback program not the expected 'rtpplay', dissection may be incorrect", EXPFILL }},
345
14
        { &ei_rtpdump_unknown_version,
346
14
          { "rtpdump.version.unknown", PI_PROTOCOL, PI_WARN,
347
14
            "Version not recognized, dissection may be incorrect", EXPFILL }},
348
14
        { &ei_rtpdump_bad_txt_addr,
349
14
          { "rtpdump.txt_addr.bad", PI_PROTOCOL, PI_WARN,
350
14
            "Unparseable text address", EXPFILL }},
351
14
        { &ei_rtpdump_bad_txt_port,
352
14
          { "rtpdump.txt_port.bad", PI_PROTOCOL, PI_WARN,
353
14
            "Unparseable text port", EXPFILL }},
354
14
        { &ei_rtpdump_bin_ipv6,
355
14
          { "rtpdump.bin_addr.ipv6", PI_PROTOCOL, PI_NOTE,
356
14
            "Binary IPv4 address may be a truncated IPv6 address", EXPFILL }},
357
14
        { &ei_rtpdump_addrs_match,
358
14
          { "rtpdump.address.match", PI_PROTOCOL, PI_CHAT,
359
14
            "Text and binary addresses and ports match -- file likely generated by rtpdump", EXPFILL }},
360
14
        { &ei_rtpdump_addrs_mismatch,
361
14
          { "rtpdump.address.mismatch", PI_PROTOCOL, PI_CHAT,
362
14
            "Text and binary addresses and ports do not match -- file likely generated by wireshark", EXPFILL }},
363
14
        { &ei_rtpdump_caplen,
364
14
          { "rtpdump.pkt_plen.truncated", PI_PROTOCOL, PI_NOTE,
365
14
            "Data was truncated during capture", EXPFILL }},
366
14
    };
367
368
14
    expert_module_t* expert_rtpdump;
369
370
    /* Register the protocol name and description */
371
14
    proto_rtpdump = proto_register_protocol("RTPDump file format", "rtpdump", "rtpdump");
372
373
    /* Required function calls to register the header fields
374
     * and subtrees used */
375
14
    proto_register_field_array(proto_rtpdump, hf, array_length(hf));
376
14
    proto_register_subtree_array(ett, array_length(ett));
377
378
14
    expert_rtpdump = expert_register_protocol(proto_rtpdump);
379
14
    expert_register_field_array(expert_rtpdump, ei, array_length(ei));
380
381
14
    register_dissector("rtpdump", dissect_rtpdump, proto_rtpdump);
382
14
}
383
384
void
385
proto_reg_handoff_rtpdump(void)
386
14
{
387
14
    heur_dissector_add("wtap_file", dissect_rtpdump_heur, "RTPDump file", "rtpdump_wtap", proto_rtpdump, HEURISTIC_ENABLE);
388
14
}