Coverage Report

Created: 2026-01-02 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wiretap/eyesdn.c
Line
Count
Source
1
/* eyesdn.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 "eyesdn.h"
11
#include "wtap_module.h"
12
#include "file_wrappers.h"
13
14
#include <stdlib.h>
15
#include <string.h>
16
17
#include <wsutil/pint.h>
18
19
static int eyesdn_file_type_subtype = -1;
20
21
void register_eyesdn(void);
22
23
/* This module reads the output of the EyeSDN USB S0/E1 ISDN probes
24
 * They store HDLC frames of D and B channels in a binary format
25
 * The fileformat is
26
 *
27
 * 1-6 Byte: EyeSDN - Magic
28
 * 7-n Byte: Frames
29
 *
30
 * Each Frame starts with the 0xff Flag byte
31
 * - Bytes 0-2: timestamp (usec in network byte order)
32
 * - Bytes 3-7: timestamp (40bits sec since 1970 in network byte order)
33
 * - Byte 8: channel (0 for D channel, 1-30 for B1-B30)
34
 * - Byte 9: Sender Bit 0(0 NT, 1 TE), Protocol in Bits 7:1, see enum
35
 * - Byte 10-11: frame size in bytes
36
 * - Byte 12-n: Frame Payload
37
 *
38
 * All multibyte values are represented in network byte order
39
 * The frame is terminated with a flag character (0xff)
40
 * bytes 0xff within a frame are escaped using the 0xfe escape character
41
 * the byte following the escape character is decremented by two:
42
 * so 0xfe 0xfd is actually a 0xff
43
 * Characters that need to be escaped are 0xff and 0xfe
44
 */
45
46
47
static bool esc_read(FILE_T fh, uint8_t *buf, int len, int *err, char **err_info)
48
0
{
49
0
  int i;
50
0
  int value;
51
52
0
  for(i=0; i<len; i++) {
53
0
    value=file_getc(fh);
54
0
    if(value==-1) {
55
      /* EOF or error */
56
0
      *err=file_error(fh, err_info);
57
0
      if(*err==0)
58
0
        *err=WTAP_ERR_SHORT_READ;
59
0
      return false;
60
0
    }
61
0
    if(value==0xff) {
62
      /* error !!, read into next frame */
63
0
      *err=WTAP_ERR_BAD_FILE;
64
0
      *err_info=g_strdup("eyesdn: No flag character seen in frame");
65
0
      return false;
66
0
    }
67
0
    if(value==0xfe) {
68
      /* we need to escape */
69
0
      value=file_getc(fh);
70
0
      if(value==-1) {
71
        /* EOF or error */
72
0
        *err=file_error(fh, err_info);
73
0
        if(*err==0)
74
0
          *err=WTAP_ERR_SHORT_READ;
75
0
        return false;
76
0
      }
77
0
      value+=2;
78
0
    }
79
0
    buf[i]=value;
80
0
  }
81
82
0
  return true;
83
0
}
84
85
/* Magic text to check for eyesdn-ness of file */
86
static const unsigned char eyesdn_hdr_magic[]  =
87
{ 'E', 'y', 'e', 'S', 'D', 'N'};
88
0
#define EYESDN_HDR_MAGIC_SIZE  sizeof(eyesdn_hdr_magic)
89
90
/* Size of a record header */
91
0
#define EYESDN_HDR_LENGTH   12
92
93
static bool eyesdn_read(wtap *wth, wtap_rec *rec,
94
  int *err, char **err_info, int64_t *data_offset);
95
static bool eyesdn_seek_read(wtap *wth, int64_t seek_off,
96
  wtap_rec *rec, int *err, char **err_info);
97
static bool read_eyesdn_rec(FILE_T fh, wtap_rec *rec,
98
  int *err, char **err_info);
99
100
/* Seeks to the beginning of the next packet, and returns the
101
   byte offset.  Returns -1 on failure, and sets "*err" to the error
102
   and "*err_info" to null or an additional error string. */
103
static int64_t eyesdn_seek_next_packet(wtap *wth, int *err, char **err_info)
104
0
{
105
0
  int byte;
106
0
  int64_t cur_off;
107
108
0
  while ((byte = file_getc(wth->fh)) != EOF) {
109
0
    if (byte == 0xff) {
110
0
      cur_off = file_tell(wth->fh);
111
0
      if (cur_off == -1) {
112
        /* Error. */
113
0
        *err = file_error(wth->fh, err_info);
114
0
        return -1;
115
0
      }
116
0
      return cur_off;
117
0
    }
118
0
  }
119
  /* EOF or error. */
120
0
  *err = file_error(wth->fh, err_info);
121
0
  return -1;
122
0
}
123
124
wtap_open_return_val eyesdn_open(wtap *wth, int *err, char **err_info)
125
0
{
126
0
  char  magic[EYESDN_HDR_MAGIC_SIZE];
127
128
  /* Look for eyesdn header */
129
0
  if (!wtap_read_bytes(wth->fh, &magic, sizeof magic, err, err_info)) {
130
0
    if (*err != WTAP_ERR_SHORT_READ)
131
0
      return WTAP_OPEN_ERROR;
132
0
    return WTAP_OPEN_NOT_MINE;
133
0
  }
134
0
  if (memcmp(magic, eyesdn_hdr_magic, EYESDN_HDR_MAGIC_SIZE) != 0)
135
0
    return WTAP_OPEN_NOT_MINE;
136
137
0
  wth->file_encap = WTAP_ENCAP_PER_PACKET;
138
0
  wth->file_type_subtype = eyesdn_file_type_subtype;
139
0
  wth->snapshot_length = 0; /* not known */
140
0
  wth->subtype_read = eyesdn_read;
141
0
  wth->subtype_seek_read = eyesdn_seek_read;
142
0
  wth->file_tsprec = WTAP_TSPREC_USEC;
143
144
0
  return WTAP_OPEN_MINE;
145
0
}
146
147
/* Find the next record and parse it; called from wtap_read(). */
148
static bool eyesdn_read(wtap *wth, wtap_rec *rec,
149
    int *err, char **err_info, int64_t *data_offset)
150
0
{
151
0
  int64_t offset;
152
153
  /* Find the next record */
154
0
  offset = eyesdn_seek_next_packet(wth, err, err_info);
155
0
  if (offset < 1)
156
0
    return false;
157
0
  *data_offset = offset;
158
159
  /* Parse the record */
160
0
  return read_eyesdn_rec(wth->fh, rec, err, err_info);
161
0
}
162
163
/* Used to read packets in random-access fashion */
164
static bool
165
eyesdn_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec,
166
  int *err, char **err_info)
167
0
{
168
0
  if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
169
0
    return false;
170
171
0
  return read_eyesdn_rec(wth->random_fh, rec, err, err_info);
172
0
}
173
174
/* Parses a record. */
175
static bool
176
read_eyesdn_rec(FILE_T fh, wtap_rec *rec, int *err, char **err_info)
177
0
{
178
0
  union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
179
0
  uint8_t   hdr[EYESDN_HDR_LENGTH];
180
0
  time_t    secs;
181
0
  int   usecs;
182
0
  unsigned  pkt_len;
183
0
  uint8_t   channel, direction;
184
0
  uint8_t   *pd;
185
186
  /* Our file pointer should be at the summary information header
187
   * for a packet. Read in that header and extract the useful
188
   * information.
189
   */
190
0
  if (!esc_read(fh, hdr, EYESDN_HDR_LENGTH, err, err_info))
191
0
    return false;
192
193
0
  wtap_setup_packet_rec(rec, WTAP_ENCAP_UNKNOWN);
194
0
  rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
195
196
  /* extract information from header */
197
0
  usecs = pntohu24(&hdr[0]);
198
0
  secs = pntohu40(&hdr[3]);
199
200
0
  channel = hdr[8];
201
0
  direction = hdr[9];
202
0
  pkt_len = pntohu16(&hdr[10]);
203
204
0
  switch(direction >> 1) {
205
206
0
  default:
207
0
  case EYESDN_ENCAP_ISDN: /* ISDN */
208
0
    pseudo_header->isdn.uton = direction & 1;
209
0
    pseudo_header->isdn.channel = channel;
210
0
    if(channel) { /* bearer channels */
211
0
      rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ISDN; /* recognises PPP */
212
0
      pseudo_header->isdn.uton=!pseudo_header->isdn.uton; /* bug */
213
0
    } else { /* D channel */
214
0
      rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ISDN;
215
0
    }
216
0
    break;
217
218
0
  case EYESDN_ENCAP_MSG: /* Layer 1 message */
219
0
    rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_LAYER1_EVENT;
220
0
    pseudo_header->l1event.uton = (direction & 1);
221
0
    break;
222
223
0
  case EYESDN_ENCAP_LAPB: /* X.25 via LAPB */
224
0
    rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_LAPB;
225
0
    pseudo_header->dte_dce.flags = (direction & 1) ? 0 : 0x80;
226
0
    break;
227
228
0
  case EYESDN_ENCAP_ATM: { /* ATM cells */
229
0
#define CELL_LEN 53
230
0
    unsigned char cell[CELL_LEN];
231
0
    int64_t cur_off;
232
233
0
    if(pkt_len != CELL_LEN) {
234
0
      *err = WTAP_ERR_BAD_FILE;
235
0
      *err_info = ws_strdup_printf(
236
0
          "eyesdn: ATM cell has a length != 53 (%u)",
237
0
          pkt_len);
238
0
      return false;
239
0
    }
240
241
0
    cur_off = file_tell(fh);
242
0
    if (!esc_read(fh, cell, CELL_LEN, err, err_info))
243
0
      return false;
244
0
    if (file_seek(fh, cur_off, SEEK_SET, err) == -1)
245
0
      return false;
246
0
    rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
247
0
    pseudo_header->atm.flags=ATM_RAW_CELL;
248
0
    pseudo_header->atm.aal=AAL_UNKNOWN;
249
0
    pseudo_header->atm.type=TRAF_UMTS_FP;
250
0
    pseudo_header->atm.subtype=TRAF_ST_UNKNOWN;
251
0
    pseudo_header->atm.vpi=((cell[0]&0xf)<<4) + (cell[0]&0xf);
252
0
    pseudo_header->atm.vci=((cell[0]&0xf)<<4) + cell[0]; /* from cell */
253
0
    pseudo_header->atm.channel=direction & 1;
254
0
    }
255
0
    break;
256
257
0
  case EYESDN_ENCAP_MTP2: /* SS7 frames */
258
0
    pseudo_header->mtp2.sent = direction & 1;
259
0
    pseudo_header->mtp2.annex_a_used = MTP2_ANNEX_A_USED_UNKNOWN;
260
0
    pseudo_header->mtp2.link_number = channel;
261
0
    rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_MTP2_WITH_PHDR;
262
0
    break;
263
264
0
  case EYESDN_ENCAP_DPNSS: /* DPNSS */
265
0
    pseudo_header->isdn.uton = direction & 1;
266
0
    pseudo_header->isdn.channel = channel;
267
0
    rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_DPNSS;
268
0
    break;
269
270
0
  case EYESDN_ENCAP_DASS2: /* DASS2 frames */
271
0
    pseudo_header->isdn.uton = direction & 1;
272
0
    pseudo_header->isdn.channel = channel;
273
0
    rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_DPNSS;
274
0
    break;
275
276
0
  case EYESDN_ENCAP_BACNET: /* BACNET async over HDLC frames */
277
0
          pseudo_header->isdn.uton = direction & 1;
278
0
    pseudo_header->isdn.channel = channel;
279
0
    rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_BACNET_MS_TP_WITH_PHDR;
280
0
    break;
281
282
0
  case EYESDN_ENCAP_V5_EF: /* V5EF */
283
0
    pseudo_header->isdn.uton = direction & 1;
284
0
    pseudo_header->isdn.channel = channel;
285
0
    rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_V5_EF;
286
0
    break;
287
0
  }
288
289
0
  if(pkt_len > WTAP_MAX_PACKET_SIZE_STANDARD) {
290
0
    *err = WTAP_ERR_BAD_FILE;
291
0
    *err_info = ws_strdup_printf("eyesdn: File has %u-byte packet, bigger than maximum of %u",
292
0
        pkt_len, WTAP_MAX_PACKET_SIZE_STANDARD);
293
0
    return false;
294
0
  }
295
296
0
  rec->presence_flags = WTAP_HAS_TS;
297
0
  rec->ts.secs = secs;
298
0
  rec->ts.nsecs = usecs * 1000;
299
0
  rec->rec_header.packet_header.caplen = pkt_len;
300
0
  rec->rec_header.packet_header.len = pkt_len;
301
302
  /* Make sure we have enough room for the packet */
303
0
  ws_buffer_assure_space(&rec->data, pkt_len);
304
305
0
  pd = ws_buffer_start_ptr(&rec->data);
306
0
  if (!esc_read(fh, pd, pkt_len, err, err_info))
307
0
    return false;
308
0
  return true;
309
0
}
310
311
312
static bool
313
esc_write(wtap_dumper *wdh, const uint8_t *buf, int len, int *err)
314
0
{
315
0
  int i;
316
0
  uint8_t byte;
317
0
  static const uint8_t esc = 0xfe;
318
319
0
  for(i=0; i<len; i++) {
320
0
    byte=buf[i];
321
0
    if(byte == 0xff || byte == 0xfe) {
322
      /*
323
       * Escape the frame delimiter and escape byte.
324
       */
325
0
      if (!wtap_dump_file_write(wdh, &esc, sizeof esc, err))
326
0
        return false;
327
0
      byte-=2;
328
0
    }
329
0
    if (!wtap_dump_file_write(wdh, &byte, sizeof byte, err))
330
0
      return false;
331
0
  }
332
0
  return true;
333
0
}
334
335
static bool eyesdn_dump(wtap_dumper *wdh, const wtap_rec *rec,
336
      int *err, char **err_info);
337
338
static bool eyesdn_dump_open(wtap_dumper *wdh, int *err, char **err_info _U_)
339
0
{
340
0
  wdh->subtype_write=eyesdn_dump;
341
342
0
  if (!wtap_dump_file_write(wdh, eyesdn_hdr_magic,
343
0
      EYESDN_HDR_MAGIC_SIZE, err))
344
0
    return false;
345
0
  *err=0;
346
0
  return true;
347
0
}
348
349
static int eyesdn_dump_can_write_encap(int encap)
350
0
{
351
0
  switch (encap) {
352
0
  case WTAP_ENCAP_ISDN:
353
0
  case WTAP_ENCAP_LAYER1_EVENT:
354
0
  case WTAP_ENCAP_DPNSS:
355
0
  case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
356
0
  case WTAP_ENCAP_LAPB:
357
0
  case WTAP_ENCAP_MTP2_WITH_PHDR:
358
0
  case WTAP_ENCAP_BACNET_MS_TP_WITH_PHDR:
359
0
  case WTAP_ENCAP_PER_PACKET:
360
0
    return 0;
361
362
0
  default:
363
0
    return WTAP_ERR_UNWRITABLE_ENCAP;
364
0
  }
365
0
}
366
367
/* Write a record for a packet to a dump file.
368
 *    Returns true on success, false on failure. */
369
static bool eyesdn_dump(wtap_dumper *wdh, const wtap_rec *rec,
370
      int *err, char **err_info _U_)
371
0
{
372
0
  static const uint8_t start_flag = 0xff;
373
0
  const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
374
0
  uint8_t buf[EYESDN_HDR_LENGTH];
375
0
  int usecs;
376
0
  time_t secs;
377
0
  int channel;
378
0
  int origin;
379
0
  int protocol;
380
0
  int size;
381
382
  /* We can only write packet records. */
383
0
  if (rec->rec_type != REC_TYPE_PACKET) {
384
0
    *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
385
0
    *err_info = wtap_unwritable_rec_type_err_string(rec);
386
0
    return false;
387
0
  }
388
389
  /* Don't write out anything bigger than we can read.
390
   * (The length field in packet headers is 16 bits, which
391
   * imposes a hard limit.) */
392
0
  if (rec->rec_header.packet_header.caplen > 65535) {
393
0
    *err = WTAP_ERR_PACKET_TOO_LARGE;
394
0
    return false;
395
0
  }
396
397
0
  usecs=rec->ts.nsecs/1000;
398
0
  secs=rec->ts.secs;
399
0
  size=rec->rec_header.packet_header.caplen;
400
0
  origin = pseudo_header->isdn.uton;
401
0
  channel = pseudo_header->isdn.channel;
402
403
0
  switch(rec->rec_header.packet_header.pkt_encap) {
404
405
0
  case WTAP_ENCAP_ISDN:
406
0
    protocol=EYESDN_ENCAP_ISDN; /* set depending on decoder format and mode */
407
0
    break;
408
409
0
  case WTAP_ENCAP_LAYER1_EVENT:
410
0
    protocol=EYESDN_ENCAP_MSG;
411
0
    break;
412
413
0
  case WTAP_ENCAP_DPNSS:
414
0
    protocol=EYESDN_ENCAP_DPNSS;
415
0
    break;
416
417
#if 0
418
  case WTAP_ENCAP_DASS2:
419
    protocol=EYESDN_ENCAP_DASS2;
420
    break;
421
#endif
422
423
0
  case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
424
0
    protocol=EYESDN_ENCAP_ATM;
425
0
    channel=0x80;
426
0
    break;
427
428
0
  case WTAP_ENCAP_LAPB:
429
0
    protocol=EYESDN_ENCAP_LAPB;
430
0
    break;
431
432
0
  case WTAP_ENCAP_MTP2_WITH_PHDR:
433
0
    protocol=EYESDN_ENCAP_MTP2;
434
0
    break;
435
436
0
  case WTAP_ENCAP_BACNET_MS_TP_WITH_PHDR:
437
0
    protocol=EYESDN_ENCAP_BACNET;
438
0
    break;
439
440
0
  case WTAP_ENCAP_V5_EF:
441
0
    protocol=EYESDN_ENCAP_V5_EF;
442
0
    break;
443
444
0
  default:
445
0
    *err=WTAP_ERR_UNWRITABLE_ENCAP;
446
0
    return false;
447
0
  }
448
449
0
  phtonu24(&buf[0], usecs);       /* 0-2 */
450
0
  phtonu40(&buf[3], secs);          /* 3-7 */
451
0
  phtonu8(&buf[8], channel);        /* 8 */
452
0
  phtonu8(&buf[9], (origin?1:0) + (protocol << 1)); /* 9 */
453
0
  phtonu16(&buf[10], size);       /* 10-11 */
454
455
  /* start flag */
456
0
  if (!wtap_dump_file_write(wdh, &start_flag, sizeof start_flag, err))
457
0
    return false;
458
0
  if (!esc_write(wdh, buf, 12, err))
459
0
    return false;
460
0
  if (!esc_write(wdh, ws_buffer_start_ptr(&rec->data), size, err))
461
0
    return false;
462
0
  return true;
463
0
}
464
465
static const struct supported_block_type eyesdn_blocks_supported[] = {
466
  /*
467
   * We support packet blocks, with no comments or other options.
468
   */
469
  { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
470
};
471
472
static const struct file_type_subtype_info eyesdn_info = {
473
  "EyeSDN USB S0/E1 ISDN trace format", "eyesdn", "trc", NULL,
474
  false, BLOCKS_SUPPORTED(eyesdn_blocks_supported),
475
  eyesdn_dump_can_write_encap, eyesdn_dump_open, NULL
476
};
477
478
void register_eyesdn(void)
479
14
{
480
14
  eyesdn_file_type_subtype = wtap_register_file_type_subtype(&eyesdn_info);
481
482
  /*
483
   * Register name for backwards compatibility with the
484
   * wtap_filetypes table in Lua.
485
   */
486
14
  wtap_register_backwards_compatibility_lua_name("EYESDN",
487
14
      eyesdn_file_type_subtype);
488
14
}
489
490
/*
491
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
492
 *
493
 * Local variables:
494
 * c-basic-offset: 8
495
 * tab-width: 8
496
 * indent-tabs-mode: t
497
 * End:
498
 *
499
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
500
 * :indentSize=8:tabSize=8:noTabs=false:
501
 */