Don't assume packets will be no larger than ISERIES_MAX_PACKET_LEN.
[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           /* OK! We found the packet header line */
677           isValid = TRUE;
678           /*
679            * XXX - The Capture length returned by the iSeries trace doesn't
680            * seem to include the Ethernet header, so we add its length here.
681            *
682            * Check the length first, just in case it's *so* big that, after
683            * adding the Ethernet header length, it overflows.
684            */
685           if (pkt_len > WTAP_MAX_PACKET_SIZE - 14)
686             {
687               /*
688                * Probably a corrupt capture file; don't blow up trying
689                * to allocate space for an immensely-large packet, and
690                * don't think it's a really *small* packet because it
691                * overflowed.  (Calculate the size as a 64-bit value in
692                * the error message, to avoid an overflow.)
693                */
694               *err = WTAP_ERR_BAD_FILE;
695               *err_info = g_strdup_printf("iseries: File has %" G_GUINT64_FORMAT "-byte packet, bigger than maximum of %u",
696                                           (guint64)pkt_len + 14,
697                                           WTAP_MAX_PACKET_SIZE);
698               return FALSE;
699             }
700           pkt_len += 14;
701           break;
702         }
703     }
704
705   /*
706    * If no packet header found we exit at this point and inform the user.
707    */
708   if (!isValid)
709     {
710       *err = WTAP_ERR_BAD_FILE;
711       *err_info = g_strdup ("iseries: packet header isn't valid");
712       return FALSE;
713     }
714
715   phdr->rec_type = REC_TYPE_PACKET;
716   phdr->presence_flags = WTAP_HAS_CAP_LEN;
717
718   /*
719    * If we have Wiretap Header then populate it here
720    *
721    * Timer resolution on the iSeries is hardware dependent.  We determine
722    * the resolution based on how many digits we see.
723    */
724   if (iseries->have_date)
725     {
726       phdr->presence_flags |= WTAP_HAS_TS;
727       tm.tm_year        = 100 + iseries->year;
728       tm.tm_mon         = iseries->month - 1;
729       tm.tm_mday        = iseries->day;
730       tm.tm_hour        = hr;
731       tm.tm_min         = min;
732       tm.tm_sec         = sec;
733       tm.tm_isdst       = -1;
734       phdr->ts.secs = mktime (&tm);
735       csec[sizeof(csec) - 1] = '\0';
736       switch (strlen(csec))
737         {
738           case 0:
739             phdr->ts.nsecs = 0;
740             break;
741           case 1:
742             phdr->ts.nsecs = atoi(csec) * 100000000;
743             break;
744           case 2:
745             phdr->ts.nsecs = atoi(csec) * 10000000;
746             break;
747           case 3:
748             phdr->ts.nsecs = atoi(csec) * 1000000;
749             break;
750           case 4:
751             phdr->ts.nsecs = atoi(csec) * 100000;
752             break;
753           case 5:
754             phdr->ts.nsecs = atoi(csec) * 10000;
755             break;
756           case 6:
757             phdr->ts.nsecs = atoi(csec) * 1000;
758             break;
759           case 7:
760             phdr->ts.nsecs = atoi(csec) * 100;
761             break;
762           case 8:
763             phdr->ts.nsecs = atoi(csec) * 10;
764             break;
765           case 9:
766             phdr->ts.nsecs = atoi(csec);
767             break;
768         }
769     }
770
771   phdr->len                       = pkt_len;
772   phdr->pkt_encap                 = WTAP_ENCAP_ETHERNET;
773   phdr->pseudo_header.eth.fcs_len = -1;
774
775   ascii_buf = (char *)g_malloc ((pkt_len*2)+1);
776   g_snprintf(ascii_buf, (pkt_len*2)+1, "%s%s%s", destmac, srcmac, type);
777   ascii_offset = 14*2; /* 14-byte Ethernet header, 2 characters per byte */
778
779   /*
780    * Start reading packet contents
781    */
782   isCurrentPacket = TRUE;
783
784   /* loop through packet lines and breakout when the next packet header is read */
785   pktline = 0;
786   while (isCurrentPacket)
787     {
788       pktline++;
789       /* Read the next line */
790       if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
791         {
792           *err = file_error (fh, err_info);
793           if (*err == 0)
794             {
795               /* Hit the EOF without an error */
796               break;
797             }
798           goto errxit;
799         }
800
801       /* Convert UNICODE data to ASCII and determine line length */
802       if (iseries->format == ISERIES_FORMAT_UNICODE)
803         {
804          buflen = iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
805         }
806       else
807         {
808           /* Else bytes to rewind is just length of ASCII string */
809           buflen = (int) strlen (data);
810         }
811
812       /*
813        * Skip leading white space.
814        */
815       for (offset = 0; g_ascii_isspace(data[offset]); offset++)
816         ;
817
818       /*
819        * The higher-level header information starts at an offset of
820        * 22 characters.  The header tags are 14 characters long.
821        *
822        * XXX - for IPv6, if the next header isn't the last header,
823        * the intermediate headers do *NOT* appear to be shown in
824        * the dump file *at all*, so the packet *cannot* be
825        * reconstructed!
826        */
827       if (offset == 22)
828         {
829           if (strncmp(data + 22, "IP Header  :  ", 14) == 0 ||
830               strncmp(data + 22, "IPv6 Header:  ", 14) == 0 ||
831               strncmp(data + 22, "ARP Header :  ", 14) == 0 ||
832               strncmp(data + 22, "TCP Header :  ", 14) == 0 ||
833               strncmp(data + 22, "UDP Header :  ", 14) == 0 ||
834               strncmp(data + 22, "ICMP Header:  ", 14) == 0 ||
835               strncmp(data + 22, "ICMPv6  Hdr:  ", 14) == 0 ||
836               strncmp(data + 22, "Option  Hdr:  ", 14) == 0)
837             {
838               ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
839                                                pkt_len*2,
840                                                data + 22 + 14, err,
841                                                err_info);
842               if (ascii_offset == -1)
843                 {
844                   /* Bad line. */
845                   return FALSE;
846                 }
847               continue;
848             }
849         }
850
851       /*
852        * Is this a data line?
853        *
854        * The "Data" starts at an offset of 8.
855        */
856       if (offset == 9)
857         {
858           if (strncmp(data + 9, "Data . . . . . :  ", 18) == 0)
859             {
860               ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
861                                                pkt_len*2,
862                                                data + 9 + 18, err,
863                                                err_info);
864               if (ascii_offset == -1)
865                 {
866                   /* Bad line. */
867                   return FALSE;
868                 }
869               continue;
870             }
871         }
872
873       /*
874        * Is this a continuation of a previous header or data line?
875        * That's blanks followed by hex digits; first try the
876        * "no column separators" form.
877        *
878        * Continuations of header lines begin at an offset of 36;
879        * continuations of data lines begin at an offset of 27.
880        */
881       if (offset == 36 || offset == 27)
882         {
883           ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
884                                            pkt_len*2,
885                                            data + offset, err,
886                                            err_info);
887           if (ascii_offset == -1)
888             {
889               /* Bad line. */
890               return FALSE;
891             }
892           continue;
893         }
894
895       /*
896        * If we see the identifier for the next packet then rewind and set
897        * isCurrentPacket FALSE
898        */
899       ascii_strup_inplace (data);
900       /* If packet header found return the offset */
901       num_items_scanned =
902           sscanf (data+78,
903           "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
904       if ((num_items_scanned == 1) && pktline > 1)
905         {
906           isCurrentPacket = FALSE;
907           cur_off = file_tell( fh);
908           if (cur_off == -1)
909             {
910               /* Error. */
911               *err = file_error (fh, err_info);
912               goto errxit;
913             }
914           if (file_seek (fh, cur_off - buflen, SEEK_SET, err) == -1)
915             {
916               /* XXX: need to set err_info ?? */
917               goto errxit;
918             }
919         }
920     }
921   ascii_buf[ascii_offset] = '\0';
922
923   /*
924    * Make the captured length be the amount of bytes we've read (which
925    * is half the number of characters of hex dump we have).
926    *
927    * XXX - this can happen for IPv6 packets if the next header isn't the
928    * last header.
929    */
930   phdr->caplen = ((guint32) strlen (ascii_buf))/2;
931
932   /* Make sure we have enough room for the packet. */
933   ws_buffer_assure_space (buf, phdr->caplen);
934   /* Convert ascii data to binary and return in the frame buffer */
935   iseries_parse_hex_string (ascii_buf, ws_buffer_start_ptr (buf), strlen (ascii_buf));
936
937   /* free buffer allocs and return */
938   *err = 0;
939   g_free (ascii_buf);
940   return TRUE;
941
942 errxit:
943   g_free (ascii_buf);
944   return FALSE;
945 }
946
947 /*
948  * Simple routine to convert an UNICODE buffer to ASCII
949  *
950  * XXX - This may be possible with iconv or similar
951  */
952 static int
953 iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes)
954 {
955   guint   i;
956   guint8 *bufptr;
957
958   bufptr = buf;
959
960   for (i = 0; i < bytes; i++)
961     {
962       switch (buf[i])
963         {
964           case 0xFE:
965           case 0xFF:
966           case 0x00:
967             break;
968           default:
969             *bufptr = buf[i];
970             bufptr++;
971         }
972       if (buf[i] == 0x0A)
973         return i;
974     }
975   return i;
976 }
977
978 /*
979  * Simple routine to convert an ASCII hex string to binary data
980  * Requires ASCII hex data and buffer to populate with binary data
981  */
982 static gboolean
983 iseries_parse_hex_string (const char * ascii, guint8 * buf, size_t len)
984 {
985   size_t i;
986   int byte;
987   gint   hexvalue;
988   guint8 bytevalue;
989
990   byte = 0;
991   for (i = 0; i < len; i++)
992     {
993       hexvalue = g_ascii_xdigit_value(ascii[i]);
994       i++;
995       if (hexvalue == -1)
996         return FALSE;        /* not a valid hex digit */
997       bytevalue = (guint8)(hexvalue << 4);
998       if (i >= len)
999         return FALSE;        /* only one hex digit of the byte is present */
1000       hexvalue = g_ascii_xdigit_value(ascii[i]);
1001       if (hexvalue == -1)
1002         return FALSE;        /* not a valid hex digit */
1003       bytevalue |= (guint8) hexvalue;
1004       buf[byte] = bytevalue;
1005       byte++;
1006     }
1007   return TRUE;
1008 }
1009
1010 /*
1011  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
1012  *
1013  * Local variables:
1014  * c-basic-offset: 2
1015  * tab-width: 8
1016  * indent-tabs-mode: nil
1017  * End:
1018  *
1019  * vi: set shiftwidth=2 tabstop=8 expandtab:
1020  * :indentSize=2:tabSize=8:noTabs=true:
1021  */