Coverage Report

Created: 2026-03-30 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wiretap/radcom.c
Line
Count
Source
1
/* radcom.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 "radcom.h"
11
12
#include <string.h>
13
14
#include <wsutil/pint.h>
15
16
#include "wtap_module.h"
17
#include "file_wrappers.h"
18
19
/*
20
 * RADCOM WAN/LAN Analyzers
21
 *
22
 * Olivier Abad has added code to read Ethernet and LAPB captures from
23
 * RADCOM WAN/LAN Analyzers (see https://web.archive.org/web/20031231213434/http://www.radcom-inc.com/).
24
*/
25
26
struct frame_date {
27
  uint16_t  year;
28
  uint8_t month;
29
  uint8_t day;
30
  uint32_t  sec;    /* seconds since midnight */
31
  uint32_t  usec;
32
};
33
34
struct unaligned_frame_date {
35
  char  year[2];
36
  char  month;
37
  char  day;
38
  char  sec[4];   /* seconds since midnight */
39
  char  usec[4];
40
};
41
42
/* Found at the beginning of the file. Bytes 2 and 3 (D2:00) seem to be
43
 * different in some captures */
44
static const uint8_t radcom_magic[8] = {
45
  0x42, 0xD2, 0x00, 0x34, 0x12, 0x66, 0x22, 0x88
46
};
47
48
static const uint8_t encap_magic[4] = {
49
  0x00, 0x42, 0x43, 0x09
50
};
51
52
static const uint8_t active_time_magic[11] = {
53
  'A', 'c', 't', 'i', 'v', 'e', ' ', 'T', 'i', 'm', 'e'
54
};
55
56
/* RADCOM record header - followed by frame data (perhaps including FCS).
57
58
   "data_length" appears to be the length of packet data following
59
   the record header.  It's 0 in the last record.
60
61
   "length" appears to be the amount of captured packet data, and
62
   "real_length" might be the actual length of the frame on the wire -
63
   in some captures, it's the same as "length", and, in others,
64
   it's greater than "length".  In the last record, however, those
65
   may have bogus values (or is that some kind of trailer record?).
66
67
   "xxx" appears to be all-zero in all but the last record in one
68
   capture; if so, perhaps this indicates that the last record is,
69
   in fact, a trailer of some sort, and some field in the header
70
   is a record type. */
71
struct radcomrec_hdr {
72
  char  xxx[4];   /* unknown */
73
  char  data_length[2]; /* packet length? */
74
  char  xxy[5];   /* unknown */
75
  struct unaligned_frame_date date; /* date/time stamp of packet */
76
  char  real_length[2]; /* actual length of packet */
77
  char  length[2];  /* captured length of packet */
78
  char  xxz[2];   /* unknown */
79
  char  dce;    /* DCE/DTE flag (and other flags?) */
80
  char  xxw[9];   /* unknown */
81
};
82
83
static bool radcom_read(wtap *wth, wtap_rec *rec,
84
  int *err, char **err_info, int64_t *data_offset);
85
static bool radcom_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec,
86
  int *err, char **err_info);
87
static bool radcom_read_rec(wtap *wth, FILE_T fh, wtap_rec *rec,
88
  int *err, char **err_info);
89
90
static int radcom_file_type_subtype = -1;
91
92
void register_radcom(void);
93
94
wtap_open_return_val radcom_open(wtap *wth, int *err, char **err_info)
95
0
{
96
0
  uint8_t r_magic[8], t_magic[11], search_encap[7];
97
0
  struct frame_date start_date;
98
#if 0
99
  uint32_t sec;
100
  struct tm tm;
101
#endif
102
103
  /* Read in the string that should be at the start of a RADCOM file */
104
0
  if (!wtap_read_bytes(wth->fh, r_magic, 8, err, err_info)) {
105
0
    if (*err != WTAP_ERR_SHORT_READ)
106
0
      return WTAP_OPEN_ERROR;
107
0
    return WTAP_OPEN_NOT_MINE;
108
0
  }
109
110
  /* XXX: bytes 2 and 3 of the "magic" header seem to be different in some
111
   * captures. We force them to our standard value so that the test
112
   * succeeds (until we find if they have a special meaning, perhaps a
113
   * version number ?) */
114
0
  r_magic[1] = 0xD2;
115
0
  r_magic[2] = 0x00;
116
0
  if (memcmp(r_magic, radcom_magic, 8) != 0) {
117
0
    return WTAP_OPEN_NOT_MINE;
118
0
  }
119
120
  /* Look for the "Active Time" string. The "frame_date" structure should
121
   * be located 32 bytes before the beginning of this string */
122
0
  if (!wtap_read_bytes(wth->fh, t_magic, 11, err, err_info)) {
123
0
    if (*err != WTAP_ERR_SHORT_READ)
124
0
      return WTAP_OPEN_ERROR;
125
0
    return WTAP_OPEN_NOT_MINE;
126
0
  }
127
0
  while (memcmp(t_magic, active_time_magic, 11) != 0)
128
0
  {
129
0
    if (file_seek(wth->fh, -10, SEEK_CUR, err) == -1)
130
0
      return WTAP_OPEN_ERROR;
131
0
    if (!wtap_read_bytes(wth->fh, t_magic, 11, 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
0
  }
137
0
  if (file_seek(wth->fh, -43, SEEK_CUR, err) == -1)
138
0
    return WTAP_OPEN_ERROR;
139
140
  /* Get capture start time */
141
0
  if (!wtap_read_bytes(wth->fh, &start_date, sizeof(struct frame_date),
142
0
      err, err_info)) {
143
0
    if (*err != WTAP_ERR_SHORT_READ)
144
0
      return WTAP_OPEN_ERROR;
145
0
    return WTAP_OPEN_NOT_MINE;
146
0
  }
147
148
  /* So what time is this? */
149
0
  if (!wtap_read_bytes(wth->fh, NULL, sizeof(struct frame_date),
150
0
      err, err_info)) {
151
0
    if (*err != WTAP_ERR_SHORT_READ)
152
0
      return WTAP_OPEN_ERROR;
153
0
    return WTAP_OPEN_NOT_MINE;
154
0
  }
155
156
0
  for (;;) {
157
0
    if (!wtap_read_bytes(wth->fh, search_encap, 4,
158
0
        err, err_info)) {
159
0
      if (*err != WTAP_ERR_SHORT_READ)
160
0
        return WTAP_OPEN_ERROR;
161
0
      return WTAP_OPEN_NOT_MINE;
162
0
    }
163
164
0
    if (memcmp(encap_magic, search_encap, 4) == 0)
165
0
      break;
166
167
    /*
168
     * OK, that's not it, go forward 1 byte - reading
169
     * the magic moved us forward 4 bytes, so seeking
170
     * backward 3 bytes moves forward 1 byte - and
171
     * try the 4 bytes at that offset.
172
     */
173
0
    if (file_seek(wth->fh, -3, SEEK_CUR, err) == -1)
174
0
      return WTAP_OPEN_ERROR;
175
0
  }
176
0
  if (!wtap_read_bytes(wth->fh, NULL, 12, err, err_info)) {
177
0
    if (*err != WTAP_ERR_SHORT_READ)
178
0
      return WTAP_OPEN_ERROR;
179
0
    return WTAP_OPEN_NOT_MINE;
180
0
  }
181
0
  if (!wtap_read_bytes(wth->fh, search_encap, 4, err, err_info)) {
182
0
    if (*err != WTAP_ERR_SHORT_READ)
183
0
      return WTAP_OPEN_ERROR;
184
0
    return WTAP_OPEN_NOT_MINE;
185
0
  }
186
187
  /* This is a radcom file */
188
0
  wth->file_type_subtype = radcom_file_type_subtype;
189
0
  wth->subtype_read = radcom_read;
190
0
  wth->subtype_seek_read = radcom_seek_read;
191
0
  wth->snapshot_length = 0; /* not available in header, only in frame */
192
0
  wth->file_tsprec = WTAP_TSPREC_USEC;
193
194
#if 0
195
  tm.tm_year = pletohu16(&start_date.year)-1900;
196
  tm.tm_mon = start_date.month-1;
197
  tm.tm_mday = start_date.day;
198
  sec = pletohu32(&start_date.sec);
199
  tm.tm_hour = sec/3600;
200
  tm.tm_min = (sec%3600)/60;
201
  tm.tm_sec = sec%60;
202
  tm.tm_isdst = -1;
203
#endif
204
205
0
  if (memcmp(search_encap, "LAPB", 4) == 0)
206
0
    wth->file_encap = WTAP_ENCAP_LAPB;
207
0
  else if (memcmp(search_encap, "Ethe", 4) == 0)
208
0
    wth->file_encap = WTAP_ENCAP_ETHERNET;
209
0
  else if (memcmp(search_encap, "ATM/", 4) == 0)
210
0
    wth->file_encap = WTAP_ENCAP_ATM_RFC1483;
211
0
  else {
212
0
    *err = WTAP_ERR_UNSUPPORTED;
213
0
    *err_info = ws_strdup_printf("radcom: network type \"%.4s\" unknown", search_encap);
214
0
    return WTAP_OPEN_ERROR;
215
0
  }
216
217
#if 0
218
  if (!wtap_read_bytes(wth->fh, &next_date, sizeof(struct frame_date),
219
      err, err_info))
220
    return WTAP_OPEN_ERROR;
221
222
  while (memcmp(&start_date, &next_date, 4)) {
223
    if (file_seek(wth->fh, 1-sizeof(struct frame_date), SEEK_CUR, err) == -1)
224
      return WTAP_OPEN_ERROR;
225
    if (!wtap_read_bytes(wth->fh, &next_date, sizeof(struct frame_date),
226
        err, err_info))
227
      return WTAP_OPEN_ERROR;
228
  }
229
#endif
230
231
0
  if (wth->file_encap == WTAP_ENCAP_ETHERNET) {
232
0
    if (!wtap_read_bytes(wth->fh, NULL, 294, err, err_info))
233
0
      return WTAP_OPEN_ERROR;
234
0
  } else if (wth->file_encap == WTAP_ENCAP_LAPB) {
235
0
    if (!wtap_read_bytes(wth->fh, NULL, 297, err, err_info))
236
0
      return WTAP_OPEN_ERROR;
237
0
  } else if (wth->file_encap == WTAP_ENCAP_ATM_RFC1483) {
238
0
    if (!wtap_read_bytes(wth->fh, NULL, 504, err, err_info))
239
0
      return WTAP_OPEN_ERROR;
240
0
  }
241
242
  /*
243
   * Add an IDB; we don't know how many interfaces were involved,
244
   * so we just say one interface, about which we only know
245
   * the link-layer type, snapshot length, and time stamp
246
   * resolution.
247
   */
248
0
  wtap_add_generated_idb(wth);
249
250
0
  return WTAP_OPEN_MINE;
251
0
}
252
253
/* Read the next packet */
254
static bool radcom_read(wtap *wth, wtap_rec *rec, int *err, char **err_info,
255
      int64_t *data_offset)
256
0
{
257
0
  char  fcs[2];
258
259
0
  *data_offset = file_tell(wth->fh);
260
261
  /* Read record. */
262
0
  if (!radcom_read_rec(wth, wth->fh, rec, err, err_info)) {
263
    /* Read error or EOF */
264
0
    return false;
265
0
  }
266
267
0
  if (wth->file_encap == WTAP_ENCAP_LAPB) {
268
    /* Read the FCS.
269
       XXX - should we have some way of indicating the
270
       presence and size of an FCS to our caller?
271
       That'd let us handle other file types as well. */
272
0
    if (!wtap_read_bytes(wth->fh, &fcs, sizeof fcs, err, err_info))
273
0
      return false;
274
0
  }
275
276
0
  return true;
277
0
}
278
279
static bool
280
radcom_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec,
281
     int *err, char **err_info)
282
0
{
283
0
  if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
284
0
    return false;
285
286
  /* Read record. */
287
0
  if (!radcom_read_rec(wth, wth->random_fh, rec, err, err_info)) {
288
    /* Read error or EOF */
289
0
    if (*err == 0) {
290
      /* EOF means "short read" in random-access mode */
291
0
      *err = WTAP_ERR_SHORT_READ;
292
0
    }
293
0
    return false;
294
0
  }
295
0
  return true;
296
0
}
297
298
static bool
299
radcom_read_rec(wtap *wth, FILE_T fh, wtap_rec *rec, int *err, char **err_info)
300
0
{
301
0
  struct radcomrec_hdr hdr;
302
0
  uint16_t data_length, real_length, length;
303
0
  uint32_t sec;
304
0
  struct tm tm;
305
0
  uint8_t atmhdr[8];
306
307
0
  if (!wtap_read_bytes_or_eof(fh, &hdr, sizeof hdr, err, err_info))
308
0
    return false;
309
310
0
  data_length = pletohu16(&hdr.data_length);
311
0
  if (data_length == 0) {
312
    /*
313
     * The last record appears to have 0 in its "data_length"
314
     * field, but non-zero values in other fields, so we
315
     * check for that and treat it as an EOF indication.
316
     */
317
0
    *err = 0;
318
0
    return false;
319
0
  }
320
0
  length = pletohu16(&hdr.length);
321
0
  real_length = pletohu16(&hdr.real_length);
322
  /*
323
   * The maximum value of length is 65535, which is less than
324
   * WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check
325
   * it.
326
   */
327
328
0
  wtap_setup_packet_rec(rec, wth->file_encap);
329
0
  rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
330
0
  rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
331
332
0
  tm.tm_year = pletohu16(&hdr.date.year)-1900;
333
0
  tm.tm_mon = (hdr.date.month&0x0f)-1;
334
0
  tm.tm_mday = hdr.date.day;
335
0
  sec = pletohu32(&hdr.date.sec);
336
0
  tm.tm_hour = sec/3600;
337
0
  tm.tm_min = (sec%3600)/60;
338
0
  tm.tm_sec = sec%60;
339
0
  tm.tm_isdst = -1;
340
0
  rec->ts.secs = mktime(&tm);
341
0
  rec->ts.nsecs = pletohu32(&hdr.date.usec) * 1000;
342
343
0
  switch (wth->file_encap) {
344
345
0
  case WTAP_ENCAP_ETHERNET:
346
    /* XXX - is there an FCS? */
347
0
    rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1;
348
0
    break;
349
350
0
  case WTAP_ENCAP_LAPB:
351
0
    rec->rec_header.packet_header.pseudo_header.dte_dce.flags = (hdr.dce & 0x1) ?
352
0
        0x00 : FROM_DCE;
353
0
    length -= 2; /* FCS */
354
0
    real_length -= 2;
355
0
    break;
356
357
0
  case WTAP_ENCAP_ATM_RFC1483:
358
    /*
359
     * XXX - is this stuff a pseudo-header?
360
     * The direction appears to be in the "hdr.dce" field.
361
     */
362
0
    if (!wtap_read_bytes(fh, atmhdr, sizeof atmhdr, err,
363
0
        err_info))
364
0
      return false; /* Read error */
365
0
    length -= 8;
366
0
    real_length -= 8;
367
0
    break;
368
0
  }
369
370
0
  rec->rec_header.packet_header.len = real_length;
371
0
  rec->rec_header.packet_header.caplen = length;
372
373
  /*
374
   * Read the packet data.
375
   */
376
0
  if (!wtap_read_bytes_buffer(fh, &rec->data, length, err, err_info))
377
0
    return false; /* Read error */
378
379
0
  return true;
380
0
}
381
382
static const struct supported_block_type radcom_blocks_supported[] = {
383
  /*
384
   * We support packet blocks, with no comments or other options.
385
   */
386
  { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
387
};
388
389
static const struct file_type_subtype_info radcom_info = {
390
  "RADCOM WAN/LAN analyzer", "radcom", NULL, NULL,
391
  false, BLOCKS_SUPPORTED(radcom_blocks_supported),
392
  NULL, NULL, NULL
393
};
394
395
void register_radcom(void)
396
14
{
397
14
  radcom_file_type_subtype = wtap_register_file_type_subtype(&radcom_info);
398
399
  /*
400
   * Register name for backwards compatibility with the
401
   * wtap_filetypes table in Lua.
402
   */
403
14
  wtap_register_backwards_compatibility_lua_name("RADCOM",
404
14
      radcom_file_type_subtype);
405
14
}
406
407
/*
408
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
409
 *
410
 * Local variables:
411
 * c-basic-offset: 8
412
 * tab-width: 8
413
 * indent-tabs-mode: t
414
 * End:
415
 *
416
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
417
 * :indentSize=8:tabSize=8:noTabs=false:
418
 */