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