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