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