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 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 "wtap-int.h"
141 #include "iseries.h"
142 #include "file_wrappers.h"
143 
144 #include <stdlib.h>
145 #include <string.h>
146 #include <errno.h>
147 
148 #include <wsutil/str_util.h>
149 #include <wsutil/strtoi.h>
150 #include <wsutil/ws_assert.h>
151 
152 #define ISERIES_LINE_LENGTH           270
153 #define ISERIES_HDR_LINES_TO_CHECK    100
154 #define ISERIES_PKT_LINES_TO_CHECK    4
155 #define ISERIES_MAX_TRACE_LEN         99999999
156 #define ISERIES_FORMAT_ASCII          1
157 #define ISERIES_FORMAT_UNICODE        2
158 
159 /*
160  * Magic strings - "COMMUNICATIONS TRACE", in ASCII and little-endian UCS-2.
161  */
162 static const char iseries_hdr_magic_ascii[] = {
163 	'C', 'O', 'M', 'M',
164 	'U', 'N', 'I', 'C',
165 	'A', 'T', 'I', 'O',
166 	'N', 'S', ' ', 'T',
167 	'R', 'A', 'C', 'E'
168 };
169 static const char iseries_hdr_magic_le_ucs_2[] = {
170 	'C', 0x0, 'O', 0x0, 'M', 0x0, 'M', 0x0,
171 	'U', 0x0, 'N', 0x0, 'I', 0x0, 'C', 0x0,
172 	'A', 0x0, 'T', 0x0, 'I', 0x0, 'O', 0x0,
173 	'N', 0x0, 'S', 0x0, ' ', 0x0, 'T', 0x0,
174 	'R', 0x0, 'A', 0x0, 'C', 0x0, 'E', 0x0
175 };
176 
177 typedef struct {
178   gboolean have_date;           /* TRUE if we found a capture start date */
179   int      year, month, day;    /* The start date */
180   int      format;              /* Trace format type        */
181 } iseries_t;
182 
183 static gboolean iseries_read (wtap * wth, wtap_rec *rec, Buffer *buf,
184                               int *err, gchar ** err_info, gint64 *data_offset);
185 static gboolean iseries_seek_read (wtap * wth, gint64 seek_off,
186                                    wtap_rec *rec,
187                                    Buffer * buf, int *err, gchar ** err_info);
188 static gboolean iseries_check_file_type (wtap * wth, int *err, gchar **err_info,
189                                          int format);
190 static gint64 iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info);
191 static gboolean iseries_parse_packet (wtap * wth, FILE_T fh,
192                                       wtap_rec *rec,
193                                       Buffer * buf, int *err, gchar ** err_info);
194 static int iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes);
195 static gboolean iseries_parse_hex_string (const char * ascii, guint8 * buf,
196                                           size_t len);
197 
198 static int iseries_file_type_subtype = -1;
199 static int iseries_unicode_file_type_subtype = -1;
200 
201 void register_iseries(void);
202 
203 /*
204  * XXX - it would probably be cleaner to use a UCS-2 flavor of file_gets(),
205  * rather than file_gets(), if we're reading a UCS-2 file.
206  */
207 wtap_open_return_val
iseries_open(wtap * wth,int * err,gchar ** err_info)208 iseries_open (wtap * wth, int *err, gchar ** err_info)
209 {
210   gint offset;
211   char magic[ISERIES_LINE_LENGTH];
212 
213   /*
214    * Check that file starts with a valid iSeries COMMS TRACE header
215    * by scanning for it in the first line
216    */
217   if (!wtap_read_bytes (wth->fh, &magic, sizeof magic, err, err_info))
218     {
219       if (*err != WTAP_ERR_SHORT_READ)
220         return WTAP_OPEN_ERROR;
221       return WTAP_OPEN_NOT_MINE;
222     }
223 
224   /*
225    * Check if this is a little-endian UCS-2 Unicode formatted file by scanning
226    * for the magic string
227    */
228   offset=0;
229   while ((unsigned int)offset < (ISERIES_LINE_LENGTH - (sizeof iseries_hdr_magic_le_ucs_2)))
230     {
231       if (memcmp (magic + offset, iseries_hdr_magic_le_ucs_2, sizeof iseries_hdr_magic_le_ucs_2) == 0) {
232         if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
233           {
234             return WTAP_OPEN_ERROR;
235           }
236         /*
237          * Do some basic sanity checking to ensure we can handle the
238          * contents of this trace
239          */
240         if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_UNICODE))
241           {
242             if (*err == 0)
243               return WTAP_OPEN_NOT_MINE;
244             else
245               return WTAP_OPEN_ERROR;
246           }
247 
248         wth->file_encap        = WTAP_ENCAP_ETHERNET;
249         wth->file_type_subtype = iseries_unicode_file_type_subtype;
250         wth->snapshot_length   = 0;
251         wth->subtype_read      = iseries_read;
252         wth->subtype_seek_read = iseries_seek_read;
253         wth->file_tsprec       = WTAP_TSPREC_USEC;
254 
255         if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
256           {
257             return WTAP_OPEN_ERROR;
258           }
259 
260         /*
261          * Add an IDB; we don't know how many interfaces were
262          * involved, so we just say one interface, about which
263          * we only know the link-layer type, snapshot length,
264          * and time stamp resolution.
265          */
266         wtap_add_generated_idb(wth);
267 
268         return WTAP_OPEN_MINE;
269       }
270       offset += 1;
271     }
272 
273     /*
274      * Check if this is a ASCII formatted file by scanning for the magic string
275      */
276     offset=0;
277     while ((unsigned int)offset < (ISERIES_LINE_LENGTH - sizeof iseries_hdr_magic_ascii))
278       {
279         if (memcmp (magic + offset, iseries_hdr_magic_ascii, sizeof iseries_hdr_magic_ascii) == 0)
280           {
281             if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
282               {
283                 return WTAP_OPEN_ERROR;
284               }
285             /*
286              * Do some basic sanity checking to ensure we can handle the
287              * contents of this trace
288              */
289             if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_ASCII))
290               {
291                 if (*err == 0)
292                   return WTAP_OPEN_NOT_MINE;
293                 else
294                   return WTAP_OPEN_ERROR;
295               }
296 
297             wth->file_encap        = WTAP_ENCAP_ETHERNET;
298             wth->file_type_subtype = iseries_file_type_subtype;
299             wth->snapshot_length   = 0;
300             wth->subtype_read      = iseries_read;
301             wth->subtype_seek_read = iseries_seek_read;
302             wth->file_tsprec       = WTAP_TSPREC_USEC;
303 
304             if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
305               {
306                 return WTAP_OPEN_ERROR;
307               }
308 
309             /*
310              * Add an IDB; we don't know how many interfaces were
311              * involved, so we just say one interface, about which
312              * we only know the link-layer type, snapshot length,
313              * and time stamp resolution.
314              */
315             wtap_add_generated_idb(wth);
316 
317             return WTAP_OPEN_MINE;
318           }
319         offset += 1;
320       }
321 
322     /* Neither ASCII or UNICODE so not supported */
323     return WTAP_OPEN_NOT_MINE;
324     }
325 
326 /*
327  * Do some basic sanity checking to ensure we can handle the
328  * contents of this trace by checking the header page for
329  * requisite requirements and additional information.
330  */
331 static gboolean
iseries_check_file_type(wtap * wth,int * err,gchar ** err_info,int format)332 iseries_check_file_type (wtap * wth, int *err, gchar **err_info, int format)
333 {
334   gboolean   is_iseries = FALSE;
335   guint      line;
336   int        num_items_scanned;
337   char       buf[ISERIES_LINE_LENGTH], protocol[9];
338   iseries_t *iseries;
339 
340   /* Save trace format for passing between packets */
341   iseries                = g_new(iseries_t, 1);
342   iseries->have_date     = FALSE;
343   iseries->format        = format;
344 
345   for (line = 0; line < ISERIES_HDR_LINES_TO_CHECK; line++)
346     {
347       memset(buf, 0x0, sizeof(buf));
348       if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
349         {
350           /* EOF or error. */
351           *err = file_error (wth->fh, err_info);
352           if (*err == WTAP_ERR_SHORT_READ)
353             *err = 0;
354           break;
355         }
356 
357       /*
358        * Check that we are dealing with an ETHERNET trace
359        */
360       if (iseries->format == ISERIES_FORMAT_UNICODE)
361         {
362           iseries_UNICODE_to_ASCII ((guint8 *)buf, ISERIES_LINE_LENGTH);
363         }
364       ascii_strup_inplace (buf);
365       num_items_scanned = sscanf (buf,
366                                  "%*[ \n\t]OBJECT PROTOCOL%*[ .:\n\t]%8s",
367                                  protocol);
368       if (num_items_scanned == 1)
369         {
370           if (memcmp (protocol, "ETHERNET", 8) == 0)
371             {
372               *err = 0;
373               is_iseries = TRUE;
374             }
375         }
376 
377       /*
378        * The header is the only place where the date part of the timestamp is held, so
379        * extract it here and store for all packets to access
380        */
381       num_items_scanned = sscanf (buf,
382                                   "%*[ \n\t]START DATE/TIME%*[ .:\n\t]%2d/%2d/%2d",
383                                   &iseries->month, &iseries->day,
384                                   &iseries->year);
385       if (num_items_scanned == 3)
386         {
387           iseries->have_date = TRUE;
388         }
389     }
390 
391   if (is_iseries)
392     wth->priv = (void *) iseries;
393   else
394     g_free(iseries);
395 
396   return is_iseries;
397 }
398 
399 /*
400  * Find the next packet and parse it; called from wtap_read().
401  */
402 static gboolean
iseries_read(wtap * wth,wtap_rec * rec,Buffer * buf,int * err,gchar ** err_info,gint64 * data_offset)403 iseries_read (wtap * wth, wtap_rec *rec, Buffer *buf, int *err,
404     gchar ** err_info, gint64 *data_offset)
405 {
406   gint64 offset;
407 
408   /*
409    * Locate the next packet
410    */
411   offset = iseries_seek_next_packet (wth, err, err_info);
412   if (offset < 0)
413     return FALSE;
414   *data_offset     = offset;
415 
416   /*
417    * Parse the packet and extract the various fields
418    */
419   return iseries_parse_packet (wth, wth->fh, rec, buf, err, err_info);
420 }
421 
422 /*
423  * Seeks to the beginning of the next packet, and returns the
424  * byte offset.  Returns -1 on failure or EOF; on EOF, sets
425  * *err to 0, and, on failure, sets *err to the error and *err_info
426  * to null or an additional error string.
427  */
428 static gint64
iseries_seek_next_packet(wtap * wth,int * err,gchar ** err_info)429 iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info)
430 {
431   iseries_t *iseries = (iseries_t *)wth->priv;
432   char       buf[ISERIES_LINE_LENGTH],type[5];
433   int        line, num_items_scanned;
434   gint64     cur_off;
435   long       buflen;
436 
437   for (line = 0; line < ISERIES_MAX_TRACE_LEN; line++)
438     {
439       if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
440         {
441           /* EOF or error. */
442           *err = file_error (wth->fh, err_info);
443           return -1;
444         }
445       /* Convert UNICODE to ASCII if required and determine    */
446       /* the number of bytes to rewind to beginning of record. */
447       if (iseries->format == ISERIES_FORMAT_UNICODE)
448         {
449           /* buflen is #bytes to 1st 0x0A */
450           buflen = iseries_UNICODE_to_ASCII ((guint8 *) buf, ISERIES_LINE_LENGTH);
451         }
452       else
453         {
454           /* Else buflen is just length of the ASCII string */
455           buflen = (long) strlen (buf);
456         }
457       ascii_strup_inplace (buf);
458       /* Check we have enough data in the line */
459       if (buflen < 78)
460         {
461           continue;
462         }
463       /* If packet header found return the offset */
464       num_items_scanned =
465         sscanf (buf+78,
466                 "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
467       if (num_items_scanned == 1)
468         {
469           /* Rewind to beginning of line */
470           cur_off = file_tell (wth->fh);
471           if (cur_off == -1)
472             {
473               *err = file_error (wth->fh, err_info);
474               return -1;
475             }
476           if (file_seek (wth->fh, cur_off - buflen, SEEK_SET, err) == -1)
477             {
478               return -1;
479             }
480           return cur_off - buflen;
481         }
482     }
483 
484   *err = WTAP_ERR_BAD_FILE;
485   *err_info =
486     g_strdup_printf ("iseries: next packet header not found within %d lines",
487              ISERIES_MAX_TRACE_LEN);
488   return -1;
489 }
490 
491 /*
492  * Read packets in random-access fashion
493  */
494 static gboolean
iseries_seek_read(wtap * wth,gint64 seek_off,wtap_rec * rec,Buffer * buf,int * err,gchar ** err_info)495 iseries_seek_read (wtap * wth, gint64 seek_off, wtap_rec *rec,
496                    Buffer * buf, int *err, gchar ** err_info)
497 {
498 
499   /* seek to packet location */
500   if (file_seek (wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
501     return FALSE;
502 
503   /*
504    * Parse the packet and extract the various fields
505    */
506   return iseries_parse_packet (wth, wth->random_fh, rec, buf,
507                                err, err_info);
508 }
509 
510 static int
append_hex_digits(char * ascii_buf,int ascii_offset,int max_offset,char * data,int * err,gchar ** err_info)511 append_hex_digits(char *ascii_buf, int ascii_offset, int max_offset,
512                   char *data, int *err, gchar **err_info)
513 {
514   int in_offset, out_offset;
515   int c;
516   unsigned int i;
517   gboolean overflow = FALSE;
518 
519   in_offset = 0;
520   out_offset = ascii_offset;
521   for (;;)
522     {
523       /*
524        * Process a block of up to 16 hex digits.
525        * The block is terminated early by an end-of-line indication (NUL,
526        * CR, or LF), by a space (which terminates the last block of the
527        * data we're processing), or by a "*", which introduces the ASCII representation
528        * of the data.
529        * All characters in the block must be upper-case hex digits;
530        * there might or might not be a space *after* a block, but, if so,
531        * that will be skipped over after the block is processed.
532        */
533       for (i = 0; i < 16; i++, in_offset++)
534         {
535           /*
536            * If we see an end-of-line indication, or an early-end-of-block
537            * indication (space), we're done.  (Only the last block ends
538            * early.)
539            */
540           c = data[in_offset] & 0xFF;
541           if (c == '\0' || c == ' ' || c == '*' || c == '\r' || c == '\n')
542             {
543               goto done;
544             }
545           if (!g_ascii_isxdigit(c) || g_ascii_islower(c))
546             {
547               /*
548                * Not a hex digit, or a lower-case hex digit.
549                * Treat this as an indication that the line isn't a data
550                * line, so we just ignore it.
551                *
552                * XXX - do so only for continuation lines; treat non-hex-digit
553                * characters as errors for other lines?
554                */
555               return ascii_offset; /* pretend we appended nothing */
556             }
557           if (out_offset >= max_offset)
558             overflow = TRUE;
559           else
560             {
561               ascii_buf[out_offset] = c;
562               out_offset++;
563             }
564         }
565       /*
566        * Skip blanks, if any.
567        */
568       for (; (data[in_offset] & 0xFF) == ' '; in_offset++)
569         ;
570     }
571 done:
572   /*
573    * If we processed an *odd* number of hex digits, report an error.
574    */
575   if ((i % 2) != 0)
576     {
577       *err = WTAP_ERR_BAD_FILE;
578       *err_info = g_strdup("iseries: odd number of hex digits in a line");
579       return -1;
580     }
581   if (overflow)
582     {
583       *err = WTAP_ERR_BAD_FILE;
584       *err_info = g_strdup("iseries: more packet data than the packet length indicated");
585       return -1;
586     }
587   return out_offset;
588 }
589 
590 /* return the multiplier for nanoseconds */
591 static guint32
csec_multiplier(guint32 csec)592 csec_multiplier(guint32 csec)
593 {
594   if (csec < 10) return 100000000;
595   if (csec < 100) return 10000000;
596   if (csec < 1000) return 1000000;
597   if (csec < 10000) return 100000;
598   if (csec < 100000) return 10000;
599   if (csec < 1000000) return 1000;
600   if (csec < 10000000) return 100;
601   if (csec < 100000000) return 10;
602   return 1;
603 }
604 
605 /* Parses a packet. */
606 static gboolean
iseries_parse_packet(wtap * wth,FILE_T fh,wtap_rec * rec,Buffer * buf,int * err,gchar ** err_info)607 iseries_parse_packet (wtap * wth, FILE_T fh, wtap_rec *rec,
608                       Buffer *buf, int *err, gchar **err_info)
609 {
610   iseries_t *iseries = (iseries_t *)wth->priv;
611   gint64     cur_off;
612   gboolean   isValid, isCurrentPacket;
613   int        num_items_scanned, line, pktline, buflen;
614   int        pkt_len, pktnum, hr, min, sec;
615   char       direction[2], destmac[13], srcmac[13], type[5];
616   guint32    csec;
617   char       data[ISERIES_LINE_LENGTH * 2];
618   int        offset;
619   char      *ascii_buf;
620   int        ascii_offset;
621   struct tm  tm;
622 
623   /*
624    * Check for packet headers in first 3 lines this should handle page breaks
625    * situations and the header lines output at each page throw and ensure we
626    * read both the captured and packet lengths.
627    */
628   isValid = FALSE;
629   for (line = 1; line < ISERIES_PKT_LINES_TO_CHECK; line++)
630     {
631       if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
632         {
633           *err = file_error (fh, err_info);
634           return FALSE;
635         }
636       /* Convert UNICODE data to ASCII */
637       if (iseries->format == ISERIES_FORMAT_UNICODE)
638         {
639          iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
640         }
641       ascii_strup_inplace (data);
642       num_items_scanned =
643         sscanf (data,
644                 "%*[ \n\t]%6d%*[ *\n\t]%1s%*[ \n\t]%6d%*[ \n\t]%2d:%2d:%2d.%9u%*[ \n\t]"
645                 "%12s%*[ \n\t]%12s%*[ \n\t]ETHV2%*[ \n\t]TYPE:%*[ \n\t]%4s",
646                 &pktnum, direction, &pkt_len, &hr, &min, &sec, &csec, destmac,
647                 srcmac, type);
648       if (num_items_scanned == 10)
649         {
650           if (pktnum < 0)
651             {
652               *err = WTAP_ERR_BAD_FILE;
653               *err_info = g_strdup ("iseries: packet header has a negative packet number");
654               return FALSE;
655             }
656 
657           if (pkt_len < 0)
658             {
659               *err = WTAP_ERR_BAD_FILE;
660               *err_info = g_strdup ("iseries: packet header has a negative packet length");
661               return FALSE;
662             }
663 
664           if (hr < 0)
665             {
666               *err = WTAP_ERR_BAD_FILE;
667               *err_info = g_strdup ("iseries: packet header has a negative hour in the time stamp");
668               return FALSE;
669             }
670 
671           if (hr > 23)
672             {
673               *err = WTAP_ERR_BAD_FILE;
674               *err_info = g_strdup ("iseries: packet header has a hour in the time stamp greater than 23");
675               return FALSE;
676             }
677 
678           if (min < 0)
679             {
680               *err = WTAP_ERR_BAD_FILE;
681               *err_info = g_strdup ("iseries: packet header has a negative minute in the time stamp");
682               return FALSE;
683             }
684 
685           if (min > 59)
686             {
687               *err = WTAP_ERR_BAD_FILE;
688               *err_info = g_strdup ("iseries: packet header has a minute in the time stamp greater than 59");
689               return FALSE;
690             }
691 
692           if (sec < 0)
693             {
694               *err = WTAP_ERR_BAD_FILE;
695               *err_info = g_strdup ("iseries: packet header has a negative second in the time stamp");
696               return FALSE;
697             }
698 
699           /*
700            * Yes, 60, even though the time-conversion routines on most OSes
701            * might not handle leap seconds.
702            */
703           if (sec > 60)
704             {
705               *err = WTAP_ERR_BAD_FILE;
706               *err_info = g_strdup ("iseries: packet header has a second in the time stamp greater than 60");
707               return FALSE;
708             }
709 
710           if (strlen(destmac) != 12)
711             {
712               *err = WTAP_ERR_BAD_FILE;
713               *err_info = g_strdup ("iseries: packet header has a destination MAC address shorter than 6 bytes");
714               return FALSE;
715             }
716 
717           if (strlen(srcmac) != 12)
718             {
719               *err = WTAP_ERR_BAD_FILE;
720               *err_info = g_strdup ("iseries: packet header has a source MAC address shorter than 6 bytes");
721               return FALSE;
722             }
723 
724           if (strlen(type) != 4)
725             {
726               *err = WTAP_ERR_BAD_FILE;
727               *err_info = g_strdup ("iseries: packet header has an Ethernet type/length field than 2 bytes");
728               return FALSE;
729             }
730 
731           /* OK! We found the packet header line */
732           isValid = TRUE;
733           /*
734            * XXX - The Capture length returned by the iSeries trace doesn't
735            * seem to include the Ethernet header, so we add its length here.
736            *
737            * Check the length first, just in case it's *so* big that, after
738            * adding the Ethernet header length, it overflows.
739            */
740           if ((guint)pkt_len > WTAP_MAX_PACKET_SIZE_STANDARD - 14)
741             {
742               /*
743                * Probably a corrupt capture file; don't blow up trying
744                * to allocate space for an immensely-large packet, and
745                * don't think it's a really *small* packet because it
746                * overflowed.  (Calculate the size as a 64-bit value in
747                * the error message, to avoid an overflow.)
748                */
749               *err = WTAP_ERR_BAD_FILE;
750               *err_info = g_strdup_printf("iseries: File has %" G_GUINT64_FORMAT "-byte packet, bigger than maximum of %u",
751                                           (guint64)pkt_len + 14,
752                                           WTAP_MAX_PACKET_SIZE_STANDARD);
753               return FALSE;
754             }
755           pkt_len += 14;
756           break;
757         }
758     }
759 
760   /*
761    * If no packet header found we exit at this point and inform the user.
762    */
763   if (!isValid)
764     {
765       *err = WTAP_ERR_BAD_FILE;
766       *err_info = g_strdup ("iseries: packet header isn't valid");
767       return FALSE;
768     }
769 
770   rec->rec_type = REC_TYPE_PACKET;
771   rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
772   rec->presence_flags = WTAP_HAS_CAP_LEN;
773 
774   /*
775    * If we have Wiretap Header then populate it here
776    *
777    * Timer resolution on the iSeries is hardware dependent.  We determine
778    * the resolution based on how many digits we see.
779    */
780   if (iseries->have_date)
781     {
782       rec->presence_flags |= WTAP_HAS_TS;
783       tm.tm_year        = 100 + iseries->year;
784       tm.tm_mon         = iseries->month - 1;
785       tm.tm_mday        = iseries->day;
786       tm.tm_hour        = hr;
787       tm.tm_min         = min;
788       tm.tm_sec         = sec;
789       tm.tm_isdst       = -1;
790       rec->ts.secs = mktime (&tm);
791       rec->ts.nsecs = csec * csec_multiplier(csec);
792     }
793 
794   rec->rec_header.packet_header.len                       = pkt_len;
795   rec->rec_header.packet_header.pkt_encap                 = WTAP_ENCAP_ETHERNET;
796   rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1;
797 
798   /*
799    * Allocate a buffer big enough to hold the claimed packet length
800    * worth of byte values; each byte will be two hex digits, so the
801    * buffer's size should be twice the packet length.
802    *
803    * (There is no need to null-terminate the buffer.)
804    */
805   ascii_buf = (char *)g_malloc (pkt_len*2);
806   ascii_offset = 0;
807 
808   /*
809    * Copy in the Ethernet header.
810    *
811    * The three fields have already been checked to have the right length
812    * (6 bytes, hence 12 characters, of hex-dump destination and source
813    * addresses, and 2 bytes, hence 4 characters, of hex-dump type/length).
814    *
815    * pkt_len is guaranteed to be >= 14, so 2*pkt_len is guaranteed to be
816    * >= 28, so we don't need to do any bounds checking.
817    */
818   memcpy(&ascii_buf[0], destmac, 12);
819   ascii_offset += 12;
820   memcpy(&ascii_buf[12], srcmac, 12);
821   ascii_offset += 12;
822   memcpy(&ascii_buf[24], type, 4);
823   ascii_offset += 4;
824 
825   /*
826    * Start reading packet contents
827    */
828   isCurrentPacket = TRUE;
829 
830   /* loop through packet lines and breakout when the next packet header is read */
831   pktline = 0;
832   while (isCurrentPacket)
833     {
834       pktline++;
835       /* Read the next line */
836       if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
837         {
838           *err = file_error (fh, err_info);
839           if (*err == 0)
840             {
841               /* Hit the EOF without an error */
842               break;
843             }
844           goto errxit;
845         }
846 
847       /* Convert UNICODE data to ASCII and determine line length */
848       if (iseries->format == ISERIES_FORMAT_UNICODE)
849         {
850          buflen = iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
851         }
852       else
853         {
854           /* Else bytes to rewind is just length of ASCII string */
855           buflen = (int) strlen (data);
856         }
857 
858       /*
859        * Skip leading white space.
860        */
861       for (offset = 0; g_ascii_isspace(data[offset]); offset++)
862         ;
863 
864       /*
865        * The higher-level header information starts at an offset of
866        * 22 characters.  The header tags are 14 characters long.
867        *
868        * XXX - for IPv6, if the next header isn't the last header,
869        * the intermediate headers do *NOT* appear to be shown in
870        * the dump file *at all*, so the packet *cannot* be
871        * reconstructed!
872        */
873       if (offset == 22)
874         {
875           if (strncmp(data + 22, "IP Header  :  ", 14) == 0 ||
876               strncmp(data + 22, "IPv6 Header:  ", 14) == 0 ||
877               strncmp(data + 22, "ARP Header :  ", 14) == 0 ||
878               strncmp(data + 22, "TCP Header :  ", 14) == 0 ||
879               strncmp(data + 22, "UDP Header :  ", 14) == 0 ||
880               strncmp(data + 22, "ICMP Header:  ", 14) == 0 ||
881               strncmp(data + 22, "ICMPv6  Hdr:  ", 14) == 0 ||
882               strncmp(data + 22, "Option  Hdr:  ", 14) == 0)
883             {
884               ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
885                                                pkt_len*2,
886                                                data + 22 + 14, err,
887                                                err_info);
888               if (ascii_offset == -1)
889                 {
890                   /* Bad line. */
891                   return FALSE;
892                 }
893               continue;
894             }
895         }
896 
897       /*
898        * Is this a data line?
899        *
900        * The "Data" starts at an offset of 8.
901        */
902       if (offset == 9)
903         {
904           if (strncmp(data + 9, "Data . . . . . :  ", 18) == 0)
905             {
906               ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
907                                                pkt_len*2,
908                                                data + 9 + 18, err,
909                                                err_info);
910               if (ascii_offset == -1)
911                 {
912                   /* Bad line. */
913                   return FALSE;
914                 }
915               continue;
916             }
917         }
918 
919       /*
920        * Is this a continuation of a previous header or data line?
921        * That's blanks followed by hex digits; first try the
922        * "no column separators" form.
923        *
924        * Continuations of header lines begin at an offset of 36;
925        * continuations of data lines begin at an offset of 27.
926        */
927       if (offset == 36 || offset == 27)
928         {
929           ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
930                                            pkt_len*2,
931                                            data + offset, err,
932                                            err_info);
933           if (ascii_offset == -1)
934             {
935               /* Bad line. */
936               return FALSE;
937             }
938           continue;
939         }
940 
941       /*
942        * If we see the identifier for the next packet then rewind and set
943        * isCurrentPacket FALSE
944        */
945       ascii_strup_inplace (data);
946       /* If packet header found return the offset */
947       num_items_scanned =
948           sscanf (data+78,
949           "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
950       if ((num_items_scanned == 1) && pktline > 1)
951         {
952           isCurrentPacket = FALSE;
953           cur_off = file_tell( fh);
954           if (cur_off == -1)
955             {
956               /* Error. */
957               *err = file_error (fh, err_info);
958               goto errxit;
959             }
960           if (file_seek (fh, cur_off - buflen, SEEK_SET, err) == -1)
961             {
962               /* XXX: need to set err_info ?? */
963               goto errxit;
964             }
965         }
966     }
967 
968   /*
969    * Make the captured length be the amount of bytes we've read (which
970    * is half the number of characters of hex dump we have).
971    *
972    * XXX - this can happen for IPv6 packets if the next header isn't the
973    * last header.
974    */
975   rec->rec_header.packet_header.caplen = ((guint32) ascii_offset)/2;
976 
977   /* Make sure we have enough room for the packet. */
978   ws_buffer_assure_space (buf, rec->rec_header.packet_header.caplen);
979   /* Convert ascii data to binary and return in the frame buffer */
980   iseries_parse_hex_string (ascii_buf, ws_buffer_start_ptr (buf), ascii_offset);
981 
982   /* free buffer allocs and return */
983   *err = 0;
984   g_free (ascii_buf);
985   return TRUE;
986 
987 errxit:
988   g_free (ascii_buf);
989   return FALSE;
990 }
991 
992 /*
993  * Simple routine to convert an UNICODE buffer to ASCII
994  *
995  * XXX - This may be possible with iconv or similar
996  */
997 static int
iseries_UNICODE_to_ASCII(guint8 * buf,guint bytes)998 iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes)
999 {
1000   guint   i;
1001   guint8 *bufptr;
1002 
1003   bufptr = buf;
1004 
1005   for (i = 0; i < bytes; i++)
1006     {
1007       switch (buf[i])
1008         {
1009           case 0xFE:
1010           case 0xFF:
1011           case 0x00:
1012             break;
1013           default:
1014             *bufptr = buf[i];
1015             bufptr++;
1016         }
1017       if (buf[i] == 0x0A)
1018         break;
1019     }
1020   ws_assert(bufptr < buf + bytes);
1021   *bufptr = '\0';
1022   return i;
1023 }
1024 
1025 /*
1026  * Simple routine to convert an ASCII hex string to binary data
1027  * Requires ASCII hex data and buffer to populate with binary data
1028  */
1029 static gboolean
iseries_parse_hex_string(const char * ascii,guint8 * buf,size_t len)1030 iseries_parse_hex_string (const char * ascii, guint8 * buf, size_t len)
1031 {
1032   size_t i;
1033   int byte;
1034   gint   hexvalue;
1035   guint8 bytevalue;
1036 
1037   byte = 0;
1038   for (i = 0; i < len; i++)
1039     {
1040       hexvalue = g_ascii_xdigit_value(ascii[i]);
1041       i++;
1042       if (hexvalue == -1)
1043         return FALSE;        /* not a valid hex digit */
1044       bytevalue = (guint8)(hexvalue << 4);
1045       if (i >= len)
1046         return FALSE;        /* only one hex digit of the byte is present */
1047       hexvalue = g_ascii_xdigit_value(ascii[i]);
1048       if (hexvalue == -1)
1049         return FALSE;        /* not a valid hex digit */
1050       bytevalue |= (guint8) hexvalue;
1051       buf[byte] = bytevalue;
1052       byte++;
1053     }
1054   return TRUE;
1055 }
1056 
1057 static const struct supported_block_type iseries_blocks_supported[] = {
1058   /*
1059    * We support packet blocks, with no comments or other options.
1060    */
1061   { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
1062 };
1063 
1064 static const struct file_type_subtype_info iseries_info = {
1065   "IBM iSeries comm. trace (ASCII)", "iseries_ascii", "txt", NULL,
1066   FALSE, BLOCKS_SUPPORTED(iseries_blocks_supported),
1067   NULL, NULL, NULL
1068 };
1069 
1070 static const struct supported_block_type iseries_unicode_blocks_supported[] = {
1071   /*
1072    * We support packet blocks, with no comments or other options.
1073    */
1074   { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
1075 };
1076 
1077 static const struct file_type_subtype_info iseries_unicode_info = {
1078   "IBM iSeries comm. trace (Unicode)", "iseries_unicode", "txt", NULL,
1079   FALSE, BLOCKS_SUPPORTED(iseries_unicode_blocks_supported),
1080   NULL, NULL, NULL
1081 };
1082 
register_iseries(void)1083 void register_iseries(void)
1084 {
1085   iseries_file_type_subtype = wtap_register_file_type_subtype(&iseries_info);
1086   iseries_unicode_file_type_subtype = wtap_register_file_type_subtype(&iseries_unicode_info);
1087 
1088   /*
1089    * Register names for backwards compatibility with the
1090    * wtap_filetypes table in Lua.
1091    */
1092   wtap_register_backwards_compatibility_lua_name("ISERIES",
1093                                                  iseries_file_type_subtype);
1094   wtap_register_backwards_compatibility_lua_name("ISERIES_UNICODE",
1095                                                  iseries_unicode_file_type_subtype);
1096 }
1097 
1098 /*
1099  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1100  *
1101  * Local variables:
1102  * c-basic-offset: 2
1103  * tab-width: 8
1104  * indent-tabs-mode: nil
1105  * End:
1106  *
1107  * vi: set shiftwidth=2 tabstop=8 expandtab:
1108  * :indentSize=2:tabSize=8:noTabs=true:
1109  */
1110