From Jakub Zawadzki:
[obnox/wireshark/wip.git] / wiretap / iseries.c
1 /* iseries.c
2  *
3  * $Id$
4  *
5  * Wiretap Library
6  * Copyright (c) 2005 by Martin Warnes <Martin_Warnes@Stercomm.com>
7  *
8  * Based on toshiba.c and vms.c
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 /*
26  * This module will read the contents of the iSeries (OS/400) Communication trace
27  * Both ASCII & Unicode formatted traces are supported.
28  *
29  * iSeries Comms traces consist of a header page and a subsequent number of packet records
30  *
31  * The header page contains details on the options set during running of the trace,
32  * currently the following options are a requirement for this module:
33  *
34  * 1. Object protocol = ETHERNET (Default)
35  * 2. ASCII or UNICODE file formats.
36  *
37  * The above can be acheived by passing option ASCII(*YES) with the trace command
38  *
39  */
40
41 /* iSeries header page
42
43  COMMUNICATIONS TRACE       Title: OS400 - OS400 trace               10/28/05  11:44:50                           Page:       1
44    Trace Description  . . . . . :   OS400 - OS400 trace
45    Configuration object . . . . :   ETH0
46    Type . . . . . . . . . . . . :   1            1=Line, 2=Network Interface
47                                                  3=Network server
48    Object protocol  . . . . . . :   ETHERNET
49    Start date/Time  . . . . . . :   10/28/05  11:43:00.341
50    End date/Time  . . . . . . . :   10/28/05  11:44:22.148
51    Bytes collected  . . . . . . :   11999
52    Buffer size  . . . . . . . . :   2048         kilobytes
53    Data direction . . . . . . . :   3            1=Sent, 2=Received, 3=Both
54    Stop on buffer full  . . . . :   Y            Y=Yes, N=No
55    Number of bytes to trace
56      Beginning bytes  . . . . . :   *MAX         Value, *CALC, *MAX
57      Ending bytes   . . . . . . :   *CALC        Value, *CALC
58    Controller name  . . . . . . :   *ALL         *ALL, name
59    Data representation  . . . . :   1            1=ASCII, 2=EBCDIC, 3=*CALC
60    Format SNA data only . . . . :   N            Y=Yes, N=No
61    Format RR, RNR commands  . . :   N            Y=Yes, N=No
62    Format TCP/IP data only  . . :   Y            Y=Yes, N=No
63      IP address . . . . . . . . :   *ALL             *ALL, address
64      IP address . . . . . . . . :   *ALL             *ALL, address
65      IP port  . . . . . . . . . :   *ALL             *ALL, IP port
66    Format UI data only  . . . . :   N            Y=Yes, N=No
67    Select Ethernet data . . . . :   3            1=802.3, 2=ETHV2, 3=Both
68    Format Broadcast data  . . . :   Y            Y=Yes, N=No
69 */
70
71 /* iSeries formatted packet records consist of a header line identifying the packet number,direction,size,
72  * timestamp,source/destination MAC addresses and packet type.
73  *
74  * Thereafter there will be a formated display of the IP and TCP headers as well as a hex string dump
75  * of the headers themselves displayed in the the "IP Header" and "TCP header" fields.
76  *
77  * If the packet contains data this is displayed as 4 groups of 16 hex digits followed by an ASCII
78  * representaion of the data line.
79  *
80  * Information from the header line, IP header, TCP header and if available data lines are extracted
81  * by the module for displaying.
82  *
83  *
84  Record       Data    Record           Controller  Destination   Source        Frame
85  Number  S/R  Length  Timer            Name        MAC Address   MAC Address   Format
86  ------  ---  ------  ---------------  ----------  ------------  ------------  ------
87       8   S      145  11:43:59.82956               0006299C14AE  0006299C14FE   ETHV2   Type: 0800
88                       Frame Type :  IP          DSCP: 0   ECN: 00-NECT  Length:   145   Protocol: TCP         Datagram ID: 388B
89                                     Src Addr: 10.20.144.150       Dest Addr: 10.20.144.151       Fragment Flags: DON'T,LAST
90                       IP Header  :  45000091388B40004006CC860A1490960A149097
91                       IP Options :  NONE
92                       TCP  . . . :  Src Port:  6006,Unassigned    Dest Port: 35366,Unassigned
93                                     SEQ Number:  2666470699 ('9EEF1D2B'X)  ACK Number: 2142147535 ('7FAE93CF'X)
94                                     Code Bits: ACK PSH                  Window: 32648  TCP Option: NO OP
95                       TCP Header :  17768A269EEF1D2B7FAE93CF80187F885B5600000101080A0517E0F805166DE0
96          Data . . . . . :  5443503200020010 0000004980000000  B800000080470103 01001E0000002000   *TCP2.......I*...*...*G........ .*
97                            002F010080000004 0300800700C00600  4002008000000304 00800000060FB067   *./..*.....*..*..@..*.....*....*G*
98                            FC276228786B3EB0 EF34F5F1D27EF8DF  20926820E7B322AA 739F1FB20D         **'B(XK>**4***.** *H **"*S*.*.   *
99 */
100
101 /* iSeries unformatted packet record consist of the same header record as the formatted trace but all
102  * other records are simply unformatted data containing IP, TCP and packet data combined.
103  *
104  Record       Data    Record           Controller  Destination   Source        Frame            Number  Number    Poll/
105  Number  S/R  Length  Timer            Name        MAC Address   MAC Address   Format  Command  Sent    Received  Final  DSAP  SSAP
106  ------  ---  ------  ---------------  ----------  ------------  ------------  ------  -------  ------  --------  -----  ----  ----
107       1   R       64  12:19:29.97108               000629ECF48E  0006D78E23C2   ETHV2   Type: 0800
108          Data . . . . . :  4500003C27954000 3A06CE3D9797440F  0A5964EAC4F50554 58C9915500000000   *E..<'*@.:.*=**D..YD***.TX**U....*
109                            A00216D06A200000 020405B40402080A  1104B6C000000000 010303000B443BF1   **..*J .....*......**.........D;**
110 */
111
112 #ifdef HAVE_CONFIG_H
113 #include "config.h"
114 #endif
115 #include "wtap-int.h"
116 #include "buffer.h"
117 #include "iseries.h"
118 #include "file_wrappers.h"
119
120 #include <stdio.h>
121 #include <stdlib.h>
122 #include <string.h>
123 #include <ctype.h>
124 #include <errno.h>
125
126 #include <wsutil/str_util.h>
127
128 #define ISERIES_HDR_MAGIC_STR   " COMMUNICATIONS TRACE"
129 #define ISERIES_HDR_MAGIC_LEN   21
130 #define ISERIES_PKT_MAGIC_STR   "ETHV2"
131 #define ISERIES_PKT_MAGIC_LEN   5
132 #define ISERIES_LINE_LENGTH     270
133 #define ISERIES_HDR_LINES_TO_CHECK  50
134 #define ISERIES_PKT_LINES_TO_CHECK  4
135 #define ISERIES_MAX_PACKET_LEN  16384
136 #define ISERIES_MAX_TRACE_LEN   99999999
137 #define ISERIES_PKT_ALLOC_SIZE (cap_len*2)+1
138 #define ISERIES_FORMAT_ASCII    1
139 #define ISERIES_FORMAT_UNICODE  2
140
141 typedef struct {
142   gboolean have_date;     /* TRUE if we found a capture start date */
143   int year, month, day;   /* The start date */
144   gboolean tcp_formatted; /* TCP/IP data formated Y/N */
145   int format;             /* Trace format type        */
146 } iseries_t;
147
148 static gboolean iseries_read (wtap * wth, int *err, gchar ** err_info,
149                               gint64 *data_offset);
150 static gboolean iseries_seek_read (wtap * wth, gint64 seek_off,
151                                    union wtap_pseudo_header *pseudo_header,
152                                    guint8 * pd, int len, int *err,
153                                    gchar ** err_info);
154 static gboolean iseries_check_file_type (wtap * wth, int *err, int format);
155 static gint64 iseries_seek_next_packet (wtap * wth, int *err);
156 static int iseries_parse_packet (wtap * wth, FILE_T fh,
157                                  union wtap_pseudo_header *pseudo_header,
158                                  guint8 * pd, int *err, gchar ** err_info);
159 static int iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes);
160 static gboolean iseries_parse_hex_string (const char * ascii, guint8 * buf,
161                                           int len);
162
163 int
164 iseries_open (wtap * wth, int *err, gchar ** err_info _U_)
165 {
166   int bytes_read;
167   char magic[ISERIES_HDR_MAGIC_LEN];
168   /* UNICODE identification */
169   char unicodemagic[ISERIES_HDR_MAGIC_LEN] =
170     { '\xFF', '\xFE', '\x20', '\x00', '\x43', '\x00', '\x4F', '\x00', '\x4D',
171     '\x00', '\x4D', '\x00', '\x55', '\x00', '\x4E', '\x00', '\x49', '\x00',
172     '\x43', '\x00', '\x41'
173   };
174
175   /*
176    * Check that file starts with a valid iSeries COMMS TRACE header
177    */
178   errno = WTAP_ERR_CANT_READ;
179   bytes_read = file_read (&magic, sizeof magic, wth->fh);
180   if (bytes_read != sizeof magic)
181     {
182       *err = file_error (wth->fh);
183       if (*err != 0)
184         return -1;
185       return 0;
186     }
187
188   /* Check if this is an ASCII formatted file */
189   if (memcmp (magic, ISERIES_HDR_MAGIC_STR, ISERIES_HDR_MAGIC_LEN) == 0)
190     {
191       if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
192         {
193           return 0;
194         }
195       /*
196        * Do some basic sanity checking to ensure we can handle the
197        * contents of this trace
198        */
199       if (!iseries_check_file_type (wth, err, ISERIES_FORMAT_ASCII))
200         {
201           if (*err == 0)
202             return 0;
203           else
204             return -1;
205         }
206       wth->data_offset = 0;
207       wth->file_encap = WTAP_ENCAP_ETHERNET;
208       wth->file_type = WTAP_FILE_ISERIES;
209       wth->snapshot_length = 0;
210       wth->subtype_read = iseries_read;
211       wth->subtype_seek_read = iseries_seek_read;
212       wth->tsprecision = WTAP_FILE_TSPREC_USEC;
213       if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
214         {
215           return 0;
216         }
217       return 1;
218     }
219
220   /* Check if this is a UNICODE formatted file */
221   if (memcmp (magic, unicodemagic, ISERIES_HDR_MAGIC_LEN) == 0)
222     {
223       if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
224         {
225           return 0;
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, ISERIES_FORMAT_UNICODE))
232         {
233           if (*err == 0)
234             return 0;
235           else
236             return -1;
237         }
238       wth->data_offset = 0;
239       wth->file_encap = WTAP_ENCAP_ETHERNET;
240       wth->file_type = WTAP_FILE_ISERIES_UNICODE;
241       wth->snapshot_length = 0;
242       wth->subtype_read = iseries_read;
243       wth->subtype_seek_read = iseries_seek_read;
244       wth->tsprecision = WTAP_FILE_TSPREC_USEC;
245       if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
246         {
247           return 0;
248         }
249       return 1;
250     }
251
252   /* Neither ASCII or UNICODE so not supported */
253   return 0;
254 }
255
256 /*
257  * Do some basic sanity checking to ensure we can handle the
258  * contents of this trace by checking the header page for
259  * requisit requirements and additional information.
260  */
261 static gboolean
262 iseries_check_file_type (wtap * wth, int *err, int format)
263 {
264   guint line;
265   int num_items_scanned;
266   char buf[ISERIES_LINE_LENGTH], protocol[9], tcpformat[2] = "";
267   iseries_t *iseries;
268
269   /* Save trace format for passing between packets */
270   iseries = (iseries_t *) g_malloc (sizeof (iseries_t));
271   wth->priv = (void *)iseries;
272   iseries->have_date = FALSE;
273   iseries->format = format;
274   iseries->tcp_formatted = FALSE;
275
276   for (line = 0; line < ISERIES_HDR_LINES_TO_CHECK; line++)
277     {
278       if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) != NULL)
279         {
280           /*
281            * Check that we are dealing with an ETHERNET trace
282            */
283           if (iseries->format == ISERIES_FORMAT_UNICODE)
284             {
285              iseries_UNICODE_to_ASCII ((guint8 *)buf, ISERIES_LINE_LENGTH);
286             }
287           ascii_strup_inplace(buf);
288           num_items_scanned = sscanf (buf,
289                                       "   OBJECT PROTOCOL  . . . . . . :  %8s",
290                                       protocol);
291           if (num_items_scanned == 1)
292             {
293               if (memcmp (protocol, "ETHERNET", 8) != 0)
294                 return FALSE;
295             }
296
297           /*
298            * Determine if the data has been formatted or not
299            */
300           num_items_scanned = sscanf (buf,
301                                       "   FORMAT TCP/IP DATA ONLY  . . :  %1s",
302                                       tcpformat);
303           if (num_items_scanned == 1)
304             {
305               if (strncmp (tcpformat, "Y", 1) == 0)
306                 {
307                   iseries->tcp_formatted = TRUE;
308                 }
309               else
310                 {
311                   iseries->tcp_formatted = FALSE;
312                 }
313             }
314
315           /*
316            * The header is the only place where the date part of the timestamp is held, so
317            * extract it here and store for all packets to access
318            */
319           num_items_scanned = sscanf (buf,
320                                       "   START DATE/TIME  . . . . . . :  %d/%d/%d",
321                                       &iseries->month, &iseries->day,
322                                       &iseries->year);
323           if (num_items_scanned == 3)
324             {
325               iseries->have_date = TRUE;
326             }
327
328         }
329       else
330         {
331           /* EOF or error. */
332           if (file_eof (wth->fh))
333             *err = 0;
334           else
335             *err = file_error (wth->fh);
336           return FALSE;
337         }
338     }
339   *err = 0;
340   return TRUE;
341 }
342
343 /*
344  * Find the next packet and parse it; called from wtap_read().
345  */
346 static gboolean
347 iseries_read (wtap * wth, int *err, gchar ** err_info, gint64 *data_offset)
348 {
349   gint64 offset;
350   int pkt_len;
351
352   /*
353    * Locate the next packet
354    */
355   offset = iseries_seek_next_packet (wth, err);
356   if (offset < 1)
357     return FALSE;
358
359   /*
360    * Parse the packet and extract the various fields
361    */
362   pkt_len =
363     iseries_parse_packet (wth, wth->fh, &wth->pseudo_header, NULL, err,
364                           err_info);
365   if (pkt_len == -1)
366     return FALSE;
367
368   wth->data_offset = offset;
369   *data_offset = offset;
370   return TRUE;
371 }
372
373 /*
374  * Seeks to the beginning of the next packet, and returns the
375  * byte offset.  Returns -1 on failure, and sets "*err" to the error.
376  */
377 static gint64
378 iseries_seek_next_packet (wtap * wth, int *err)
379 {
380   iseries_t *iseries = (iseries_t *)wth->priv;
381   char buf[ISERIES_LINE_LENGTH];
382   int line;
383   gint64 cur_off;
384   long buflen;
385
386   /*
387    * Seeks to the beginning of the next packet, and returns the
388    * byte offset.  Returns -1 on failure, and sets "*err" to the error.
389    */
390   for (line = 0; line < ISERIES_MAX_TRACE_LEN; line++)
391     {
392       if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) != NULL)
393         {
394
395           /* Convert UNICODE to ASCII if required and determine    */
396           /* the number of bytes to rewind to beginning of record. */
397           if (iseries->format == ISERIES_FORMAT_UNICODE)
398             {
399               /* buflen is #bytes to 1st 0x0A */
400              buflen = iseries_UNICODE_to_ASCII ((guint8 *)buf, ISERIES_LINE_LENGTH);
401             }
402           else
403             {
404               /* Else buflen is just length of the ASCII string */
405               buflen = (long) strlen (buf);
406             }
407           /* If packet header found return the offset */
408           if (strncmp (buf + 80, ISERIES_PKT_MAGIC_STR, ISERIES_PKT_MAGIC_LEN)
409               == 0)
410             {
411               /* Rewind to beginning of line */
412               cur_off = file_tell (wth->fh);
413               if (cur_off == -1)
414                 {
415                   *err = file_error (wth->fh);
416                   return -1;
417                 }
418               if (file_seek (wth->fh, cur_off - buflen, SEEK_SET, err) == -1)
419                 {
420                   return -1;
421                 }
422               return cur_off - buflen;
423             }
424         }
425       /* Otherwise we got an error or reached EOF */
426       else
427         {
428           if (file_eof (wth->fh))
429             {
430               *err = 0;
431             }
432           else
433             {
434               /* We (presumably) got an error (there's no equivalent to "ferror()"
435                  in zlib, alas, so we don't have a wrapper to check for an error). */
436               *err = file_error (wth->fh);
437             }
438           return -1;
439         }
440     }
441
442   return -1;
443 }
444
445 /*
446  * Read packets in random-access fashion
447  */
448 static gboolean
449 iseries_seek_read (wtap * wth, gint64 seek_off,
450                    union wtap_pseudo_header *pseudo_header, guint8 * pd,
451                    int len, int *err, gchar ** err_info)
452 {
453   int pkt_len;
454
455   /* seek to packet location */
456   if (file_seek (wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
457     return FALSE;
458
459   /*
460    * Parse the packet and extract the various fields
461    */
462   pkt_len = iseries_parse_packet (wth, wth->random_fh, pseudo_header, pd,
463                                   err, err_info);
464
465   if (pkt_len != len)
466     {
467       if (pkt_len != -1)
468         {
469           *err = WTAP_ERR_BAD_RECORD;
470           *err_info =
471             g_strdup_printf
472             ("iseries: requested length %d doesn't match record length %d",
473              len, pkt_len);
474         }
475       return FALSE;
476     }
477   return TRUE;
478 }
479
480 /* Parses a packet. */
481 static int
482 iseries_parse_packet (wtap * wth, FILE_T fh,
483                       union wtap_pseudo_header *pseudo_header, guint8 * pd,
484                       int *err, gchar ** err_info)
485 {
486   iseries_t *iseries = (iseries_t *)wth->priv;
487   gint64 cur_off;
488   gboolean isValid, isCurrentPacket, IPread, TCPread, isDATA;
489   int num_items_scanned, line, pktline, buflen, i;
490   guint32 pkt_len;
491   int cap_len, pktnum, hr, min, sec, csec;
492   char direction[2], destmac[13], srcmac[13], type[5], ipheader[41],
493     tcpheader[81];
494   char hex1[17], hex2[17], hex3[17], hex4[17];
495   char data[ISERIES_LINE_LENGTH * 2];
496   guint8 *buf;
497   char   *tcpdatabuf, *workbuf, *asciibuf;
498   struct tm tm;
499
500   /*
501    * Check for packet headers in first 3 lines this should handle page breaks
502    * situations and the header lines output at each page throw and ensure we
503    * read both the captured and packet lengths.
504    */
505   isValid = FALSE;
506   for (line = 1; line < ISERIES_PKT_LINES_TO_CHECK; line++)
507     {
508       cur_off = file_tell (fh);
509       if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
510         {
511           *err = file_error (fh);
512           if (*err == 0)
513             {
514               *err = WTAP_ERR_SHORT_READ;
515             }
516           return -1;
517         }
518       /* Convert UNICODE data to ASCII */
519       if (iseries->format == ISERIES_FORMAT_UNICODE)
520         {
521          iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
522         }
523       /* look for packet header */
524       for (i=0; i<8; i++) {
525                   if (strncmp(data+i,"*",1) == 0)
526                           g_strlcpy(data+i," ",(ISERIES_LINE_LENGTH * 2));
527       }
528       num_items_scanned =
529         sscanf (data,
530                 "%6d   %1s   %6d  %d:%d:%d.%d               %12s  %12s  ETHV2   Type: %4s",
531                 &pktnum, direction, &cap_len, &hr, &min, &sec, &csec, destmac,
532                 srcmac, type);
533       if (num_items_scanned == 10)
534         {
535           /* OK! We found the packet header line */
536           isValid = TRUE;
537           /*
538            * XXX - The Capture length returned by the iSeries trace doesn't seem to include the src/dest MAC
539            * addresses or the packet type. So we add them here.
540            */
541           cap_len += 14;
542           break;
543         }
544     }
545
546   /*
547    * If no packet header found we exit at this point and inform the user.
548    */
549   if (!isValid)
550     {
551       *err = WTAP_ERR_BAD_RECORD;
552       *err_info = g_strdup ("iseries: packet header isn't valid");
553       return -1;
554     }
555
556   /*
557    * If we have Wiretap Header then populate it here
558    *
559    * XXX - Timer resolution on the iSeries is hardware dependant; the value for csec may be
560    * different on other platforms though all the traces I've seen seem to show resolution
561    * to Milliseconds (i.e HH:MM:SS.nnnnn) or Nanoseconds (i.e HH:MM:SS.nnnnnn)
562    */
563   if (iseries->have_date)
564     {
565       tm.tm_year = 100 + iseries->year;
566       tm.tm_mon = iseries->month - 1;
567       tm.tm_mday = iseries->day;
568       tm.tm_hour = hr;
569       tm.tm_min = min;
570       tm.tm_sec = sec;
571       tm.tm_isdst = -1;
572       wth->phdr.ts.secs = mktime (&tm);
573       /* Handle Millisecond precision for timer */
574       if (csec > 99999)
575         {
576           wth->phdr.ts.nsecs = csec * 1000;
577         }
578       /* Handle Nanosecond precision for timer */
579       else
580         {
581           wth->phdr.ts.nsecs = csec * 10000;
582         }
583     }
584
585     wth->phdr.caplen = cap_len;
586     wth->phdr.pkt_encap = WTAP_ENCAP_ETHERNET;
587     pseudo_header->eth.fcs_len = -1;
588
589   /*
590    * Start Reading packet contents
591    */
592   isCurrentPacket = TRUE;
593   IPread = FALSE;
594   TCPread = FALSE;
595   isDATA = FALSE;
596   /*
597    * Allocate 2 work buffers to handle concatentation of the hex data block
598    */
599   tcpdatabuf = g_malloc (ISERIES_PKT_ALLOC_SIZE);
600   g_snprintf (tcpdatabuf, 1, "%s", "");
601   workbuf = g_malloc (ISERIES_PKT_ALLOC_SIZE);
602   g_snprintf (workbuf, 1, "%s", "");
603   /* loop through packet lines and breakout when the next packet header is read */
604   pktline = 0;
605   while (isCurrentPacket)
606     {
607       pktline++;
608       /* Read the next line */
609       if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
610         {
611           if (file_eof (fh))
612             {
613               break;
614             }
615           else
616             {
617               *err = file_error (fh);
618               if (*err == 0)
619                 {
620                   *err = WTAP_ERR_SHORT_READ;
621                 }
622               return -1;
623             }
624         }
625
626       /* Convert UNICODE data to ASCII and determine line length */
627       if (iseries->format == ISERIES_FORMAT_UNICODE)
628         {
629          buflen = iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
630         }
631       else
632         {
633           /* Else bytes to rewind is just length of ASCII string */
634           buflen = (int) strlen (data);
635         }
636
637       /* If this is a IP header hex string then set flag */
638       num_items_scanned = sscanf (data + 22, "IP Header  : %40s", ipheader);
639       if (num_items_scanned == 1)
640         {
641           IPread = TRUE;
642         }
643
644       /* If this is TCP header hex string then set flag */
645       num_items_scanned = sscanf (data + 22, "TCP Header : %80s", tcpheader);
646       if (num_items_scanned == 1)
647         {
648           TCPread = TRUE;
649         }
650
651       /*
652        * If there is data in the packet handle it here.
653        *
654        * The data header line will have the "Data . . " identifier, subsequent lines don't
655        */
656       num_items_scanned =
657         sscanf (data + 27, "%16[A-Z0-9] %16[A-Z0-9] %16[A-Z0-9] %16[A-Z0-9]",
658                 hex1, hex2, hex3, hex4);
659       if (num_items_scanned > 0)
660         {
661           isDATA = TRUE;
662           /*
663            * Scan the data line for data blocks, depending on the number of blocks scanned
664            * add them along with current tcpdata buffer to the work buffer and then copy
665            * work buffer to tcpdata buffer to continue building up tcpdata buffer to contain
666            * a single hex string.
667            */
668           switch (num_items_scanned)
669             {
670             case 1:
671               g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s", tcpdatabuf,
672                           hex1);
673               break;
674             case 2:
675               g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s",
676                           tcpdatabuf, hex1, hex2);
677               break;
678             case 3:
679               g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s",
680                           tcpdatabuf, hex1, hex2, hex3);
681               break;
682             default:
683               g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s%s",
684                           tcpdatabuf, hex1, hex2, hex3, hex4);
685             }
686           memcpy (tcpdatabuf, workbuf, ISERIES_PKT_ALLOC_SIZE);
687         }
688
689       /*
690        * If we see the identifier for the next packet then rewind and set
691        * isCurrentPacket FALSE
692        */
693       if ((strncmp (data + 80, ISERIES_PKT_MAGIC_STR, ISERIES_PKT_MAGIC_LEN)
694            == 0) && pktline > 1)
695         {
696           isCurrentPacket = FALSE;
697           cur_off = file_tell (fh);
698           if (cur_off == -1)
699             {
700               /* Error. */
701               *err = file_error (fh);
702               return -1;
703             }
704           if (file_seek (fh, cur_off - buflen, SEEK_SET, err) == -1)
705             {
706               return -1;
707             }
708         }
709     }
710
711   /*
712    * For a formated trace ensure we have read at least the IP and TCP headers otherwise
713    * exit and pass error message to user.
714    */
715   if (iseries->tcp_formatted)
716     {
717       if (!IPread)
718         {
719           *err = WTAP_ERR_BAD_RECORD;
720           *err_info = g_strdup ("iseries: IP header isn't valid");
721           return -1;
722         }
723       if (!TCPread)
724         {
725           *err = WTAP_ERR_BAD_RECORD;
726           *err_info = g_strdup ("iseries: TCP header isn't valid");
727           return -1;
728         }
729     }
730
731   /*
732    * Create a buffer to hold all the ASCII Hex data and populate with all the
733    * extracted data.
734    */
735   asciibuf = g_malloc (ISERIES_PKT_ALLOC_SIZE);
736   if (isDATA)
737     {
738       /* packet contained data */
739       if (iseries->tcp_formatted)
740         {
741           /* build string for formatted fields */
742           g_snprintf (asciibuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s%s%s",
743                       destmac, srcmac, type, ipheader, tcpheader, tcpdatabuf);
744         }
745       else
746         {
747           /* build string for unformatted data fields */
748           g_snprintf (asciibuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s", destmac,
749                       srcmac, type, tcpdatabuf);
750         }
751     }
752   else
753     {
754       /* No data in the packet */
755       g_snprintf (asciibuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s%s", destmac,
756                   srcmac, type, ipheader, tcpheader);
757     }
758
759   /*
760    * Extract the packet length from the actual IP header; this may
761    * differ from the capture length reported by the formatted trace.
762    * Note: if the entire Ethernet packet is present, but the IP
763    * packet is less than 46 bytes long, there will be padding, and
764    * the length in the IP header won't include the padding; if
765    * the packet length is less than the captured length, set the
766    * packet length to the captured length.
767    */
768   num_items_scanned = sscanf (asciibuf + 32, "%4x", &pkt_len);
769   wth->phdr.len = pkt_len + 14;
770   if (wth->phdr.caplen > wth->phdr.len)
771     wth->phdr.len = wth->phdr.caplen;
772
773   /* Make sure we have enough room for the packet, only create buffer if none supplied */
774   if (pd == NULL)
775     {
776       buffer_assure_space (wth->frame_buffer, ISERIES_MAX_PACKET_LEN);
777       buf = buffer_start_ptr (wth->frame_buffer);
778       /* Convert ascii data to binary and return in the frame buffer */
779       iseries_parse_hex_string (asciibuf, buf, (int) strlen (asciibuf));
780     }
781   else
782     {
783       /* Convert ascii data to binary and return in the frame buffer */
784       iseries_parse_hex_string (asciibuf, pd, (int) strlen (asciibuf));
785     }
786
787   /* free buffers allocs and return */
788   *err = 0;
789   g_free (asciibuf);
790   g_free (tcpdatabuf);
791   g_free (workbuf);
792   return wth->phdr.len;
793 }
794
795 /*
796  * Simple routine to convert an UNICODE buffer to ASCII
797  *
798  * XXX - This may be possible with iconv or similar
799  */
800 static int
801 iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes)
802 {
803   guint i;
804   guint8 *bufptr;
805   bufptr = buf;
806
807   for (i = 0; i < bytes; i++)
808     {
809       switch (buf[i])
810         {
811         case 0xFE:
812         case 0xFF:
813         case 0x00:
814           break;
815         default:
816           *bufptr = buf[i];
817           bufptr++;
818         }
819       if (buf[i] == 0x0A)
820         return i;
821     }
822   return i;
823 }
824
825 /*
826  * Simple routine to convert an ASCII hex string to binary data
827  * Requires ASCII hex data and buffer to populate with binary data
828  */
829 static gboolean
830 iseries_parse_hex_string (const char * ascii, guint8 * buf, int len)
831 {
832   int i, byte;
833   gint hexvalue;
834   guint8 bytevalue;
835
836   byte = 0;
837   i = 0;
838   for (;;)
839     {
840       if (i >= len)
841         break;
842       hexvalue = g_ascii_xdigit_value(ascii[i]);
843       i++;
844       if (hexvalue == -1)
845         return FALSE;   /* not a valid hex digit */
846       bytevalue = (guint8)(hexvalue << 4);
847       if (i >= len)
848         return FALSE;   /* only one hex digit of the byte is present */
849       hexvalue = g_ascii_xdigit_value(ascii[i]);
850       i++;
851       if (hexvalue == -1)
852         return FALSE;   /* not a valid hex digit */
853       bytevalue |= (guint8) hexvalue;
854       buf[byte] = bytevalue;
855       byte++;
856     }
857   return TRUE;
858 }