No need for WTAP_ERR_CANT_READ.
[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 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 <wsutil/buffer.h>
154 #include "iseries.h"
155 #include "file_wrappers.h"
156
157 #include <stdio.h>
158 #include <stdlib.h>
159 #include <string.h>
160 #include <ctype.h>
161 #include <errno.h>
162
163 #include <wsutil/str_util.h>
164
165 #define ISERIES_HDR_MAGIC_STR         "COMMUNICATIONS TRACE"
166 #define ISERIES_HDR_MAGIC_LEN         20
167 #define ISERIES_LINE_LENGTH           270
168 #define ISERIES_HDR_LINES_TO_CHECK    100
169 #define ISERIES_PKT_LINES_TO_CHECK    4
170 #define ISERIES_MAX_PACKET_LEN        16384
171 #define ISERIES_MAX_TRACE_LEN         99999999
172 #define ISERIES_PKT_ALLOC_SIZE        (pkt_len*2)+1
173 #define ISERIES_FORMAT_ASCII          1
174 #define ISERIES_FORMAT_UNICODE        2
175
176 typedef struct {
177   gboolean have_date;           /* TRUE if we found a capture start date */
178   int      year, month, day;    /* The start date */
179   int      format;              /* Trace format type        */
180 } iseries_t;
181
182 static gboolean iseries_read (wtap * wth, int *err, gchar ** err_info,
183                               gint64 *data_offset);
184 static gboolean iseries_seek_read (wtap * wth, gint64 seek_off,
185                                    struct wtap_pkthdr *phdr,
186                                    Buffer * buf, int *err, gchar ** err_info);
187 static gboolean iseries_check_file_type (wtap * wth, int *err, gchar **err_info,
188                                          int format);
189 static gint64 iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info);
190 static gboolean iseries_parse_packet (wtap * wth, FILE_T fh,
191                                       struct wtap_pkthdr *phdr,
192                                       Buffer * buf, int *err, gchar ** err_info);
193 static int iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes);
194 static gboolean iseries_parse_hex_string (const char * ascii, guint8 * buf,
195                                           size_t len);
196
197 int
198 iseries_open (wtap * wth, int *err, gchar ** err_info)
199 {
200   gint offset;
201   char magic[ISERIES_LINE_LENGTH];
202   char unicodemagic[] =
203     { '\x43', '\x00', '\x4F', '\x00', '\x4D',
204     '\x00', '\x4D', '\x00', '\x55', '\x00', '\x4E', '\x00', '\x49', '\x00',
205     '\x43', '\x00', '\x41'
206   };
207
208   /*
209    * Check that file starts with a valid iSeries COMMS TRACE header
210    * by scanning for it in the first line
211    */
212   if (!wtap_read_bytes (wth->fh, &magic, sizeof magic, err, err_info))
213     {
214       if (*err != WTAP_ERR_SHORT_READ)
215         return -1;
216       return 0;
217     }
218
219   /*
220    * Check if this is a UNICODE formatted file by scanning for the magic string
221    */
222   offset=0;
223   while ((unsigned int)offset < (ISERIES_LINE_LENGTH - (sizeof unicodemagic)))
224     {
225       if (memcmp (magic + offset, unicodemagic, sizeof unicodemagic) == 0) {
226         if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
227           {
228             return 0;
229           }
230         /*
231          * Do some basic sanity checking to ensure we can handle the
232          * contents of this trace
233          */
234         if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_UNICODE))
235           {
236             if (*err == 0)
237               return 0;
238             else
239               return -1;
240           }
241
242         wth->file_encap        = WTAP_ENCAP_ETHERNET;
243         wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_ISERIES;
244         wth->snapshot_length   = 0;
245         wth->subtype_read      = iseries_read;
246         wth->subtype_seek_read = iseries_seek_read;
247         wth->file_tsprec       = WTAP_TSPREC_USEC;
248
249         if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
250           {
251             return 0;
252           }
253         return 1;
254       }
255       offset += 1;
256     }
257
258     /*
259      * Check if this is a ASCII formatted file by scanning for the magic string
260      */
261     offset=0;
262     while (offset < (ISERIES_LINE_LENGTH - ISERIES_HDR_MAGIC_LEN))
263       {
264         if (memcmp (magic + offset, ISERIES_HDR_MAGIC_STR, ISERIES_HDR_MAGIC_LEN) == 0)
265           {
266             if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
267               {
268                 return 0;
269               }
270             /*
271              * Do some basic sanity checking to ensure we can handle the
272              * contents of this trace
273              */
274             if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_ASCII))
275               {
276                 if (*err == 0)
277                   return 0;
278                 else
279                   return -1;
280               }
281
282             wth->file_encap        = WTAP_ENCAP_ETHERNET;
283             wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_ISERIES;
284             wth->snapshot_length   = 0;
285             wth->subtype_read      = iseries_read;
286             wth->subtype_seek_read = iseries_seek_read;
287             wth->file_tsprec       = WTAP_TSPREC_USEC;
288
289             if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
290               {
291                 return 0;
292               }
293             return 1;
294           }
295         offset += 1;
296       }
297
298     /* Neither ASCII or UNICODE so not supported */
299     return 0;
300     }
301
302 /*
303  * Do some basic sanity checking to ensure we can handle the
304  * contents of this trace by checking the header page for
305  * requisit requirements and additional information.
306  */
307 static gboolean
308 iseries_check_file_type (wtap * wth, int *err, gchar **err_info, int format)
309 {
310   guint      line;
311   int        num_items_scanned;
312   char       buf[ISERIES_LINE_LENGTH], protocol[9];
313   iseries_t *iseries;
314
315   /* Save trace format for passing between packets */
316   iseries                = (iseries_t *) g_malloc (sizeof (iseries_t));
317   wth->priv              = (void *) iseries;
318   iseries->have_date     = FALSE;
319   iseries->format        = format;
320
321   for (line = 0; line < ISERIES_HDR_LINES_TO_CHECK; line++)
322     {
323       if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
324         {
325           /* EOF or error. */
326           *err = file_error (wth->fh, err_info);
327           if (*err == WTAP_ERR_SHORT_READ)
328             *err = 0;
329           return FALSE;
330         }
331
332         /*
333          * Check that we are dealing with an ETHERNET trace
334          */
335         if (iseries->format == ISERIES_FORMAT_UNICODE)
336           {
337             iseries_UNICODE_to_ASCII ((guint8 *)buf, ISERIES_LINE_LENGTH);
338           }
339         ascii_strup_inplace (buf);
340         num_items_scanned = sscanf (buf,
341                                    "%*[ \n\t]OBJECT PROTOCOL%*[ .:\n\t]%8s",
342                                    protocol);
343         if (num_items_scanned == 1)
344           {
345             if (memcmp (protocol, "ETHERNET", 8) != 0)
346               return FALSE;
347           }
348
349         /*
350          * The header is the only place where the date part of the timestamp is held, so
351          * extract it here and store for all packets to access
352          */
353         num_items_scanned = sscanf (buf,
354                                     "%*[ \n\t]START DATE/TIME%*[ .:\n\t]%2d/%2d/%2d",
355                                     &iseries->month, &iseries->day,
356                                     &iseries->year);
357         if (num_items_scanned == 3)
358           {
359             iseries->have_date = TRUE;
360           }
361     }
362   *err = 0;
363   return TRUE;
364 }
365
366 /*
367  * Find the next packet and parse it; called from wtap_read().
368  */
369 static gboolean
370 iseries_read (wtap * wth, int *err, gchar ** err_info, gint64 *data_offset)
371 {
372   gint64 offset;
373
374   /*
375    * Locate the next packet
376    */
377   offset = iseries_seek_next_packet (wth, err, err_info);
378   if (offset < 0)
379     return FALSE;
380   *data_offset     = offset;
381
382   /*
383    * Parse the packet and extract the various fields
384    */
385   return iseries_parse_packet (wth, wth->fh, &wth->phdr, wth->frame_buffer,
386                                err, err_info);
387 }
388
389 /*
390  * Seeks to the beginning of the next packet, and returns the
391  * byte offset.  Returns -1 on failure or EOF; on EOF, sets
392  * *err to 0, and, on failure, sets *err to the error and *err_info
393  * to null or an additional error string.
394  */
395 static gint64
396 iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info)
397 {
398   iseries_t *iseries = (iseries_t *)wth->priv;
399   char       buf[ISERIES_LINE_LENGTH],type[5];
400   int        line, num_items_scanned;
401   gint64     cur_off;
402   long       buflen;
403
404   for (line = 0; line < ISERIES_MAX_TRACE_LEN; line++)
405     {
406       if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
407         {
408           /* EOF or error. */
409           *err = file_error (wth->fh, err_info);
410           return -1;
411         }
412         /* Convert UNICODE to ASCII if required and determine    */
413         /* the number of bytes to rewind to beginning of record. */
414         if (iseries->format == ISERIES_FORMAT_UNICODE)
415           {
416             /* buflen is #bytes to 1st 0x0A */
417             buflen = iseries_UNICODE_to_ASCII ((guint8 *) buf, ISERIES_LINE_LENGTH);
418           }
419         else
420           {
421             /* Else buflen is just length of the ASCII string */
422             buflen = (long) strlen (buf);
423           }
424         ascii_strup_inplace (buf);
425         /* If packet header found return the offset */
426         num_items_scanned =
427           sscanf (buf+78,
428                   "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
429         if (num_items_scanned == 1)
430           {
431             /* Rewind to beginning of line */
432             cur_off = file_tell (wth->fh);
433             if (cur_off == -1)
434               {
435                 *err = file_error (wth->fh, err_info);
436                 return -1;
437               }
438             if (file_seek (wth->fh, cur_off - buflen, SEEK_SET, err) == -1)
439               {
440                 return -1;
441               }
442             return cur_off - buflen;
443           }
444     }
445
446   *err = WTAP_ERR_BAD_FILE;
447   *err_info =
448     g_strdup_printf ("iseries: next packet header not found within %d lines",
449              ISERIES_MAX_TRACE_LEN);
450   return -1;
451 }
452
453 /*
454  * Read packets in random-access fashion
455  */
456 static gboolean
457 iseries_seek_read (wtap * wth, gint64 seek_off, struct wtap_pkthdr *phdr,
458                    Buffer * buf, int *err, gchar ** err_info)
459 {
460
461   /* seek to packet location */
462   if (file_seek (wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
463     return FALSE;
464
465   /*
466    * Parse the packet and extract the various fields
467    */
468   return iseries_parse_packet (wth, wth->random_fh, phdr, buf,
469                                err, err_info);
470 }
471
472 static int
473 append_hex_digits(char *ascii_buf, int ascii_offset, int max_offset,
474                   char *data, int *err, gchar **err_info)
475 {
476   int in_offset, out_offset;
477   int c;
478   unsigned int i;
479   gboolean overflow = FALSE;
480
481   in_offset = 0;
482   out_offset = ascii_offset;
483   for (;;)
484     {
485       /*
486        * Process a block of up to 16 hex digits.
487        * The block is terminated early by an end-of-line indication (NUL,
488        * CR, or LF), by a space (which terminates the last block of the
489        * data we're processing), or by a "*", which introduces the ASCII representation
490        * of the data.
491        * All characters in the block must be upper-case hex digits;
492        * there might or might not be a space *after* a block, but, if so,
493        * that will be skipped over after the block is processed.
494        */
495       for (i = 0; i < 16; i++, in_offset++)
496         {
497           /*
498            * If we see an end-of-line indication, or an early-end-of-block
499            * indication (space), we're done.  (Only the last block ends
500            * early.)
501            */
502           c = data[in_offset] & 0xFF;
503           if (c == '\0' || c == ' ' || c == '*' || c == '\r' || c == '\n')
504             {
505               goto done;
506             }
507           if (!isxdigit(c) || islower(c))
508             {
509               /*
510                * Not a hex digit, or a lower-case hex digit.
511                * Treat this as an indication that the line isn't a data
512                * line, so we just ignore it.
513                *
514                * XXX - do so only for continuation lines; treat non-hex-digit
515                * characters as errors for other lines?
516                */
517               return ascii_offset; /* pretend we appended nothing */
518             }
519           if (out_offset >= max_offset)
520             overflow = TRUE;
521           else
522             {
523               ascii_buf[out_offset] = c;
524               out_offset++;
525             }
526         }
527       /*
528        * Skip blanks, if any.
529        */
530       for (; (data[in_offset] & 0xFF) == ' '; in_offset++)
531         ;
532     }
533 done:
534   /*
535    * If we processed an *odd* number of hex digits, report an error.
536    */
537   if ((i % 2) != 0)
538     {
539       *err = WTAP_ERR_BAD_FILE;
540       *err_info = g_strdup("iseries: odd number of hex digits in a line");
541       return -1;
542     }
543   if (overflow)
544     {
545       *err = WTAP_ERR_BAD_FILE;
546       *err_info = g_strdup("iseries: more packet data than the packet length indicated");
547       return -1;
548     }
549   return out_offset;
550 }
551
552 /* Parses a packet. */
553 static gboolean
554 iseries_parse_packet (wtap * wth, FILE_T fh, struct wtap_pkthdr *phdr,
555                       Buffer *buf, int *err, gchar **err_info)
556 {
557   iseries_t *iseries = (iseries_t *)wth->priv;
558   gint64     cur_off;
559   gboolean   isValid, isCurrentPacket;
560   int        num_items_scanned, line, pktline, buflen;
561   int        pkt_len, pktnum, hr, min, sec;
562   char       direction[2], destmac[13], srcmac[13], type[5], csec[9+1];
563   char       data[ISERIES_LINE_LENGTH * 2];
564   int        offset;
565   char      *ascii_buf;
566   int        ascii_offset;
567   struct tm  tm;
568
569   /*
570    * Check for packet headers in first 3 lines this should handle page breaks
571    * situations and the header lines output at each page throw and ensure we
572    * read both the captured and packet lengths.
573    */
574   isValid = FALSE;
575   for (line = 1; line < ISERIES_PKT_LINES_TO_CHECK; line++)
576     {
577       if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
578         {
579           *err = file_error (fh, err_info);
580           return FALSE;
581         }
582       /* Convert UNICODE data to ASCII */
583       if (iseries->format == ISERIES_FORMAT_UNICODE)
584         {
585          iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
586         }
587       ascii_strup_inplace (data);
588       num_items_scanned =
589         sscanf (data,
590                 "%*[ \n\t]%6d%*[ *\n\t]%1s%*[ \n\t]%6d%*[ \n\t]%2d:%2d:%2d.%9[0-9]%*[ \n\t]"
591                 "%12s%*[ \n\t]%12s%*[ \n\t]ETHV2%*[ \n\t]TYPE:%*[ \n\t]%4s",
592                 &pktnum, direction, &pkt_len, &hr, &min, &sec, csec, destmac,
593                 srcmac, type);
594       if (num_items_scanned == 10)
595         {
596           /* OK! We found the packet header line */
597           isValid = TRUE;
598           /*
599            * XXX - The Capture length returned by the iSeries trace doesn't
600            * seem to include the Ethernet header, so we add its length here.
601            */
602           pkt_len += 14;
603           break;
604         }
605     }
606
607   /*
608    * If no packet header found we exit at this point and inform the user.
609    */
610   if (!isValid)
611     {
612       *err = WTAP_ERR_BAD_FILE;
613       *err_info = g_strdup ("iseries: packet header isn't valid");
614       return FALSE;
615     }
616
617   phdr->rec_type = REC_TYPE_PACKET;
618   phdr->presence_flags = WTAP_HAS_CAP_LEN;
619
620   /*
621    * If we have Wiretap Header then populate it here
622    *
623    * Timer resolution on the iSeries is hardware dependent.  We determine
624    * the resolution based on how many digits we see.
625    */
626   if (iseries->have_date)
627     {
628       phdr->presence_flags |= WTAP_HAS_TS;
629       tm.tm_year        = 100 + iseries->year;
630       tm.tm_mon         = iseries->month - 1;
631       tm.tm_mday        = iseries->day;
632       tm.tm_hour        = hr;
633       tm.tm_min         = min;
634       tm.tm_sec         = sec;
635       tm.tm_isdst       = -1;
636       phdr->ts.secs = mktime (&tm);
637       csec[sizeof(csec) - 1] = '\0';
638       switch (strlen(csec))
639         {
640           case 0:
641             phdr->ts.nsecs = 0;
642             break;
643           case 1:
644             phdr->ts.nsecs = atoi(csec) * 100000000;
645             break;
646           case 2:
647             phdr->ts.nsecs = atoi(csec) * 10000000;
648             break;
649           case 3:
650             phdr->ts.nsecs = atoi(csec) * 1000000;
651             break;
652           case 4:
653             phdr->ts.nsecs = atoi(csec) * 100000;
654             break;
655           case 5:
656             phdr->ts.nsecs = atoi(csec) * 10000;
657             break;
658           case 6:
659             phdr->ts.nsecs = atoi(csec) * 1000;
660             break;
661           case 7:
662             phdr->ts.nsecs = atoi(csec) * 100;
663             break;
664           case 8:
665             phdr->ts.nsecs = atoi(csec) * 10;
666             break;
667           case 9:
668             phdr->ts.nsecs = atoi(csec);
669             break;
670         }
671     }
672
673   phdr->len                       = pkt_len;
674   phdr->pkt_encap                 = WTAP_ENCAP_ETHERNET;
675   phdr->pseudo_header.eth.fcs_len = -1;
676
677   ascii_buf = (char *)g_malloc (ISERIES_PKT_ALLOC_SIZE);
678   g_snprintf(ascii_buf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s", destmac, srcmac, type);
679   ascii_offset = 14*2; /* 14-byte Ethernet header, 2 characters per byte */
680
681   /*
682    * Start reading packet contents
683    */
684   isCurrentPacket = TRUE;
685
686   /* loop through packet lines and breakout when the next packet header is read */
687   pktline = 0;
688   while (isCurrentPacket)
689     {
690       pktline++;
691       /* Read the next line */
692       if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
693         {
694           *err = file_error (fh, err_info);
695           if (*err == 0)
696             {
697               /* Hit the EOF without an error */
698               break;
699             }
700           goto errxit;
701         }
702
703       /* Convert UNICODE data to ASCII and determine line length */
704       if (iseries->format == ISERIES_FORMAT_UNICODE)
705         {
706          buflen = iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
707         }
708       else
709         {
710           /* Else bytes to rewind is just length of ASCII string */
711           buflen = (int) strlen (data);
712         }
713
714       /*
715        * Skip leading white space.
716        */
717       for (offset = 0; isspace((guchar)data[offset]); offset++)
718         ;
719
720       /*
721        * The higher-level header information starts at an offset of
722        * 22 characters.  The header tags are 14 characters long.
723        *
724        * XXX - for IPv6, if the next header isn't the last header,
725        * the intermediate headers do *NOT* appear to be shown in
726        * the dump file *at all*, so the packet *cannot* be
727        * reconstructed!
728        */
729       if (offset == 22)
730         {
731           if (strncmp(data + 22, "IP Header  :  ", 14) == 0 ||
732               strncmp(data + 22, "IPv6 Header:  ", 14) == 0 ||
733               strncmp(data + 22, "ARP Header :  ", 14) == 0 ||
734               strncmp(data + 22, "TCP Header :  ", 14) == 0 ||
735               strncmp(data + 22, "UDP Header :  ", 14) == 0 ||
736               strncmp(data + 22, "ICMP Header:  ", 14) == 0 ||
737               strncmp(data + 22, "ICMPv6  Hdr:  ", 14) == 0 ||
738               strncmp(data + 22, "Option  Hdr:  ", 14) == 0)
739             {
740               ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
741                                                ISERIES_PKT_ALLOC_SIZE - 1,
742                                                data + 22 + 14, err,
743                                                err_info);
744               if (ascii_offset == -1)
745                 {
746                   /* Bad line. */
747                   return FALSE;
748                 }
749               continue;
750             }
751         }
752
753       /*
754        * Is this a data line?
755        *
756        * The "Data" starts at an offset of 8.
757        */
758       if (offset == 9)
759         {
760           if (strncmp(data + 9, "Data . . . . . :  ", 18) == 0)
761             {
762               ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
763                                                ISERIES_PKT_ALLOC_SIZE - 1,
764                                                data + 9 + 18, err,
765                                                err_info);
766               if (ascii_offset == -1)
767                 {
768                   /* Bad line. */
769                   return FALSE;
770                 }
771               continue;
772             }
773         }
774
775       /*
776        * Is this a continuation of a previous header or data line?
777        * That's blanks followed by hex digits; first try the
778        * "no column separators" form.
779        *
780        * Continuations of header lines begin at an offset of 36;
781        * continuations of data lines begin at an offset of 27.
782        */
783       if (offset == 36 || offset == 27)
784         {
785           ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
786                                            ISERIES_PKT_ALLOC_SIZE - 1,
787                                            data + offset, err,
788                                            err_info);
789           if (ascii_offset == -1)
790             {
791               /* Bad line. */
792               return FALSE;
793             }
794           continue;
795         }
796
797       /*
798        * If we see the identifier for the next packet then rewind and set
799        * isCurrentPacket FALSE
800        */
801       ascii_strup_inplace (data);
802       /* If packet header found return the offset */
803       num_items_scanned =
804           sscanf (data+78,
805           "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
806       if ((num_items_scanned == 1) && pktline > 1)
807         {
808           isCurrentPacket = FALSE;
809           cur_off = file_tell( fh);
810           if (cur_off == -1)
811             {
812               /* Error. */
813               *err = file_error (fh, err_info);
814               goto errxit;
815             }
816           if (file_seek (fh, cur_off - buflen, SEEK_SET, err) == -1)
817             {
818               /* XXX: need to set err_info ?? */
819               goto errxit;
820             }
821         }
822     }
823   ascii_buf[ascii_offset] = '\0';
824
825   /*
826    * Make the captured length be the amount of bytes we've read (which
827    * is half the number of characters of hex dump we have).
828    *
829    * XXX - this can happen for IPv6 packets if the next header isn't the
830    * last header.
831    */
832   phdr->caplen = ((guint32) strlen (ascii_buf))/2;
833
834   /* Make sure we have enough room for the packet. */
835   ws_buffer_assure_space (buf, ISERIES_MAX_PACKET_LEN);
836   /* Convert ascii data to binary and return in the frame buffer */
837   iseries_parse_hex_string (ascii_buf, ws_buffer_start_ptr (buf), strlen (ascii_buf));
838
839   /* free buffer allocs and return */
840   *err = 0;
841   g_free (ascii_buf);
842   return TRUE;
843
844 errxit:
845   g_free (ascii_buf);
846   return FALSE;
847 }
848
849 /*
850  * Simple routine to convert an UNICODE buffer to ASCII
851  *
852  * XXX - This may be possible with iconv or similar
853  */
854 static int
855 iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes)
856 {
857   guint   i;
858   guint8 *bufptr;
859
860   bufptr = buf;
861
862   for (i = 0; i < bytes; i++)
863     {
864       switch (buf[i])
865         {
866           case 0xFE:
867           case 0xFF:
868           case 0x00:
869             break;
870           default:
871             *bufptr = buf[i];
872             bufptr++;
873         }
874       if (buf[i] == 0x0A)
875         return i;
876     }
877   return i;
878 }
879
880 /*
881  * Simple routine to convert an ASCII hex string to binary data
882  * Requires ASCII hex data and buffer to populate with binary data
883  */
884 static gboolean
885 iseries_parse_hex_string (const char * ascii, guint8 * buf, size_t len)
886 {
887   size_t i;
888   int byte;
889   gint   hexvalue;
890   guint8 bytevalue;
891
892   byte = 0;
893   for (i = 0; i < len; i++)
894     {
895       hexvalue = g_ascii_xdigit_value(ascii[i]);
896       i++;
897       if (hexvalue == -1)
898         return FALSE;        /* not a valid hex digit */
899       bytevalue = (guint8)(hexvalue << 4);
900       if (i >= len)
901         return FALSE;        /* only one hex digit of the byte is present */
902       hexvalue = g_ascii_xdigit_value(ascii[i]);
903       if (hexvalue == -1)
904         return FALSE;        /* not a valid hex digit */
905       bytevalue |= (guint8) hexvalue;
906       buf[byte] = bytevalue;
907       byte++;
908     }
909   return TRUE;
910 }
911
912 /*
913  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
914  *
915  * Local variables:
916  * c-basic-offset: 2
917  * tab-width: 8
918  * indent-tabs-mode: nil
919  * End:
920  *
921  * vi: set shiftwidth=2 tabstop=8 expandtab:
922  * :indentSize=2:tabSize=8:noTabs=true:
923  */