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