Coverage Report

Created: 2026-03-30 07:00

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