Coverage Report

Created: 2025-08-04 07:15

/src/wireshark/wiretap/capsa.c
Line
Count
Source (jump to first uncovered line)
1
/* capsa.c
2
 *
3
 * Wiretap Library
4
 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
5
 *
6
 * SPDX-License-Identifier: GPL-2.0-or-later
7
 */
8
9
#include "config.h"
10
#include "capsa.h"
11
12
#include <string.h>
13
#include "wtap-int.h"
14
#include "file_wrappers.h"
15
#include <wsutil/ws_assert.h>
16
17
/*
18
 * A file begins with a header containing:
19
 *
20
 *   a 4-byte magic number, with 'c', 'p', 's', 'e';
21
 *
22
 *   either a 2-byte little-endian "format indicator" (version number?),
23
 *   or a 1-byte major version number followed by a 1-byte minor version
24
 *   number, or a 1-byte "format indicator" followed by something else
25
 *   that's always been 0;
26
 *
27
 *   a 2-byte 0xe8 0x03 (1000 - a data rate?  megabits/second?)
28
 *
29
 *   4 bytes of 0x01 0x00 0x01 0x00;
30
 *
31
 *   either a 4-byte little-endian file size followed by 0x00 0x00 0x00 0x00
32
 *   or an 8-byte little-endian file size;
33
 *
34
 *   a 4-byte little-endian packet count (in dns_error_of_udp, it exceeds?)
35
 *
36
 *   a 4-byte little-endian number?
37
 *
38
 *   hex 2c 01 c8 00 00 00 da 36 00 00 00 00 00 00;
39
 *
40
 *   the same 4-byte little-endian number as above (yes, misaligned);
41
 *
42
 *   0x01 or 0x03;
43
 *
44
 *   a bunch of 0s, up to an offset of 0x36d6;
45
 *
46
 *   more stuff.
47
 *
48
 * Following that is a sequence of { record offset block, up to 200 records }
49
 * pairs.
50
 *
51
 * A record offset block has 1 byte with the value 0xfe, a sequence of
52
 * up to 200 4-byte little-endian record offsets, and 4 or more bytes
53
 * of unknown data, making the block 805 bytes long.
54
 *
55
 * The record offsets are offsets, from the beginning of the record offset
56
 * block (i.e., from the 0xfe byte), of the records following the block.
57
 */
58
59
/* Magic number in Capsa files. */
60
static const char capsa_magic[] = {
61
  'c', 'p', 's', 'e'
62
};
63
64
/*
65
 * Before each group of 200 or fewer records there's a block of frame
66
 * offsets, giving the offsets, from the beginning of that block minus
67
 * one(1), of the next N records.
68
 */
69
0
#define N_RECORDS_PER_GROUP 200
70
71
/* Capsa (format indicator 1) record header. */
72
struct capsarec_hdr {
73
  uint32_t unknown1;  /* low-order 32 bits of a number? */
74
  uint32_t unknown2;  /* 0x00 0x00 0x00 0x00 */
75
  uint32_t timestamplo; /* low-order 32 bits of the time stamp, in microseconds since January 1, 1970, 00:00:00 UTC */
76
  uint32_t timestamphi; /* high-order 32 bits of the time stamp, in microseconds since January 1, 1970, 00:00:00 UTC */
77
  uint16_t rec_len; /* length of record */
78
  uint16_t incl_len;  /* number of octets captured in file */
79
  uint16_t orig_len;  /* actual length of packet */
80
  uint16_t unknown5;  /* 0x00 0x00 */
81
  uint8_t  count1;  /* count1*4 bytes after unknown8 */
82
  uint8_t  count2;  /* count2*4 bytes after that */
83
  uint16_t unknown7;  /* 0x01 0x10 */
84
  uint32_t unknown8;  /* 0x00 0x00 0x00 0x00 or random numbers */
85
};
86
87
/* Packet Builder (format indicator 2) record header. */
88
struct pbrec_hdr {
89
  uint16_t rec_len; /* length of record */
90
  uint16_t incl_len;  /* number of octets captured in file */
91
  uint16_t orig_len;  /* actual length of packet */
92
  uint16_t unknown1;
93
  uint16_t unknown2;
94
  uint16_t unknown3;
95
  uint32_t unknown4;
96
  uint32_t timestamplo; /* low-order 32 bits of the time stamp, in microseconds since January 1, 1970, 00:00:00 UTC */
97
  uint32_t timestamphi; /* high-order 32 bits of the time stamp, in microseconds since January 1, 1970, 00:00:00 UTC */
98
  uint32_t unknown5;
99
  uint32_t unknown6;
100
};
101
102
typedef struct {
103
  uint16_t format_indicator;
104
  uint32_t number_of_frames;
105
  uint32_t frame_count;
106
  int64_t  base_offset;
107
  uint32_t record_offsets[N_RECORDS_PER_GROUP];
108
} capsa_t;
109
110
static bool capsa_read(wtap *wth, wtap_rec *rec,
111
    int *err, char **err_info, int64_t *data_offset);
112
static bool capsa_seek_read(wtap *wth, int64_t seek_off,
113
    wtap_rec *rec, int *err, char **err_info);
114
static int capsa_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
115
    int *err, char **err_info);
116
117
static int capsa_file_type_subtype = -1;
118
static int packet_builder_file_type_subtype = -1;
119
120
void register_capsa(void);
121
122
wtap_open_return_val capsa_open(wtap *wth, int *err, char **err_info)
123
0
{
124
0
  char magic[sizeof capsa_magic];
125
0
  uint16_t format_indicator;
126
0
  int file_type_subtype;
127
0
  uint32_t number_of_frames;
128
0
  capsa_t *capsa;
129
130
  /* Read in the string that should be at the start of a Capsa file */
131
0
  if (!wtap_read_bytes(wth->fh, magic, sizeof magic, err, err_info)) {
132
0
    if (*err != WTAP_ERR_SHORT_READ)
133
0
      return WTAP_OPEN_ERROR;
134
0
    return WTAP_OPEN_NOT_MINE;
135
0
  }
136
137
0
  if (memcmp(magic, capsa_magic, sizeof capsa_magic) != 0) {
138
0
    return WTAP_OPEN_NOT_MINE;
139
0
  }
140
141
  /* Read the mysterious "format indicator" */
142
0
  if (!wtap_read_bytes(wth->fh, &format_indicator, sizeof format_indicator,
143
0
      err, err_info))
144
0
    return WTAP_OPEN_ERROR;
145
0
  format_indicator = GUINT16_FROM_LE(format_indicator);
146
147
  /*
148
   * Make sure it's a format we support.
149
   */
150
0
  switch (format_indicator) {
151
152
0
  case 1:   /* Capsa */
153
0
    file_type_subtype = capsa_file_type_subtype;
154
0
    break;
155
156
0
  case 2:   /* Packet Builder */
157
0
    file_type_subtype = packet_builder_file_type_subtype;
158
0
    break;
159
160
0
  default:
161
0
    *err = WTAP_ERR_UNSUPPORTED;
162
0
    *err_info = ws_strdup_printf("capsa: format indicator %u unsupported",
163
0
        format_indicator);
164
0
    return WTAP_OPEN_ERROR;
165
0
  }
166
167
  /*
168
   * Link speed, in megabytes/second?
169
   */
170
0
  if (!wtap_read_bytes(wth->fh, NULL, 2, err, err_info))
171
0
    return WTAP_OPEN_ERROR;
172
173
  /*
174
   * Flags of some sort?  Four 1-byte numbers, two of which are 1
175
   * and two of which are zero?  Two 2-byte numbers or flag fields,
176
   * both of which are 1?
177
   */
178
0
  if (!wtap_read_bytes(wth->fh, NULL, 4, err, err_info))
179
0
    return WTAP_OPEN_ERROR;
180
181
  /*
182
   * File size, in bytes.
183
   */
184
0
  if (!wtap_read_bytes(wth->fh, NULL, 4, err, err_info))
185
0
    return WTAP_OPEN_ERROR;
186
187
  /*
188
   * Zeroes?  Or upper 4 bytes of file size?
189
   */
190
0
  if (!wtap_read_bytes(wth->fh, NULL, 4, err, err_info))
191
0
    return WTAP_OPEN_ERROR;
192
193
  /*
194
   * Count of packets.
195
   */
196
0
  if (!wtap_read_bytes(wth->fh, &number_of_frames, sizeof number_of_frames,
197
0
      err, err_info))
198
0
    return WTAP_OPEN_ERROR;
199
0
  number_of_frames = GUINT32_FROM_LE(number_of_frames);
200
201
  /*
202
   * Skip past what we think is file header.
203
   */
204
0
  if (!file_seek(wth->fh, 0x44ef, SEEK_SET, err))
205
0
    return WTAP_OPEN_ERROR;
206
207
0
  wth->file_type_subtype = file_type_subtype;
208
0
  capsa = g_new(capsa_t, 1);
209
0
  capsa->format_indicator = format_indicator;
210
0
  capsa->number_of_frames = number_of_frames;
211
0
  capsa->frame_count = 0;
212
0
  wth->priv = (void *)capsa;
213
0
  wth->subtype_read = capsa_read;
214
0
  wth->subtype_seek_read = capsa_seek_read;
215
  /*
216
   * XXX - we've never seen a Wi-Fi Capsa capture, so we don't
217
   * yet know how to handle them.
218
   */
219
0
  wth->file_encap = WTAP_ENCAP_ETHERNET;
220
0
  wth->snapshot_length = 0; /* not available in header */
221
0
  wth->file_tsprec = WTAP_TSPREC_USEC;
222
223
  /*
224
   * Add an IDB; we don't know how many interfaces were
225
   * involved, so we just say one interface, about which
226
   * we only know the link-layer type, snapshot length,
227
   * and time stamp resolution.
228
   */
229
0
  wtap_add_generated_idb(wth);
230
231
0
  return WTAP_OPEN_MINE;
232
0
}
233
234
/* Read the next packet */
235
static bool capsa_read(wtap *wth, wtap_rec *rec,
236
    int *err, char **err_info, int64_t *data_offset)
237
0
{
238
0
  capsa_t *capsa = (capsa_t *)wth->priv;
239
0
  uint32_t frame_within_block;
240
0
  int padbytes;
241
242
0
  if (capsa->frame_count == capsa->number_of_frames) {
243
    /*
244
     * No more frames left.  Return an EOF.
245
     */
246
0
    *err = 0;
247
0
    return false;
248
0
  }
249
0
  frame_within_block = capsa->frame_count % N_RECORDS_PER_GROUP;
250
0
  if (frame_within_block == 0) {
251
    /*
252
     * Here's a record offset block.
253
     * Get the offset of the block, and then skip the
254
     * first byte.
255
     */
256
0
    capsa->base_offset = file_tell(wth->fh);
257
0
    if (!wtap_read_bytes(wth->fh, NULL, 1, err, err_info))
258
0
      return false;
259
260
    /*
261
     * Now read the record offsets.
262
     */
263
0
    if (!wtap_read_bytes(wth->fh, &capsa->record_offsets,
264
0
        sizeof capsa->record_offsets, err, err_info))
265
0
      return false;
266
267
    /*
268
     * And finish processing all 805 bytes by skipping
269
     * the last 4 bytes.
270
     */
271
0
    if (!wtap_read_bytes(wth->fh, NULL, 4, err, err_info))
272
0
      return false;
273
0
  }
274
275
0
  *data_offset = capsa->base_offset +
276
0
      GUINT32_FROM_LE(capsa->record_offsets[frame_within_block]);
277
0
  if (!file_seek(wth->fh, *data_offset, SEEK_SET, err))
278
0
    return false;
279
280
0
  padbytes = capsa_read_packet(wth, wth->fh, rec, err, err_info);
281
0
  if (padbytes == -1)
282
0
    return false;
283
284
  /*
285
   * Skip over the padding, if any.
286
   */
287
0
  if (padbytes != 0) {
288
0
    if (!wtap_read_bytes(wth->fh, NULL, padbytes, err, err_info))
289
0
      return false;
290
0
  }
291
292
0
  capsa->frame_count++;
293
294
0
  return true;
295
0
}
296
297
static bool
298
capsa_seek_read(wtap *wth, int64_t seek_off,
299
    wtap_rec *rec, int *err, char **err_info)
300
0
{
301
0
  if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
302
0
    return false;
303
304
0
  if (capsa_read_packet(wth, wth->random_fh, rec, err, err_info) == -1) {
305
0
    if (*err == 0)
306
0
      *err = WTAP_ERR_SHORT_READ;
307
0
    return false;
308
0
  }
309
0
  return true;
310
0
}
311
312
static int
313
capsa_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
314
    int *err, char **err_info)
315
0
{
316
0
  capsa_t *capsa = (capsa_t *)wth->priv;
317
0
  struct capsarec_hdr capsarec_hdr;
318
0
  struct pbrec_hdr pbrec_hdr;
319
0
  uint32_t rec_size;
320
0
  uint32_t packet_size;
321
0
  uint32_t orig_size;
322
0
  uint32_t header_size;
323
0
  uint64_t timestamp;
324
325
  /* Read record header. */
326
0
  switch (capsa->format_indicator) {
327
328
0
  case 1:
329
0
    if (!wtap_read_bytes_or_eof(fh, &capsarec_hdr,
330
0
        sizeof capsarec_hdr, err, err_info))
331
0
      return -1;
332
0
    rec_size = GUINT16_FROM_LE(capsarec_hdr.rec_len);
333
0
    orig_size = GUINT16_FROM_LE(capsarec_hdr.orig_len);
334
0
    packet_size = GUINT16_FROM_LE(capsarec_hdr.incl_len);
335
0
    header_size = sizeof capsarec_hdr;
336
0
    timestamp = (((uint64_t)GUINT32_FROM_LE(capsarec_hdr.timestamphi))<<32) + GUINT32_FROM_LE(capsarec_hdr.timestamplo);
337
338
    /*
339
     * OK, the rest of this is variable-length.
340
     * We skip: (count1+count2)*4 bytes.
341
     * XXX - what is that?  Measured statistics?
342
     * Calculated statistics?
343
     */
344
0
    if (!wtap_read_bytes(fh, NULL,
345
0
        (capsarec_hdr.count1 + capsarec_hdr.count2)*4,
346
0
        err, err_info))
347
0
      return -1;
348
0
    header_size += (capsarec_hdr.count1 + capsarec_hdr.count2)*4;
349
0
    break;
350
351
0
  case 2:
352
0
    if (!wtap_read_bytes_or_eof(fh, &pbrec_hdr,
353
0
        sizeof pbrec_hdr, err, err_info))
354
0
      return -1;
355
0
    rec_size = GUINT16_FROM_LE(pbrec_hdr.rec_len);
356
0
    orig_size = GUINT16_FROM_LE(pbrec_hdr.orig_len);
357
0
    packet_size = GUINT16_FROM_LE(pbrec_hdr.incl_len);
358
0
    header_size = sizeof pbrec_hdr;
359
0
    timestamp = (((uint64_t)GUINT32_FROM_LE(pbrec_hdr.timestamphi))<<32) + GUINT32_FROM_LE(pbrec_hdr.timestamplo);
360
    /*
361
     * XXX - from the results of some conversions between
362
     * Capsa format and pcap by Colasoft Packet Builder,
363
     * I do not trust its conversion of time stamps (at
364
     * least one of Colasoft's sample files, when
365
     * converted to pcap format, has, as its time stamps,
366
     * time stamps on the day after the conversion was
367
     * done, which seems like more than just coincidence).
368
     */
369
0
    break;
370
371
0
  default:
372
0
    ws_assert_not_reached();
373
0
    *err = WTAP_ERR_INTERNAL;
374
0
    *err_info = ws_strdup_printf("capsa: format indicator is %u", capsa->format_indicator);
375
0
    return -1;
376
0
  }
377
0
  if (orig_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
378
    /*
379
     * Probably a corrupt capture file; don't blow up trying
380
     * to allocate space for an immensely-large packet.
381
     */
382
0
    *err = WTAP_ERR_BAD_FILE;
383
0
    *err_info = ws_strdup_printf("capsa: File has %u-byte original length, bigger than maximum of %u",
384
0
        orig_size, WTAP_MAX_PACKET_SIZE_STANDARD);
385
0
    return -1;
386
0
  }
387
0
  if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
388
    /*
389
     * Probably a corrupt capture file; don't blow up trying
390
     * to allocate space for an immensely-large packet.
391
     */
392
0
    *err = WTAP_ERR_BAD_FILE;
393
0
    *err_info = ws_strdup_printf("capsa: File has %u-byte packet, bigger than maximum of %u",
394
0
        packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
395
0
    return -1;
396
0
  }
397
0
  if (header_size + packet_size > rec_size) {
398
    /*
399
     * Probably a corrupt capture file.
400
     */
401
0
    *err = WTAP_ERR_BAD_FILE;
402
0
    *err_info = ws_strdup_printf("capsa: File has %u-byte packet with %u-byte record header, bigger than record size %u",
403
0
        packet_size, header_size, rec_size);
404
0
    return -1;
405
0
  }
406
407
  /*
408
   * The "on the wire" record size always includes the CRC.
409
   * If it's greater than the "captured" size by 4, then
410
   * we subtract 4 from it, to reflect the way the "on the wire"
411
   * record size works for other file formats.
412
   */
413
0
  if (orig_size == packet_size + 4)
414
0
    orig_size = packet_size;
415
416
  /*
417
   * We assume there's no FCS in this frame.
418
   * XXX - is there ever one?
419
   */
420
0
  rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
421
422
0
  wtap_setup_packet_rec(rec, wth->file_encap);
423
0
  rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
424
0
  rec->rec_header.packet_header.caplen = packet_size;
425
0
  rec->rec_header.packet_header.len = orig_size;
426
0
  rec->presence_flags = WTAP_HAS_CAP_LEN|WTAP_HAS_TS;
427
0
  rec->ts.secs = (time_t)(timestamp / 1000000);
428
0
  rec->ts.nsecs = ((int)(timestamp % 1000000))*1000;
429
430
  /*
431
   * Read the packet data.
432
   */
433
0
  if (!wtap_read_bytes_buffer(fh, &rec->data,
434
0
      rec->rec_header.packet_header.caplen, err, err_info))
435
0
    return -1; /* failed */
436
437
0
  return rec_size - (header_size + packet_size);
438
0
}
439
440
static const struct supported_block_type capsa_blocks_supported[] = {
441
  /*
442
   * We support packet blocks, with no comments or other options.
443
   */
444
  { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
445
};
446
447
static const struct file_type_subtype_info capsa_info = {
448
  "Colasoft Capsa format", "capsa", "cscpkt", NULL,
449
  false, BLOCKS_SUPPORTED(capsa_blocks_supported),
450
  NULL, NULL, NULL
451
};
452
453
static const struct supported_block_type packet_builder_blocks_supported[] = {
454
  /*
455
   * We support packet blocks, with no comments or other options.
456
   */
457
  { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
458
};
459
460
static const struct file_type_subtype_info packet_builder_info = {
461
  "Colasoft Packet Builder format", "colasoft-pb", "cscpkt", NULL,
462
  false, BLOCKS_SUPPORTED(packet_builder_blocks_supported),
463
  NULL, NULL, NULL
464
};
465
466
void register_capsa(void)
467
14
{
468
14
  capsa_file_type_subtype = wtap_register_file_type_subtype(&capsa_info);
469
14
  packet_builder_file_type_subtype = wtap_register_file_type_subtype(&packet_builder_info);
470
471
  /*
472
   * Register names for backwards compatibility with the
473
   * wtap_filetypes table in Lua.
474
   */
475
14
  wtap_register_backwards_compatibility_lua_name("COLASOFT_CAPSA",
476
14
      capsa_file_type_subtype);
477
14
  wtap_register_backwards_compatibility_lua_name("COLASOFT_PACKET_BUILDER",
478
14
      packet_builder_file_type_subtype);
479
14
}
480
481
/*
482
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
483
 *
484
 * Local variables:
485
 * c-basic-offset: 8
486
 * tab-width: 8
487
 * indent-tabs-mode: t
488
 * End:
489
 *
490
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
491
 * :indentSize=8:tabSize=8:noTabs=false:
492
 */