Coverage Report

Created: 2025-04-03 08:43

/src/wireshark/wiretap/iseries.c
Line
Count
Source (jump to first uncovered line)
1
/* iseries.c
2
 *
3
 * Wiretap Library
4
 * Copyright (c) 2011 by Martin Warnes <Martin_Warnes@uk.ibm.com>
5
 *
6
 * Based on toshiba.c and vms.c
7
 *
8
 * SPDX-License-Identifier: GPL-2.0-or-later
9
 */
10
11
/*
12
 * This module will read the contents of the iSeries (OS/400) Communication trace
13
 * Both ASCII & Unicode (little-endian UCS-2) formatted traces are supported.
14
 *
15
 * iSeries Comms traces consist of a header page and a subsequent number of packet records
16
 *
17
 * The header page contains details on the options set during running of the trace,
18
 * currently the following options are a requirement for this module:
19
 *
20
 * 1. Object protocol = ETHERNET (Default)
21
 * 2. ASCII or Unicode file formats.
22
 *
23
 * The above can be achieved by passing option ASCII(*YES) with the trace command
24
 *
25
 */
26
27
/* iSeries header page
28
29
 COMMUNICATIONS TRACE       Title: OS400 - OS400 trace               10/28/05  11:44:50                           Page:       1
30
   Trace Description  . . . . . :   OS400 - OS400 trace
31
   Configuration object . . . . :   ETH0
32
   Type . . . . . . . . . . . . :   1            1=Line, 2=Network Interface
33
                                                 3=Network server
34
   Object protocol  . . . . . . :   ETHERNET
35
   Start date/Time  . . . . . . :   10/28/05  11:43:00.341
36
   End date/Time  . . . . . . . :   10/28/05  11:44:22.148
37
   Bytes collected  . . . . . . :   11999
38
   Buffer size  . . . . . . . . :   2048         kilobytes
39
   Data direction . . . . . . . :   3            1=Sent, 2=Received, 3=Both
40
   Stop on buffer full  . . . . :   Y            Y=Yes, N=No
41
   Number of bytes to trace
42
     Beginning bytes  . . . . . :   *MAX         Value, *CALC, *MAX
43
     Ending bytes   . . . . . . :   *CALC        Value, *CALC
44
   Controller name  . . . . . . :   *ALL         *ALL, name
45
   Data representation  . . . . :   1            1=ASCII, 2=EBCDIC, 3=*CALC
46
   Format SNA data only . . . . :   N            Y=Yes, N=No
47
   Format RR, RNR commands  . . :   N            Y=Yes, N=No
48
   Format TCP/IP data only  . . :   Y            Y=Yes, N=No
49
     IP address . . . . . . . . :   *ALL             *ALL, address
50
     IP address . . . . . . . . :   *ALL             *ALL, address
51
     IP port  . . . . . . . . . :   *ALL             *ALL, IP port
52
   Format UI data only  . . . . :   N            Y=Yes, N=No
53
   Select Ethernet data . . . . :   3            1=802.3, 2=ETHV2, 3=Both
54
   Format Broadcast data  . . . :   Y            Y=Yes, N=No
55
*/
56
57
/* iSeries IPv4 formatted packet records consist of a packet header line
58
 * identifying the packet number, direction, size, timestamp,
59
 * source/destination MAC addresses and packet type.
60
 *
61
 * Thereafter there will be a formatted display of the headers above
62
 * the link layer, such as ARP, IP, TCP, UDP, and ICMP (all but
63
 * ICMP have either been seen in captures or on pages such as the ones
64
 * at
65
 *
66
 *    http://www-912.ibm.com/s_dir/SLKBase.nsf/1ac66549a21402188625680b0002037e/e05fb0515bc3449686256ce600512c37?OpenDocument
67
 *
68
 * and
69
 *
70
 *    http://publib.boulder.ibm.com/infocenter/javasdk/v5r0/index.jsp?topic=%2Fcom.ibm.java.doc.diagnostics.50%2Fdiag%2Fproblem_determination%2Fi5os_perf_io_commstrace.html
71
 *
72
 * so we cannot assume that "IP Header" or "TCP Header" will appear). The
73
 * formatted display includes lines that show the contents of some of the
74
 * fields in the header, as well as hex strings dumps of the headers
75
 * themselves, with tags such as "IP Header  :", "ARP Header :",
76
 * "TCP Header :", "UDP Header :", and (presumably) "ICMP Header:".
77
 *
78
 * If the packet contains data this is displayed as 4 groups of 16 hex digits
79
 * followed by an ASCII representation of the data line.
80
 *
81
 * Information from the packet header line, higher-level headers and, if
82
 * available, data lines are extracted by the module for displaying.
83
 *
84
 *
85
 Record       Data    Record           Controller  Destination   Source        Frame
86
 Number  S/R  Length  Timer            Name        MAC Address   MAC Address   Format
87
 ------  ---  ------  ---------------  ----------  ------------  ------------  ------
88
      8   S      145  11:43:59.82956               0006299C14AE  0006299C14FE   ETHV2   Type: 0800
89
                      Frame Type :  IP          DSCP: 0   ECN: 00-NECT  Length:   145   Protocol: TCP         Datagram ID: 388B
90
                                    Src Addr: 10.20.144.150       Dest Addr: 10.20.144.151       Fragment Flags: DON'T,LAST
91
                      IP Header  :  45000091388B40004006CC860A1490960A149097
92
                      IP Options :  NONE
93
                      TCP  . . . :  Src Port:  6006,Unassigned    Dest Port: 35366,Unassigned
94
                                    SEQ Number:  2666470699 ('9EEF1D2B'X)  ACK Number: 2142147535 ('7FAE93CF'X)
95
                                    Code Bits: ACK PSH                  Window: 32648  TCP Option: NO OP
96
                      TCP Header :  17768A269EEF1D2B7FAE93CF80187F885B5600000101080A0517E0F805166DE0
97
         Data . . . . . :  5443503200020010 0000004980000000  B800000080470103 01001E0000002000   *TCP2.......I*...*...*G........ .*
98
                           002F010080000004 0300800700C00600  4002008000000304 00800000060FB067   *./..*.....*..*..@..*.....*....*G*
99
                           FC276228786B3EB0 EF34F5F1D27EF8DF  20926820E7B322AA 739F1FB20D         **'B(XK>**4***.** *H **"*S*.*.   *
100
*/
101
102
/* iSeries IPv6 formatted traces are similar to the IPv4 version above,
103
 * except that the higher-level headers have "IPv6 Header:" and
104
 * "ICMPv6  Hdr:", and data is no longer output in groups of 16 hex
105
 * digits.
106
 *
107
108
Record       Data      Record                       Destination   Source        Frame
109
Number  S/R  Length    Timer                        MAC Address   MAC Address   Format
110
------  ---  ------    ------------                 ------------  ------------  ------
111
   218   S     1488    15:01:14.389                 0011BC358680  00096B6BD918   ETHV2  Type: 86DD
112
                      IPv6   Data:  Ver: 06                      Traffic Class: 00            Flow Label: 000000
113
                                    Payload Length:  1448        Next Header: 06,TCP          Hop Limit:    64
114
                                    Src Addr:   fd00:0:0:20f2::122
115
                                    Dest Addr:  fd00:0:0:20a0::155
116
                      IPv6 Header:  6000000005A80640FD000000000020F20000000000000122FD000000000020A0
117
                                    0000000000000155
118
                      TCP  . . . :  Src Port: 21246,Unassigned    Dest Port: 13601,Unassigned
119
                                    SEQ Number:  2282300877 ('880925CD'X)  ACK Number: 3259003715 ('C2407343'X)
120
                                    Code Bits: ACK                      Window: 65535  TCP Option: NO OP
121
                      TCP Header :  52FE3521880925CDC24073438010FFFFCFBB00000101080A0E15127000237A08
122
         Data . . . . . :  54435032000200140000061880000000ECBEB867F0000000004CE640E6C1D9D5       *TCP2........*...***g*....L*@*****
123
                           C9D5C740E3C8C9E240C9E240E3C8C540E6C1D9D5C9D5C740C6C9C5D3C4404040       ****@****@**@***@*******@*****@@@*
124
                           4040404040404040404040404040404040404040404040404040404040404040       *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
125
*/
126
127
/* iSeries unformatted packet record consist of the same header record as
128
 * the formatted trace but all other records are simply unformatted data
129
 * containing higher-level headers and packet data combined.
130
 *
131
 Record       Data    Record           Controller  Destination   Source        Frame            Number  Number    Poll/
132
 Number  S/R  Length  Timer            Name        MAC Address   MAC Address   Format  Command  Sent    Received  Final  DSAP  SSAP
133
 ------  ---  ------  ---------------  ----------  ------------  ------------  ------  -------  ------  --------  -----  ----  ----
134
      1   R       64  12:19:29.97108               000629ECF48E  0006D78E23C2   ETHV2   Type: 0800
135
         Data . . . . . :  4500003C27954000 3A06CE3D9797440F  0A5964EAC4F50554 58C9915500000000   *E..<'*@.:.*=**D..YD***.TX**U....*
136
                           A00216D06A200000 020405B40402080A  1104B6C000000000 010303000B443BF1   **..*J .....*......**.........D;**
137
*/
138
139
#include "config.h"
140
#include "iseries.h"
141
#include "wtap-int.h"
142
#include "file_wrappers.h"
143
144
#include <stdlib.h>
145
#include <string.h>
146
147
#include <wsutil/str_util.h>
148
#include <wsutil/strtoi.h>
149
#include <wsutil/ws_assert.h>
150
151
0
#define ISERIES_LINE_LENGTH           270
152
0
#define ISERIES_HDR_LINES_TO_CHECK    100
153
0
#define ISERIES_PKT_LINES_TO_CHECK    4
154
0
#define ISERIES_MAX_TRACE_LEN         99999999
155
0
#define ISERIES_FORMAT_ASCII          1
156
0
#define ISERIES_FORMAT_UNICODE        2
157
158
/*
159
 * Magic strings - "COMMUNICATIONS TRACE", in ASCII and little-endian UCS-2.
160
 */
161
static const char iseries_hdr_magic_ascii[] = {
162
  'C', 'O', 'M', 'M',
163
  'U', 'N', 'I', 'C',
164
  'A', 'T', 'I', 'O',
165
  'N', 'S', ' ', 'T',
166
  'R', 'A', 'C', 'E'
167
};
168
static const char iseries_hdr_magic_le_ucs_2[] = {
169
  'C', 0x0, 'O', 0x0, 'M', 0x0, 'M', 0x0,
170
  'U', 0x0, 'N', 0x0, 'I', 0x0, 'C', 0x0,
171
  'A', 0x0, 'T', 0x0, 'I', 0x0, 'O', 0x0,
172
  'N', 0x0, 'S', 0x0, ' ', 0x0, 'T', 0x0,
173
  'R', 0x0, 'A', 0x0, 'C', 0x0, 'E', 0x0
174
};
175
176
typedef struct {
177
  bool have_date;           /* true if we found a capture start date */
178
  int      year, month, day;    /* The start date */
179
  int      format;              /* Trace format type        */
180
} iseries_t;
181
182
static bool iseries_read (wtap * wth, wtap_rec *rec,
183
                              int *err, char ** err_info, int64_t *data_offset);
184
static bool iseries_seek_read (wtap * wth, int64_t seek_off,
185
                                   wtap_rec *rec,
186
                                   int *err, char ** err_info);
187
static bool iseries_check_file_type (wtap * wth, int *err, char **err_info,
188
                                         int format);
189
static int64_t iseries_seek_next_packet (wtap * wth, int *err, char **err_info);
190
static bool iseries_parse_packet (wtap * wth, FILE_T fh,
191
                                      wtap_rec *rec,
192
                                      int *err, char ** err_info);
193
static int iseries_UNICODE_to_ASCII (uint8_t * buf, unsigned bytes);
194
static bool iseries_parse_hex_string (const char * ascii, uint8_t * buf,
195
                                          size_t len);
196
197
static int iseries_file_type_subtype = -1;
198
static int iseries_unicode_file_type_subtype = -1;
199
200
void register_iseries(void);
201
202
/*
203
 * XXX - it would probably be cleaner to use a UCS-2 flavor of file_gets(),
204
 * rather than file_gets(), if we're reading a UCS-2 file.
205
 */
206
wtap_open_return_val
207
iseries_open (wtap * wth, int *err, char ** err_info)
208
0
{
209
0
  int offset;
210
0
  char magic[ISERIES_LINE_LENGTH];
211
212
  /*
213
   * Check that file starts with a valid iSeries COMMS TRACE header
214
   * by scanning for it in the first line
215
   */
216
0
  if (!wtap_read_bytes (wth->fh, &magic, sizeof magic, err, err_info))
217
0
    {
218
0
      if (*err != WTAP_ERR_SHORT_READ)
219
0
        return WTAP_OPEN_ERROR;
220
0
      return WTAP_OPEN_NOT_MINE;
221
0
    }
222
223
  /*
224
   * Check if this is a little-endian UCS-2 Unicode formatted file by scanning
225
   * for the magic string
226
   */
227
0
  offset=0;
228
0
  while ((unsigned int)offset < (ISERIES_LINE_LENGTH - (sizeof iseries_hdr_magic_le_ucs_2)))
229
0
    {
230
0
      if (memcmp (magic + offset, iseries_hdr_magic_le_ucs_2, sizeof iseries_hdr_magic_le_ucs_2) == 0) {
231
0
        if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
232
0
          {
233
0
            return WTAP_OPEN_ERROR;
234
0
          }
235
        /*
236
         * Do some basic sanity checking to ensure we can handle the
237
         * contents of this trace
238
         */
239
0
        if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_UNICODE))
240
0
          {
241
0
            if (*err == 0)
242
0
              return WTAP_OPEN_NOT_MINE;
243
0
            else
244
0
              return WTAP_OPEN_ERROR;
245
0
          }
246
247
0
        wth->file_encap        = WTAP_ENCAP_ETHERNET;
248
0
        wth->file_type_subtype = iseries_unicode_file_type_subtype;
249
0
        wth->snapshot_length   = 0;
250
0
        wth->subtype_read      = iseries_read;
251
0
        wth->subtype_seek_read = iseries_seek_read;
252
0
        wth->file_tsprec       = WTAP_TSPREC_USEC;
253
254
0
        if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
255
0
          {
256
0
            return WTAP_OPEN_ERROR;
257
0
          }
258
259
        /*
260
         * Add an IDB; we don't know how many interfaces were
261
         * involved, so we just say one interface, about which
262
         * we only know the link-layer type, snapshot length,
263
         * and time stamp resolution.
264
         */
265
0
        wtap_add_generated_idb(wth);
266
267
0
        return WTAP_OPEN_MINE;
268
0
      }
269
0
      offset += 1;
270
0
    }
271
272
    /*
273
     * Check if this is a ASCII formatted file by scanning for the magic string
274
     */
275
0
    offset=0;
276
0
    while ((unsigned int)offset < (ISERIES_LINE_LENGTH - sizeof iseries_hdr_magic_ascii))
277
0
      {
278
0
        if (memcmp (magic + offset, iseries_hdr_magic_ascii, sizeof iseries_hdr_magic_ascii) == 0)
279
0
          {
280
0
            if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
281
0
              {
282
0
                return WTAP_OPEN_ERROR;
283
0
              }
284
            /*
285
             * Do some basic sanity checking to ensure we can handle the
286
             * contents of this trace
287
             */
288
0
            if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_ASCII))
289
0
              {
290
0
                if (*err == 0)
291
0
                  return WTAP_OPEN_NOT_MINE;
292
0
                else
293
0
                  return WTAP_OPEN_ERROR;
294
0
              }
295
296
0
            wth->file_encap        = WTAP_ENCAP_ETHERNET;
297
0
            wth->file_type_subtype = iseries_file_type_subtype;
298
0
            wth->snapshot_length   = 0;
299
0
            wth->subtype_read      = iseries_read;
300
0
            wth->subtype_seek_read = iseries_seek_read;
301
0
            wth->file_tsprec       = WTAP_TSPREC_USEC;
302
303
0
            if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
304
0
              {
305
0
                return WTAP_OPEN_ERROR;
306
0
              }
307
308
            /*
309
             * Add an IDB; we don't know how many interfaces were
310
             * involved, so we just say one interface, about which
311
             * we only know the link-layer type, snapshot length,
312
             * and time stamp resolution.
313
             */
314
0
            wtap_add_generated_idb(wth);
315
316
0
            return WTAP_OPEN_MINE;
317
0
          }
318
0
        offset += 1;
319
0
      }
320
321
    /* Neither ASCII or UNICODE so not supported */
322
0
    return WTAP_OPEN_NOT_MINE;
323
0
    }
324
325
/*
326
 * Do some basic sanity checking to ensure we can handle the
327
 * contents of this trace by checking the header page for
328
 * requisite requirements and additional information.
329
 */
330
static bool
331
iseries_check_file_type (wtap * wth, int *err, char **err_info, int format)
332
0
{
333
0
  bool       is_iseries = false;
334
0
  unsigned   line;
335
0
  int        num_items_scanned;
336
0
  char       buf[ISERIES_LINE_LENGTH], protocol[9];
337
0
  iseries_t *iseries;
338
339
  /* Save trace format for passing between packets */
340
0
  iseries                = g_new(iseries_t, 1);
341
0
  iseries->have_date     = false;
342
0
  iseries->format        = format;
343
344
0
  for (line = 0; line < ISERIES_HDR_LINES_TO_CHECK; line++)
345
0
    {
346
0
      memset(buf, 0x0, sizeof(buf));
347
0
      if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
348
0
        {
349
          /* EOF or error. */
350
0
          *err = file_error (wth->fh, err_info);
351
0
          if (*err == WTAP_ERR_SHORT_READ)
352
0
            *err = 0;
353
0
          break;
354
0
        }
355
356
      /*
357
       * Check that we are dealing with an ETHERNET trace
358
       */
359
0
      if (iseries->format == ISERIES_FORMAT_UNICODE)
360
0
        {
361
0
          iseries_UNICODE_to_ASCII ((uint8_t *)buf, ISERIES_LINE_LENGTH);
362
0
        }
363
0
      ascii_strup_inplace (buf);
364
0
      num_items_scanned = sscanf (buf,
365
0
                                 "%*[ \n\t]OBJECT PROTOCOL%*[ .:\n\t]%8s",
366
0
                                 protocol);
367
0
      if (num_items_scanned == 1)
368
0
        {
369
0
          if (memcmp (protocol, "ETHERNET", 8) == 0)
370
0
            {
371
0
              *err = 0;
372
0
              is_iseries = true;
373
0
            }
374
0
        }
375
376
      /*
377
       * The header is the only place where the date part of the timestamp is held, so
378
       * extract it here and store for all packets to access
379
       */
380
0
      num_items_scanned = sscanf (buf,
381
0
                                  "%*[ \n\t]START DATE/TIME%*[ .:\n\t]%2d/%2d/%2d",
382
0
                                  &iseries->month, &iseries->day,
383
0
                                  &iseries->year);
384
0
      if (num_items_scanned == 3)
385
0
        {
386
0
          iseries->have_date = true;
387
0
        }
388
0
    }
389
390
0
  if (is_iseries)
391
0
    wth->priv = (void *) iseries;
392
0
  else
393
0
    g_free(iseries);
394
395
0
  return is_iseries;
396
0
}
397
398
/*
399
 * Find the next packet and parse it; called from wtap_read().
400
 */
401
static bool
402
iseries_read (wtap * wth, wtap_rec *rec, int *err, char ** err_info,
403
              int64_t *data_offset)
404
0
{
405
0
  int64_t offset;
406
407
  /*
408
   * Locate the next packet
409
   */
410
0
  offset = iseries_seek_next_packet (wth, err, err_info);
411
0
  if (offset < 0)
412
0
    return false;
413
0
  *data_offset     = offset;
414
415
  /*
416
   * Parse the packet and extract the various fields
417
   */
418
0
  return iseries_parse_packet (wth, wth->fh, rec, err, err_info);
419
0
}
420
421
/*
422
 * Seeks to the beginning of the next packet, and returns the
423
 * byte offset.  Returns -1 on failure or EOF; on EOF, sets
424
 * *err to 0, and, on failure, sets *err to the error and *err_info
425
 * to null or an additional error string.
426
 */
427
static int64_t
428
iseries_seek_next_packet (wtap * wth, int *err, char **err_info)
429
0
{
430
0
  iseries_t *iseries = (iseries_t *)wth->priv;
431
0
  char       buf[ISERIES_LINE_LENGTH],type[5];
432
0
  int        line, num_items_scanned;
433
0
  int64_t    cur_off;
434
0
  long       buflen;
435
436
0
  for (line = 0; line < ISERIES_MAX_TRACE_LEN; line++)
437
0
    {
438
0
      if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
439
0
        {
440
          /* EOF or error. */
441
0
          *err = file_error (wth->fh, err_info);
442
0
          return -1;
443
0
        }
444
      /* Convert UNICODE to ASCII if required and determine    */
445
      /* the number of bytes to rewind to beginning of record. */
446
0
      if (iseries->format == ISERIES_FORMAT_UNICODE)
447
0
        {
448
          /* buflen is #bytes to 1st 0x0A */
449
0
          buflen = iseries_UNICODE_to_ASCII ((uint8_t *) buf, ISERIES_LINE_LENGTH);
450
0
        }
451
0
      else
452
0
        {
453
          /* Else buflen is just length of the ASCII string */
454
0
          buflen = (long) strlen (buf);
455
0
        }
456
0
      ascii_strup_inplace (buf);
457
      /* Check we have enough data in the line */
458
0
      if (buflen < 78)
459
0
        {
460
0
          continue;
461
0
        }
462
      /* If packet header found return the offset */
463
0
      num_items_scanned =
464
0
        sscanf (buf+78,
465
0
                "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
466
0
      if (num_items_scanned == 1)
467
0
        {
468
          /* Rewind to beginning of line */
469
0
          cur_off = file_tell (wth->fh);
470
0
          if (cur_off == -1)
471
0
            {
472
0
              *err = file_error (wth->fh, err_info);
473
0
              return -1;
474
0
            }
475
0
          if (file_seek (wth->fh, cur_off - buflen, SEEK_SET, err) == -1)
476
0
            {
477
0
              return -1;
478
0
            }
479
0
          return cur_off - buflen;
480
0
        }
481
0
    }
482
483
0
  *err = WTAP_ERR_BAD_FILE;
484
0
  *err_info =
485
0
    ws_strdup_printf ("iseries: next packet header not found within %d lines",
486
0
             ISERIES_MAX_TRACE_LEN);
487
0
  return -1;
488
0
}
489
490
/*
491
 * Read packets in random-access fashion
492
 */
493
static bool
494
iseries_seek_read (wtap * wth, int64_t seek_off, wtap_rec *rec,
495
                   int *err, char ** err_info)
496
0
{
497
498
  /* seek to packet location */
499
0
  if (file_seek (wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
500
0
    return false;
501
502
  /*
503
   * Parse the packet and extract the various fields
504
   */
505
0
  return iseries_parse_packet (wth, wth->random_fh, rec, err, err_info);
506
0
}
507
508
static int
509
append_hex_digits(char *ascii_buf, int ascii_offset, int max_offset,
510
                  char *data, int *err, char **err_info)
511
0
{
512
0
  int in_offset, out_offset;
513
0
  int c;
514
0
  unsigned int i;
515
0
  bool overflow = false;
516
517
0
  in_offset = 0;
518
0
  out_offset = ascii_offset;
519
0
  for (;;)
520
0
    {
521
      /*
522
       * Process a block of up to 16 hex digits.
523
       * The block is terminated early by an end-of-line indication (NUL,
524
       * CR, or LF), by a space (which terminates the last block of the
525
       * data we're processing), or by a "*", which introduces the ASCII representation
526
       * of the data.
527
       * All characters in the block must be upper-case hex digits;
528
       * there might or might not be a space *after* a block, but, if so,
529
       * that will be skipped over after the block is processed.
530
       */
531
0
      for (i = 0; i < 16; i++, in_offset++)
532
0
        {
533
          /*
534
           * If we see an end-of-line indication, or an early-end-of-block
535
           * indication (space), we're done.  (Only the last block ends
536
           * early.)
537
           */
538
0
          c = data[in_offset] & 0xFF;
539
0
          if (c == '\0' || c == ' ' || c == '*' || c == '\r' || c == '\n')
540
0
            {
541
0
              goto done;
542
0
            }
543
0
          if (!g_ascii_isxdigit(c) || g_ascii_islower(c))
544
0
            {
545
              /*
546
               * Not a hex digit, or a lower-case hex digit.
547
               * Treat this as an indication that the line isn't a data
548
               * line, so we just ignore it.
549
               *
550
               * XXX - do so only for continuation lines; treat non-hex-digit
551
               * characters as errors for other lines?
552
               */
553
0
              return ascii_offset; /* pretend we appended nothing */
554
0
            }
555
0
          if (out_offset >= max_offset)
556
0
            overflow = true;
557
0
          else
558
0
            {
559
0
              ascii_buf[out_offset] = c;
560
0
              out_offset++;
561
0
            }
562
0
        }
563
      /*
564
       * Skip blanks, if any.
565
       */
566
0
      for (; (data[in_offset] & 0xFF) == ' '; in_offset++)
567
0
        ;
568
0
    }
569
0
done:
570
  /*
571
   * If we processed an *odd* number of hex digits, report an error.
572
   */
573
0
  if ((i % 2) != 0)
574
0
    {
575
0
      *err = WTAP_ERR_BAD_FILE;
576
0
      *err_info = g_strdup("iseries: odd number of hex digits in a line");
577
0
      return -1;
578
0
    }
579
0
  if (overflow)
580
0
    {
581
0
      *err = WTAP_ERR_BAD_FILE;
582
0
      *err_info = g_strdup("iseries: more packet data than the packet length indicated");
583
0
      return -1;
584
0
    }
585
0
  return out_offset;
586
0
}
587
588
/* return the multiplier for nanoseconds */
589
static uint32_t
590
csec_multiplier(uint32_t csec)
591
0
{
592
0
  if (csec < 10) return 100000000;
593
0
  if (csec < 100) return 10000000;
594
0
  if (csec < 1000) return 1000000;
595
0
  if (csec < 10000) return 100000;
596
0
  if (csec < 100000) return 10000;
597
0
  if (csec < 1000000) return 1000;
598
0
  if (csec < 10000000) return 100;
599
0
  if (csec < 100000000) return 10;
600
0
  return 1;
601
0
}
602
603
/* Parses a packet. */
604
static bool
605
iseries_parse_packet (wtap * wth, FILE_T fh, wtap_rec *rec,
606
                      int *err, char **err_info)
607
0
{
608
0
  iseries_t *iseries = (iseries_t *)wth->priv;
609
0
  int64_t    cur_off;
610
0
  bool       isValid, isCurrentPacket;
611
0
  int        num_items_scanned, line, pktline, buflen;
612
0
  int        pkt_len, pktnum, hr, min, sec;
613
0
  char       direction[2], destmac[13], srcmac[13], type[5];
614
0
  uint32_t   csec;
615
0
  char       data[ISERIES_LINE_LENGTH * 2];
616
0
  int        offset;
617
0
  char      *ascii_buf;
618
0
  int        ascii_offset;
619
0
  struct tm  tm;
620
621
  /*
622
   * Check for packet headers in first 3 lines this should handle page breaks
623
   * situations and the header lines output at each page throw and ensure we
624
   * read both the captured and packet lengths.
625
   */
626
0
  isValid = false;
627
0
  for (line = 1; line < ISERIES_PKT_LINES_TO_CHECK; line++)
628
0
    {
629
0
      if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
630
0
        {
631
0
          *err = file_error (fh, err_info);
632
0
          return false;
633
0
        }
634
      /* Convert UNICODE data to ASCII */
635
0
      if (iseries->format == ISERIES_FORMAT_UNICODE)
636
0
        {
637
0
         iseries_UNICODE_to_ASCII ((uint8_t *)data, ISERIES_LINE_LENGTH);
638
0
        }
639
0
      ascii_strup_inplace (data);
640
0
      num_items_scanned =
641
0
        sscanf (data,
642
0
                "%*[ \n\t]%6d%*[ *\n\t]%1s%*[ \n\t]%6d%*[ \n\t]%2d:%2d:%2d.%9u%*[ \n\t]"
643
0
                "%12s%*[ \n\t]%12s%*[ \n\t]ETHV2%*[ \n\t]TYPE:%*[ \n\t]%4s",
644
0
                &pktnum, direction, &pkt_len, &hr, &min, &sec, &csec, destmac,
645
0
                srcmac, type);
646
0
      if (num_items_scanned == 10)
647
0
        {
648
0
          if (pktnum < 0)
649
0
            {
650
0
              *err = WTAP_ERR_BAD_FILE;
651
0
              *err_info = g_strdup ("iseries: packet header has a negative packet number");
652
0
              return false;
653
0
            }
654
655
0
          if (pkt_len < 0)
656
0
            {
657
0
              *err = WTAP_ERR_BAD_FILE;
658
0
              *err_info = g_strdup ("iseries: packet header has a negative packet length");
659
0
              return false;
660
0
            }
661
662
0
          if (hr < 0)
663
0
            {
664
0
              *err = WTAP_ERR_BAD_FILE;
665
0
              *err_info = g_strdup ("iseries: packet header has a negative hour in the time stamp");
666
0
              return false;
667
0
            }
668
669
0
          if (hr > 23)
670
0
            {
671
0
              *err = WTAP_ERR_BAD_FILE;
672
0
              *err_info = g_strdup ("iseries: packet header has a hour in the time stamp greater than 23");
673
0
              return false;
674
0
            }
675
676
0
          if (min < 0)
677
0
            {
678
0
              *err = WTAP_ERR_BAD_FILE;
679
0
              *err_info = g_strdup ("iseries: packet header has a negative minute in the time stamp");
680
0
              return false;
681
0
            }
682
683
0
          if (min > 59)
684
0
            {
685
0
              *err = WTAP_ERR_BAD_FILE;
686
0
              *err_info = g_strdup ("iseries: packet header has a minute in the time stamp greater than 59");
687
0
              return false;
688
0
            }
689
690
0
          if (sec < 0)
691
0
            {
692
0
              *err = WTAP_ERR_BAD_FILE;
693
0
              *err_info = g_strdup ("iseries: packet header has a negative second in the time stamp");
694
0
              return false;
695
0
            }
696
697
          /*
698
           * Yes, 60, even though the time-conversion routines on most OSes
699
           * might not handle leap seconds.
700
           */
701
0
          if (sec > 60)
702
0
            {
703
0
              *err = WTAP_ERR_BAD_FILE;
704
0
              *err_info = g_strdup ("iseries: packet header has a second in the time stamp greater than 60");
705
0
              return false;
706
0
            }
707
708
0
          if (strlen(destmac) != 12)
709
0
            {
710
0
              *err = WTAP_ERR_BAD_FILE;
711
0
              *err_info = g_strdup ("iseries: packet header has a destination MAC address shorter than 6 bytes");
712
0
              return false;
713
0
            }
714
715
0
          if (strlen(srcmac) != 12)
716
0
            {
717
0
              *err = WTAP_ERR_BAD_FILE;
718
0
              *err_info = g_strdup ("iseries: packet header has a source MAC address shorter than 6 bytes");
719
0
              return false;
720
0
            }
721
722
0
          if (strlen(type) != 4)
723
0
            {
724
0
              *err = WTAP_ERR_BAD_FILE;
725
0
              *err_info = g_strdup ("iseries: packet header has an Ethernet type/length field than 2 bytes");
726
0
              return false;
727
0
            }
728
729
          /* OK! We found the packet header line */
730
0
          isValid = true;
731
          /*
732
           * XXX - The Capture length returned by the iSeries trace doesn't
733
           * seem to include the Ethernet header, so we add its length here.
734
           *
735
           * Check the length first, just in case it's *so* big that, after
736
           * adding the Ethernet header length, it overflows.
737
           */
738
0
          if ((unsigned)pkt_len > WTAP_MAX_PACKET_SIZE_STANDARD - 14)
739
0
            {
740
              /*
741
               * Probably a corrupt capture file; don't blow up trying
742
               * to allocate space for an immensely-large packet, and
743
               * don't think it's a really *small* packet because it
744
               * overflowed.  (Calculate the size as a 64-bit value in
745
               * the error message, to avoid an overflow.)
746
               */
747
0
              *err = WTAP_ERR_BAD_FILE;
748
0
              *err_info = ws_strdup_printf("iseries: File has %" PRIu64 "-byte packet, bigger than maximum of %u",
749
0
                                          (uint64_t)pkt_len + 14,
750
0
                                          WTAP_MAX_PACKET_SIZE_STANDARD);
751
0
              return false;
752
0
            }
753
0
          pkt_len += 14;
754
0
          break;
755
0
        }
756
0
    }
757
758
  /*
759
   * If no packet header found we exit at this point and inform the user.
760
   */
761
0
  if (!isValid)
762
0
    {
763
0
      *err = WTAP_ERR_BAD_FILE;
764
0
      *err_info = g_strdup ("iseries: packet header isn't valid");
765
0
      return false;
766
0
    }
767
768
0
  rec->rec_type = REC_TYPE_PACKET;
769
0
  rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
770
0
  rec->presence_flags = WTAP_HAS_CAP_LEN;
771
772
  /*
773
   * If we have Wiretap Header then populate it here
774
   *
775
   * Timer resolution on the iSeries is hardware dependent.  We determine
776
   * the resolution based on how many digits we see.
777
   */
778
0
  if (iseries->have_date)
779
0
    {
780
0
      rec->presence_flags |= WTAP_HAS_TS;
781
0
      tm.tm_year        = 100 + iseries->year;
782
0
      tm.tm_mon         = iseries->month - 1;
783
0
      tm.tm_mday        = iseries->day;
784
0
      tm.tm_hour        = hr;
785
0
      tm.tm_min         = min;
786
0
      tm.tm_sec         = sec;
787
0
      tm.tm_isdst       = -1;
788
0
      rec->ts.secs = mktime (&tm);
789
0
      rec->ts.nsecs = csec * csec_multiplier(csec);
790
0
    }
791
792
0
  rec->rec_header.packet_header.len                       = pkt_len;
793
0
  rec->rec_header.packet_header.pkt_encap                 = WTAP_ENCAP_ETHERNET;
794
0
  rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1;
795
796
  /*
797
   * Allocate a buffer big enough to hold the claimed packet length
798
   * worth of byte values; each byte will be two hex digits, so the
799
   * buffer's size should be twice the packet length.
800
   *
801
   * (There is no need to null-terminate the buffer.)
802
   */
803
0
  ascii_buf = (char *)g_malloc (pkt_len*2);
804
0
  ascii_offset = 0;
805
806
  /*
807
   * Copy in the Ethernet header.
808
   *
809
   * The three fields have already been checked to have the right length
810
   * (6 bytes, hence 12 characters, of hex-dump destination and source
811
   * addresses, and 2 bytes, hence 4 characters, of hex-dump type/length).
812
   *
813
   * pkt_len is guaranteed to be >= 14, so 2*pkt_len is guaranteed to be
814
   * >= 28, so we don't need to do any bounds checking.
815
   */
816
0
  memcpy(&ascii_buf[0], destmac, 12);
817
0
  ascii_offset += 12;
818
0
  memcpy(&ascii_buf[12], srcmac, 12);
819
0
  ascii_offset += 12;
820
0
  memcpy(&ascii_buf[24], type, 4);
821
0
  ascii_offset += 4;
822
823
  /*
824
   * Start reading packet contents
825
   */
826
0
  isCurrentPacket = true;
827
828
  /* loop through packet lines and breakout when the next packet header is read */
829
0
  pktline = 0;
830
0
  while (isCurrentPacket)
831
0
    {
832
0
      pktline++;
833
      /* Read the next line */
834
0
      if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
835
0
        {
836
0
          *err = file_error (fh, err_info);
837
0
          if (*err == 0)
838
0
            {
839
              /* Hit the EOF without an error */
840
0
              break;
841
0
            }
842
0
          goto errxit;
843
0
        }
844
845
      /* Convert UNICODE data to ASCII and determine line length */
846
0
      if (iseries->format == ISERIES_FORMAT_UNICODE)
847
0
        {
848
0
         buflen = iseries_UNICODE_to_ASCII ((uint8_t *)data, ISERIES_LINE_LENGTH);
849
0
        }
850
0
      else
851
0
        {
852
          /* Else bytes to rewind is just length of ASCII string */
853
0
          buflen = (int) strlen (data);
854
0
        }
855
856
      /*
857
       * Skip leading white space.
858
       */
859
0
      for (offset = 0; g_ascii_isspace(data[offset]); offset++)
860
0
        ;
861
862
      /*
863
       * The higher-level header information starts at an offset of
864
       * 22 characters.  The header tags are 14 characters long.
865
       *
866
       * XXX - for IPv6, if the next header isn't the last header,
867
       * the intermediate headers do *NOT* appear to be shown in
868
       * the dump file *at all*, so the packet *cannot* be
869
       * reconstructed!
870
       */
871
0
      if (offset == 22)
872
0
        {
873
0
          if (strncmp(data + 22, "IP Header  :  ", 14) == 0 ||
874
0
              strncmp(data + 22, "IPv6 Header:  ", 14) == 0 ||
875
0
              strncmp(data + 22, "ARP Header :  ", 14) == 0 ||
876
0
              strncmp(data + 22, "TCP Header :  ", 14) == 0 ||
877
0
              strncmp(data + 22, "UDP Header :  ", 14) == 0 ||
878
0
              strncmp(data + 22, "ICMP Header:  ", 14) == 0 ||
879
0
              strncmp(data + 22, "ICMPv6  Hdr:  ", 14) == 0 ||
880
0
              strncmp(data + 22, "Option  Hdr:  ", 14) == 0)
881
0
            {
882
0
              ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
883
0
                                               pkt_len*2,
884
0
                                               data + 22 + 14, err,
885
0
                                               err_info);
886
0
              if (ascii_offset == -1)
887
0
                {
888
                  /* Bad line. */
889
0
                  return false;
890
0
                }
891
0
              continue;
892
0
            }
893
0
        }
894
895
      /*
896
       * Is this a data line?
897
       *
898
       * The "Data" starts at an offset of 8.
899
       */
900
0
      if (offset == 9)
901
0
        {
902
0
          if (strncmp(data + 9, "Data . . . . . :  ", 18) == 0)
903
0
            {
904
0
              ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
905
0
                                               pkt_len*2,
906
0
                                               data + 9 + 18, err,
907
0
                                               err_info);
908
0
              if (ascii_offset == -1)
909
0
                {
910
                  /* Bad line. */
911
0
                  return false;
912
0
                }
913
0
              continue;
914
0
            }
915
0
        }
916
917
      /*
918
       * Is this a continuation of a previous header or data line?
919
       * That's blanks followed by hex digits; first try the
920
       * "no column separators" form.
921
       *
922
       * Continuations of header lines begin at an offset of 36;
923
       * continuations of data lines begin at an offset of 27.
924
       */
925
0
      if (offset == 36 || offset == 27)
926
0
        {
927
0
          ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
928
0
                                           pkt_len*2,
929
0
                                           data + offset, err,
930
0
                                           err_info);
931
0
          if (ascii_offset == -1)
932
0
            {
933
              /* Bad line. */
934
0
              return false;
935
0
            }
936
0
          continue;
937
0
        }
938
939
      /*
940
       * If we see the identifier for the next packet then rewind and set
941
       * isCurrentPacket false
942
       */
943
0
      ascii_strup_inplace (data);
944
      /* If packet header found return the offset */
945
0
      num_items_scanned =
946
0
          sscanf (data+78,
947
0
          "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
948
0
      if ((num_items_scanned == 1) && pktline > 1)
949
0
        {
950
0
          isCurrentPacket = false;
951
0
          cur_off = file_tell( fh);
952
0
          if (cur_off == -1)
953
0
            {
954
              /* Error. */
955
0
              *err = file_error (fh, err_info);
956
0
              goto errxit;
957
0
            }
958
0
          if (file_seek (fh, cur_off - buflen, SEEK_SET, err) == -1)
959
0
            {
960
              /* XXX: need to set err_info ?? */
961
0
              goto errxit;
962
0
            }
963
0
        }
964
0
    }
965
966
  /*
967
   * Make the captured length be the amount of bytes we've read (which
968
   * is half the number of characters of hex dump we have).
969
   *
970
   * XXX - this can happen for IPv6 packets if the next header isn't the
971
   * last header.
972
   */
973
0
  rec->rec_header.packet_header.caplen = ((uint32_t) ascii_offset)/2;
974
975
  /* Make sure we have enough room for the packet. */
976
0
  ws_buffer_assure_space (&rec->data, rec->rec_header.packet_header.caplen);
977
  /* Convert ascii data to binary and return in the frame buffer */
978
0
  iseries_parse_hex_string (ascii_buf, ws_buffer_start_ptr (&rec->data), ascii_offset);
979
980
  /* free buffer allocs and return */
981
0
  *err = 0;
982
0
  g_free (ascii_buf);
983
0
  return true;
984
985
0
errxit:
986
0
  g_free (ascii_buf);
987
0
  return false;
988
0
}
989
990
/*
991
 * Simple routine to convert an UNICODE buffer to ASCII
992
 *
993
 * XXX - This may be possible with iconv or similar
994
 */
995
static int
996
iseries_UNICODE_to_ASCII (uint8_t * buf, unsigned bytes)
997
0
{
998
0
  unsigned   i;
999
0
  uint8_t *bufptr;
1000
1001
0
  bufptr = buf;
1002
1003
0
  for (i = 0; i < bytes; i++)
1004
0
    {
1005
0
      switch (buf[i])
1006
0
        {
1007
0
          case 0xFE:
1008
0
          case 0xFF:
1009
0
          case 0x00:
1010
0
            break;
1011
0
          default:
1012
0
            *bufptr = buf[i];
1013
0
            bufptr++;
1014
0
        }
1015
0
      if (buf[i] == 0x0A)
1016
0
        break;
1017
0
    }
1018
0
  ws_assert(bufptr < buf + bytes);
1019
0
  *bufptr = '\0';
1020
0
  return i;
1021
0
}
1022
1023
/*
1024
 * Simple routine to convert an ASCII hex string to binary data
1025
 * Requires ASCII hex data and buffer to populate with binary data
1026
 */
1027
static bool
1028
iseries_parse_hex_string (const char * ascii, uint8_t * buf, size_t len)
1029
0
{
1030
0
  size_t i;
1031
0
  int byte;
1032
0
  int    hexvalue;
1033
0
  uint8_t bytevalue;
1034
1035
0
  byte = 0;
1036
0
  for (i = 0; i < len; i++)
1037
0
    {
1038
0
      hexvalue = g_ascii_xdigit_value(ascii[i]);
1039
0
      i++;
1040
0
      if (hexvalue == -1)
1041
0
        return false;        /* not a valid hex digit */
1042
0
      bytevalue = (uint8_t)(hexvalue << 4);
1043
0
      if (i >= len)
1044
0
        return false;        /* only one hex digit of the byte is present */
1045
0
      hexvalue = g_ascii_xdigit_value(ascii[i]);
1046
0
      if (hexvalue == -1)
1047
0
        return false;        /* not a valid hex digit */
1048
0
      bytevalue |= (uint8_t) hexvalue;
1049
0
      buf[byte] = bytevalue;
1050
0
      byte++;
1051
0
    }
1052
0
  return true;
1053
0
}
1054
1055
static const struct supported_block_type iseries_blocks_supported[] = {
1056
  /*
1057
   * We support packet blocks, with no comments or other options.
1058
   */
1059
  { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
1060
};
1061
1062
static const struct file_type_subtype_info iseries_info = {
1063
  "IBM iSeries comm. trace (ASCII)", "iseries_ascii", "txt", NULL,
1064
  false, BLOCKS_SUPPORTED(iseries_blocks_supported),
1065
  NULL, NULL, NULL
1066
};
1067
1068
static const struct supported_block_type iseries_unicode_blocks_supported[] = {
1069
  /*
1070
   * We support packet blocks, with no comments or other options.
1071
   */
1072
  { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
1073
};
1074
1075
static const struct file_type_subtype_info iseries_unicode_info = {
1076
  "IBM iSeries comm. trace (Unicode)", "iseries_unicode", "txt", NULL,
1077
  false, BLOCKS_SUPPORTED(iseries_unicode_blocks_supported),
1078
  NULL, NULL, NULL
1079
};
1080
1081
void register_iseries(void)
1082
2
{
1083
2
  iseries_file_type_subtype = wtap_register_file_type_subtype(&iseries_info);
1084
2
  iseries_unicode_file_type_subtype = wtap_register_file_type_subtype(&iseries_unicode_info);
1085
1086
  /*
1087
   * Register names for backwards compatibility with the
1088
   * wtap_filetypes table in Lua.
1089
   */
1090
2
  wtap_register_backwards_compatibility_lua_name("ISERIES",
1091
2
                                                 iseries_file_type_subtype);
1092
2
  wtap_register_backwards_compatibility_lua_name("ISERIES_UNICODE",
1093
2
                                                 iseries_unicode_file_type_subtype);
1094
2
}
1095
1096
/*
1097
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1098
 *
1099
 * Local variables:
1100
 * c-basic-offset: 2
1101
 * tab-width: 8
1102
 * indent-tabs-mode: nil
1103
 * End:
1104
 *
1105
 * vi: set shiftwidth=2 tabstop=8 expandtab:
1106
 * :indentSize=2:tabSize=8:noTabs=true:
1107
 */