Coverage Report

Created: 2025-12-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wiretap/mpeg.c
Line
Count
Source
1
/* mpeg.c
2
 *
3
 * MPEG-1/2 file format decoder for the Wiretap library.
4
 * Written by Shaun Jackman <sjackman@gmail.com>
5
 * Copyright 2007 Shaun Jackman
6
 *
7
 * MPEG-1/2 Program Streams (ISO/IEC 11172-1, ISO/IEC 13818-1 / ITU-T H.220.0)
8
 * MPEG-1/2 Video bitstream (ISO/IEC 11172-2, ISO/IEC 13818-2 / ITU-T H.262)
9
 * MPEG-1/2 Audio files (ISO/IEC 11172-3, ISO/IEC 13818-3)
10
 *
11
 * Does not handle other MPEG-2 container formats such as Transport Streams
12
 * (also ISO/IEC 13818-1 / ITU-T H.222.0) or MPEG-4 containers such as
13
 * MPEG-4 Part 14 / MP4 (ISO/IEC 14496-14). Support in wiretap for those
14
 * two formats is provided in mp2t.c and mp4.c, respectively.
15
 *
16
 * Wiretap Library
17
 * SPDX-License-Identifier: GPL-2.0-or-later
18
 */
19
20
#include "config.h"
21
#include "mpeg.h"
22
23
#include <sys/types.h>
24
25
#ifdef HAVE_UNISTD_H
26
#include <unistd.h>
27
#endif
28
29
#include "wsutil/mpeg-audio.h"
30
31
#include "wtap_module.h"
32
#include <wsutil/buffer.h>
33
#include "file_wrappers.h"
34
#include <stdlib.h>
35
#include <string.h>
36
#include <time.h>
37
38
0
#define PES_PREFIX 1
39
0
#define PES_VALID(n) (((n) >> 8 & 0xffffff) == PES_PREFIX)
40
41
typedef struct {
42
  nstime_t now;
43
  time_t t0;
44
  bool is_audio;
45
} mpeg_t;
46
47
static int mpeg_file_type_subtype = -1;
48
49
void register_mpeg(void);
50
51
static int
52
mpeg_resync(FILE_T fh, int *err)
53
0
{
54
0
  int64_t offset = file_tell(fh);
55
0
  int count = 0;
56
0
  int byte = file_getc(fh);
57
58
0
  while (byte != EOF) {
59
0
    if (byte == 0xff && count > 0) {
60
0
      byte = file_getc(fh);
61
0
      if (byte != EOF && (byte & 0xe0) == 0xe0)
62
0
        break;
63
0
    } else
64
0
      byte = file_getc(fh);
65
0
    count++;
66
0
  }
67
0
  if (file_seek(fh, offset, SEEK_SET, err) == -1)
68
0
    return 0;
69
0
  return count;
70
0
}
71
72
0
#define SCRHZ 27000000
73
74
static unsigned int
75
mpeg_read_audio_packet(wtap *wth, FILE_T fh, bool is_random, int *err, char **err_info)
76
0
{
77
0
  mpeg_t *mpeg = (mpeg_t *)wth->priv;
78
0
  unsigned int packet_size;
79
0
  uint32_t n;
80
0
  if (!wtap_read_bytes_or_eof(fh, &n, sizeof n, err, err_info))
81
0
    return 0;
82
0
  if (file_seek(fh, -(int64_t)(sizeof n), SEEK_CUR, err) == -1)
83
0
    return 0;
84
0
  n = g_ntohl(n);
85
0
  struct mpa mpa;
86
87
0
  MPA_UNMARSHAL(&mpa, n);
88
0
  if (MPA_VALID(&mpa)) {
89
0
    packet_size = MPA_BYTES(&mpa);
90
0
    if (!is_random) {
91
0
      mpeg->now.nsecs += MPA_DURATION_NS(&mpa);
92
0
      if (mpeg->now.nsecs >= 1000000000) {
93
0
        mpeg->now.secs++;
94
0
        mpeg->now.nsecs -= 1000000000;
95
0
      }
96
0
    }
97
0
  } else {
98
0
    if ((n & 0xffffff00) == 0x49443300) {
99
      /* We have an ID3v2 header; read the size */
100
0
      if (file_seek(fh, 6, SEEK_CUR, err) == -1)
101
0
        return 0;
102
0
      if (!wtap_read_bytes_or_eof(fh, &n, sizeof n, err, err_info))
103
0
        return 0;
104
0
      if (file_seek(fh, -(int64_t)(6+sizeof(n)), SEEK_CUR, err) == -1)
105
0
        return 0;
106
0
      n = g_ntohl(n);
107
108
      /* ID3v2 size does not include the 10-byte header */
109
0
      packet_size = decode_synchsafe_int(n) + 10;
110
0
    } else {
111
0
      packet_size = mpeg_resync(fh, err);
112
0
    }
113
0
  }
114
0
  return packet_size;
115
0
}
116
117
static unsigned int
118
mpeg_read_pes_packet(wtap *wth, FILE_T fh, bool is_random, int *err, char **err_info)
119
0
{
120
0
  mpeg_t *mpeg = (mpeg_t *)wth->priv;
121
0
  unsigned int packet_size = 0;
122
0
  uint32_t n;
123
0
  while (1) {
124
0
    if (!wtap_read_bytes_or_eof(fh, &n, sizeof n, err, err_info))
125
0
      return 0;
126
0
    if (file_seek(fh, -(int64_t)(sizeof n), SEEK_CUR, err) == -1)
127
0
      return 0;
128
0
    n = g_ntohl(n);
129
0
    if (PES_VALID(n)) {
130
0
      break;
131
0
    } else if (n == PES_PREFIX) {
132
0
      if (!wtap_read_bytes(fh, NULL, 1, err, err_info))
133
0
        return 0;
134
0
      break;
135
0
    } else if (n != 0) {
136
      /* XXX: We could try to recover from errors and
137
       * resynchronize to the next start code.
138
       */
139
0
      *err = WTAP_ERR_BAD_FILE;
140
0
      *err_info = ws_strdup("mpeg: Non-zero stuffing bytes before start code");
141
0
      return 0;
142
0
    }
143
0
    if (!wtap_read_bytes(fh, NULL, 2, err, err_info))
144
0
      return 0;
145
0
  }
146
147
0
  int64_t offset = file_tell(fh);
148
0
  uint8_t stream;
149
150
0
  if (!wtap_read_bytes(fh, NULL, 3, err, err_info))
151
0
    return 0;
152
153
0
  if (!wtap_read_bytes(fh, &stream, sizeof stream, err, err_info))
154
0
    return 0;
155
156
0
  if (stream == 0xba) {
157
0
    uint32_t pack1;
158
0
    uint32_t pack0;
159
0
    uint64_t pack;
160
0
    uint8_t stuffing;
161
162
0
    if (!wtap_read_bytes(fh, &pack1, sizeof pack1, err, err_info))
163
0
      return 0;
164
0
    if (!wtap_read_bytes(fh, &pack0, sizeof pack0, err, err_info))
165
0
      return 0;
166
0
    pack = (uint64_t)g_ntohl(pack1) << 32 | g_ntohl(pack0);
167
168
0
    switch (pack >> 62) {
169
0
      case 1:
170
0
        if (!wtap_read_bytes(fh, NULL, 1, err,
171
0
            err_info))
172
0
          return false;
173
0
        if (!wtap_read_bytes(fh, &stuffing,
174
0
            sizeof stuffing, err, err_info))
175
0
          return false;
176
0
        stuffing &= 0x07;
177
0
        packet_size = 14 + stuffing;
178
179
0
        if (!is_random) {
180
0
          uint64_t bytes = pack >> 16;
181
0
          uint64_t ts_val =
182
0
            (bytes >> 43 & 0x0007) << 30 |
183
0
            (bytes >> 27 & 0x7fff) << 15 |
184
0
            (bytes >> 11 & 0x7fff) << 0;
185
0
          unsigned ext = (unsigned)((bytes >> 1) & 0x1ff);
186
0
          uint64_t cr = 300 * ts_val + ext;
187
0
          unsigned rem = (unsigned)(cr % SCRHZ);
188
0
          mpeg->now.secs
189
0
            = mpeg->t0 + (time_t)(cr / SCRHZ);
190
0
          mpeg->now.nsecs
191
0
            = (int)(INT64_C(1000000000) * rem / SCRHZ);
192
0
        }
193
0
        break;
194
0
      default:
195
0
        packet_size = 12;
196
0
    }
197
0
  } else if (stream == 0xb9) {
198
    /* MPEG_program_end_code */
199
0
    packet_size = 4;
200
0
  } else {
201
0
    uint16_t length;
202
0
    if (!wtap_read_bytes(fh, &length, sizeof length, err, err_info))
203
0
      return false;
204
0
    length = g_ntohs(length);
205
0
    packet_size = 6 + length;
206
0
  }
207
208
0
  if (file_seek(fh, offset, SEEK_SET, err) == -1)
209
0
    return 0;
210
211
0
  return packet_size;
212
0
}
213
214
static bool
215
mpeg_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
216
    bool is_random, int *err, char **err_info)
217
0
{
218
0
  mpeg_t *mpeg = (mpeg_t *)wth->priv;
219
0
  unsigned int packet_size;
220
0
  nstime_t ts = mpeg->now;
221
222
0
  if (mpeg->is_audio) {
223
    /* mpeg_read_audio_packet calculates the duration of this
224
     * packet to determine an updated relative timestamp for the
225
     * next packet, if possible.
226
     */
227
0
    packet_size = mpeg_read_audio_packet(wth, fh, is_random, err, err_info);
228
0
  } else {
229
    /* mpeg_read_pes_packet uses the System Clock Reference counter
230
     * to produce a relative timestamp for this packet, if possible.
231
     */
232
0
    packet_size = mpeg_read_pes_packet(wth, fh, is_random, err, err_info);
233
0
  }
234
235
0
  if (packet_size == 0)
236
0
    return false;
237
238
0
  if (!wtap_read_bytes_buffer(fh, &rec->data, packet_size, err, err_info))
239
0
    return false;
240
241
0
  wtap_setup_packet_rec(rec, wth->file_encap);
242
0
  rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
243
244
0
  rec->presence_flags = 0; /* we may or may not have a time stamp */
245
0
  if (!is_random) {
246
    /* XXX - relative, not absolute, time stamps */
247
0
    rec->presence_flags = WTAP_HAS_TS;
248
0
    rec->ts = ts;
249
0
  }
250
0
  rec->rec_header.packet_header.caplen = packet_size;
251
0
  rec->rec_header.packet_header.len = packet_size;
252
253
0
  return true;
254
0
}
255
256
static bool
257
mpeg_read(wtap *wth, wtap_rec *rec, int *err,
258
    char **err_info, int64_t *data_offset)
259
0
{
260
0
  *data_offset = file_tell(wth->fh);
261
262
0
  return mpeg_read_packet(wth, wth->fh, rec, false, err, err_info);
263
0
}
264
265
static bool
266
mpeg_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec,
267
    int *err, char **err_info)
268
0
{
269
0
  if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
270
0
    return false;
271
272
0
  if (!mpeg_read_packet(wth, wth->random_fh, rec, true, err, err_info)) {
273
0
    if (*err == 0)
274
0
      *err = WTAP_ERR_SHORT_READ;
275
0
    return false;
276
0
  }
277
0
  return true;
278
0
}
279
280
const struct _mpeg_magic {
281
  size_t len;
282
  const char* match;
283
  bool is_audio;
284
} magic[] = {
285
  { 3, "TAG", true }, /* ID3v1 */
286
  /* XXX: ID3v1 tags come at the end of MP3 files, so in practice the
287
   * untagged magic number is used instead.
288
   */
289
  { 3, "ID3", true }, /* ID3v2 */
290
  { 3, "\0\0\1", false }, /* MPEG PES */
291
  { 2, "\xff\xfb", true }, /* MP3 (MPEG-1 Audio Layer 3, no CRC), taken from https://en.wikipedia.org/wiki/MP3#File_structure */
292
#if 0
293
  /* XXX: The value above is for MPEG-1 Audio Layer 3 with no CRC.
294
   * Only the first three nibbles are the guaranteed sync byte.
295
   * For the fourth nibble, the first bit is '1' for MPEG-1 and
296
   * '0' for MPEG-2 (i.e., extension to lower sampling rates),
297
   * the next two bits indicate the layer (1 for layer 3, 2 for
298
   * layer 2, 3 for layer 1, 0 reserved), and the last ("protection")
299
   * bit is 1 if there is no CRC and 0 if there is a CRC.
300
   *
301
   * The mpeg-audio dissector handles these, so wiretap should open
302
   * them. Including all of them might increase false positives though.
303
   */
304
  { 2, "\xff\xf2", true }, /* MPEG-2 Audio Layer 3, CRC */
305
  { 2, "\xff\xf3", true }, /* MPEG-2 Audio Layer 3, No CRC */
306
  { 2, "\xff\xf4", true }, /* MPEG-2 Audio Layer 2, CRC */
307
  { 2, "\xff\xf5", true }, /* MPEG-2 Audio Layer 2, No CRC */
308
  { 2, "\xff\xf6", true }, /* MPEG-2 Audio Layer 1, CRC */
309
  { 2, "\xff\xf7", true }, /* MPEG-2 Audio Layer 1, No CRC */
310
  { 2, "\xff\xfa", true }, /* MPEG-1 Audio Layer 3, CRC */
311
  { 2, "\xff\xfc", true }, /* MPEG-1 Audio Layer 2, CRC */
312
  { 2, "\xff\xfd", true }, /* MPEG-1 Audio Layer 2, No CRC */
313
  { 2, "\xff\xfe", true }, /* MPEG-1 Audio Layer 1, CRC */
314
  { 2, "\xff\xff", true }, /* MPEG-1 Audio Layer 1, No CRC */
315
#endif
316
  { 0, NULL, false }
317
};
318
319
/*
320
 * Even though this dissector uses magic numbers, it is registered in
321
 * file_access.c as OPEN_INFO_HEURISTIC because the magic numbers are
322
 * short and prone to false positives.
323
 *
324
 * XXX: There's room for improvement in detection if needed. A Program Stream
325
 * starts with the pack_start_code, \x00\x00\x01\xba, and an uncontainered
326
 * Video bitstream starts with the sequence_header_code, \x00\x00\x01\xb3.
327
 * We could use those instead of matching any PES packet, which would greatly
328
 * reduce false positives with e.g. PacketLogger files. (Unlike Transport
329
 * Streams, unaligned file starts are unlikely with PS.)
330
 *
331
 * Untagged MPEG Audio files would still have to be heuristics, though.
332
 */
333
wtap_open_return_val
334
mpeg_open(wtap *wth, int *err, char **err_info)
335
0
{
336
0
  char magic_buf[16];
337
0
  const struct _mpeg_magic* m;
338
0
  mpeg_t *mpeg;
339
340
0
  if (!wtap_read_bytes(wth->fh, magic_buf, sizeof magic_buf,
341
0
      err, err_info)) {
342
0
    if (*err != WTAP_ERR_SHORT_READ)
343
0
      return WTAP_OPEN_ERROR;
344
0
    return WTAP_OPEN_NOT_MINE;
345
0
  }
346
347
0
  for (m=magic; m->match; m++) {
348
0
    if (memcmp(magic_buf, m->match, m->len) == 0)
349
0
      goto good_magic;
350
0
  }
351
352
0
  return WTAP_OPEN_NOT_MINE;
353
354
0
good_magic:
355
  /* This appears to be a file with MPEG data. */
356
0
  if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
357
0
    return WTAP_OPEN_ERROR;
358
359
0
  wth->file_type_subtype = mpeg_file_type_subtype;
360
0
  wth->file_encap = WTAP_ENCAP_MPEG;
361
0
  wth->file_tsprec = WTAP_TSPREC_NSEC;
362
0
  wth->subtype_read = mpeg_read;
363
0
  wth->subtype_seek_read = mpeg_seek_read;
364
0
  wth->snapshot_length = 0;
365
366
0
  mpeg = g_new(mpeg_t, 1);
367
0
  wth->priv = (void *)mpeg;
368
0
  mpeg->now.secs = 0;
369
0
  mpeg->now.nsecs = 0;
370
0
  mpeg->t0 = mpeg->now.secs;
371
0
  mpeg->is_audio = m->is_audio;
372
373
0
  return WTAP_OPEN_MINE;
374
0
}
375
376
static const struct supported_block_type mpeg_blocks_supported[] = {
377
  /*
378
   * This file format divides the file up into a "packet" for
379
   * each frame, and doesn't support any options.
380
   */
381
  { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
382
};
383
384
static const struct file_type_subtype_info mpeg_info = {
385
  "MPEG", "mpeg", "mpeg", "mpg;mp3",
386
  false, BLOCKS_SUPPORTED(mpeg_blocks_supported),
387
  NULL, NULL, NULL
388
};
389
390
void register_mpeg(void)
391
14
{
392
14
  mpeg_file_type_subtype = wtap_register_file_type_subtype(&mpeg_info);
393
394
  /*
395
   * Register name for backwards compatibility with the
396
   * wtap_filetypes table in Lua.
397
   */
398
14
  wtap_register_backwards_compatibility_lua_name("MPEG",
399
14
      mpeg_file_type_subtype);
400
14
}
401
402
/*
403
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
404
 *
405
 * Local variables:
406
 * c-basic-offset: 8
407
 * tab-width: 8
408
 * indent-tabs-mode: t
409
 * End:
410
 *
411
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
412
 * :indentSize=8:tabSize=8:noTabs=false:
413
 */