iseries: rework the read routine.
[metze/wireshark/wip.git] / wiretap / iseries.c
index 48ecc2c3c2a67c5041931ba57cdc0ae95fd182d0..aec989203d98368bd6e848efcf1973389ba523fc 100644 (file)
@@ -1,9 +1,7 @@
 /* iseries.c
- *
- * $Id$
  *
  * Wiretap Library
- * Copyright (c) 2005 by Martin Warnes <Martin_Warnes@Stercomm.com>
+ * Copyright (c) 2011 by Martin Warnes <Martin_Warnes@uk.ibm.com>
  *
  * Based on toshiba.c and vms.c
  *
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 /*
  * This module will read the contents of the iSeries (OS/400) Communication trace
- * Both ASCII & Unicode formatted traces are supported.
+ * Both ASCII & Unicode (little-endian UCS-2) formatted traces are supported.
  *
  * iSeries Comms traces consist of a header page and a subsequent number of packet records
  *
@@ -32,7 +30,7 @@
  * currently the following options are a requirement for this module:
  *
  * 1. Object protocol = ETHERNET (Default)
- * 2. ASCII or UNICODE file formats.
+ * 2. ASCII or Unicode file formats.
  *
  * The above can be acheived by passing option ASCII(*YES) with the trace command
  *
    Format Broadcast data  . . . :   Y            Y=Yes, N=No
 */
 
-/* iSeries formatted packet records consist of a header line identifying the packet number,direction,size,
- * timestamp,source/destination MAC addresses and packet type.
+/* iSeries IPv4 formatted packet records consist of a packet header line
+ * identifying the packet number, direction, size, timestamp,
+ * source/destination MAC addresses and packet type.
+ *
+ * Thereafter there will be a formated display of the headers above
+ * the link layer, such as ARP, IP, TCP, UDP, and ICMP (all but
+ * ICMP have either been seen in captures or on pages such as the ones
+ * at
+ *
+ *    http://www-912.ibm.com/s_dir/SLKBase.nsf/1ac66549a21402188625680b0002037e/e05fb0515bc3449686256ce600512c37?OpenDocument
+ *
+ * and
+ *
+ *    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
  *
- * Thereafter there will be a formated display of the IP and TCP headers as well as a hex string dump
- * of the headers themselves displayed in the the "IP Header" and "TCP header" fields.
+ * so we cannot assume that "IP Header" or "TCP Header" will appear). The
+ * formatted display includes lines that show the contents of some of the
+ * fields in the header, as well as hex strings dumps of the headers
+ * themselves, with tags such as "IP Header  :", "ARP Header :",
+ * "TCP Header :", "UDP Header :", and (presumably) "ICMP Header:".
  *
- * If the packet contains data this is displayed as 4 groups of 16 hex digits followed by an ASCII
- * representaion of the data line.
+ * If the packet contains data this is displayed as 4 groups of 16 hex digits
+ * followed by an ASCII representaion of the data line.
  *
- * Information from the header line, IP header, TCP header and if available data lines are extracted
- * by the module for displaying.
+ * Information from the packet header line, higher-level headers and, if
+ * available, data lines are extracted by the module for displaying.
  *
  *
  Record       Data    Record           Controller  Destination   Source        Frame
                            FC276228786B3EB0 EF34F5F1D27EF8DF  20926820E7B322AA 739F1FB20D         **'B(XK>**4***.** *H **"*S*.*.   *
 */
 
-/* iSeries unformatted packet record consist of the same header record as the formatted trace but all
- * other records are simply unformatted data containing IP, TCP and packet data combined.
+/* iSeries IPv6 formatted traces are similar to the IPv4 version above,
+ * except that the higher-level headers have "IPv6 Header:" and
+ * "ICMPv6  Hdr:", and data data is no longer output in groups of 16 hex
+ * digits.
+ *
+
+Record       Data      Record                       Destination   Source        Frame
+Number  S/R  Length    Timer                        MAC Address   MAC Address   Format
+------  ---  ------    ------------                 ------------  ------------  ------
+   218   S     1488    15:01:14.389                 0011BC358680  00096B6BD918   ETHV2  Type: 86DD
+                      IPv6   Data:  Ver: 06                      Traffic Class: 00            Flow Label: 000000
+                                    Payload Length:  1448        Next Header: 06,TCP          Hop Limit:    64
+                                    Src Addr:   fd00:0:0:20f2::122
+                                    Dest Addr:  fd00:0:0:20a0::155
+                      IPv6 Header:  6000000005A80640FD000000000020F20000000000000122FD000000000020A0
+                                    0000000000000155
+                      TCP  . . . :  Src Port: 21246,Unassigned    Dest Port: 13601,Unassigned
+                                    SEQ Number:  2282300877 ('880925CD'X)  ACK Number: 3259003715 ('C2407343'X)
+                                    Code Bits: ACK                      Window: 65535  TCP Option: NO OP
+                      TCP Header :  52FE3521880925CDC24073438010FFFFCFBB00000101080A0E15127000237A08
+         Data . . . . . :  54435032000200140000061880000000ECBEB867F0000000004CE640E6C1D9D5       *TCP2........*...***g*....L*@*****
+                           C9D5C740E3C8C9E240C9E240E3C8C540E6C1D9D5C9D5C740C6C9C5D3C4404040       ****@****@**@***@*******@*****@@@*
+                           4040404040404040404040404040404040404040404040404040404040404040       *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
+*/
+
+/* iSeries unformatted packet record consist of the same header record as
+ * the formatted trace but all other records are simply unformatted data
+ * containing higher-level headers and packet data combined.
  *
  Record       Data    Record           Controller  Destination   Source        Frame            Number  Number    Poll/
  Number  S/R  Length  Timer            Name        MAC Address   MAC Address   Format  Command  Sent    Received  Final  DSAP  SSAP
                            A00216D06A200000 020405B40402080A  1104B6C000000000 010303000B443BF1   **..*J .....*......**.........D;**
 */
 
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 #include "wtap-int.h"
-#include "buffer.h"
 #include "iseries.h"
 #include "file_wrappers.h"
 
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <ctype.h>
 #include <errno.h>
 
-#define ISERIES_HDR_MAGIC_STR  " COMMUNICATIONS TRACE"
-#define ISERIES_HDR_MAGIC_LEN   21
-#define ISERIES_PKT_MAGIC_STR   "ETHV2"
-#define ISERIES_PKT_MAGIC_LEN   5
-#define ISERIES_LINE_LENGTH     270
-#define ISERIES_HDR_LINES_TO_CHECK  50
-#define ISERIES_PKT_LINES_TO_CHECK  4
-#define ISERIES_MAX_PACKET_LEN  16384
-#define ISERIES_MAX_TRACE_LEN   99999999
-#define ISERIES_PKT_ALLOC_SIZE (cap_len*2)+1
-#define ISERIES_FORMAT_ASCII    1
-#define ISERIES_FORMAT_UNICODE  2
+#include <wsutil/str_util.h>
+#include <wsutil/strtoi.h>
+
+#define ISERIES_LINE_LENGTH           270
+#define ISERIES_HDR_LINES_TO_CHECK    100
+#define ISERIES_PKT_LINES_TO_CHECK    4
+#define ISERIES_MAX_TRACE_LEN         99999999
+#define ISERIES_FORMAT_ASCII          1
+#define ISERIES_FORMAT_UNICODE        2
+
+/*
+ * Magic strings - "COMMUNICATIONS TRACE", in ASCII and little-endian UCS-2.
+ */
+static const char iseries_hdr_magic_ascii[] = {
+       'C', 'O', 'M', 'M',
+       'U', 'N', 'I', 'C',
+       'A', 'T', 'I', 'O',
+       'N', 'S', ' ', 'T',
+       'R', 'A', 'C', 'E'
+};
+static const char iseries_hdr_magic_le_ucs_2[] = {
+       'C', 0x0, 'O', 0x0, 'M', 0x0, 'M', 0x0,
+       'U', 0x0, 'N', 0x0, 'I', 0x0, 'C', 0x0,
+       'A', 0x0, 'T', 0x0, 'I', 0x0, 'O', 0x0,
+       'N', 0x0, 'S', 0x0, ' ', 0x0, 'T', 0x0,
+       'R', 0x0, 'A', 0x0, 'C', 0x0, 'E', 0x0
+};
+
+typedef struct {
+  gboolean have_date;           /* TRUE if we found a capture start date */
+  int      year, month, day;    /* The start date */
+  int      format;              /* Trace format type        */
+} iseries_t;
 
 static gboolean iseries_read (wtap * wth, int *err, gchar ** err_info,
-                             gint64 *data_offset);
+                              gint64 *data_offset);
 static gboolean iseries_seek_read (wtap * wth, gint64 seek_off,
-                                  union wtap_pseudo_header *pseudo_header,
-                                  guint8 * pd, int len, int *err,
-                                  gchar ** err_info);
-static gboolean iseries_check_file_type (wtap * wth, int *err, int format);
-static gint64 iseries_seek_next_packet (wtap * wth, int *err);
-static int iseries_parse_packet (wtap * wth, FILE_T fh,
-                                union wtap_pseudo_header *pseudo_header,
-                                guint8 * pd, int *err, gchar ** err_info);
+                                   struct wtap_pkthdr *phdr,
+                                   Buffer * buf, int *err, gchar ** err_info);
+static gboolean iseries_check_file_type (wtap * wth, int *err, gchar **err_info,
+                                         int format);
+static gint64 iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info);
+static gboolean iseries_parse_packet (wtap * wth, FILE_T fh,
+                                      struct wtap_pkthdr *phdr,
+                                      Buffer * buf, int *err, gchar ** err_info);
 static int iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes);
-static gboolean iseries_parse_hex_string (guint8 * ascii, guint8 * buf,
-                                         int len);
+static gboolean iseries_parse_hex_string (const char * ascii, guint8 * buf,
+                                          size_t len);
 
-int
-iseries_open (wtap * wth, int *err, gchar ** err_info _U_)
+/*
+ * XXX - it would probably be cleaner to use a UCS-2 flavor of file_gets(),
+ * rather than file_gets(), if we're reading a UCS-2 file.
+ */
+wtap_open_return_val
+iseries_open (wtap * wth, int *err, gchar ** err_info)
 {
-  int bytes_read;
-  char magic[ISERIES_HDR_MAGIC_LEN];
-  /* UNICODE identification */
-  char unicodemagic[ISERIES_HDR_MAGIC_LEN] =
-    { '\xFF', '\xFE', '\x20', '\x00', '\x43', '\x00', '\x4F', '\x00', '\x4D',
-    '\x00', '\x4D', '\x00', '\x55', '\x00', '\x4E', '\x00', '\x49', '\x00',
-    '\x43', '\x00', '\x41'
-  };
+  gint offset;
+  char magic[ISERIES_LINE_LENGTH];
 
   /*
    * Check that file starts with a valid iSeries COMMS TRACE header
+   * by scanning for it in the first line
    */
-  errno = WTAP_ERR_CANT_READ;
-  bytes_read = file_read (&magic, 1, sizeof magic, wth->fh);
-  if (bytes_read != sizeof magic)
+  if (!wtap_read_bytes (wth->fh, &magic, sizeof magic, err, err_info))
     {
-      *err = file_error (wth->fh);
-      if (*err != 0)
-       return -1;
-      return 0;
+      if (*err != WTAP_ERR_SHORT_READ)
+        return WTAP_OPEN_ERROR;
+      return WTAP_OPEN_NOT_MINE;
     }
 
-  /* Check if this is an ASCII formatted file */
-  if (memcmp (magic, ISERIES_HDR_MAGIC_STR, ISERIES_HDR_MAGIC_LEN) == 0)
+  /*
+   * Check if this is a little-endian UCS-2 Unicode formatted file by scanning
+   * for the magic string
+   */
+  offset=0;
+  while ((unsigned int)offset < (ISERIES_LINE_LENGTH - (sizeof iseries_hdr_magic_le_ucs_2)))
     {
-      if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
-       {
-         return 0;
-       }
-      /*
-       * Do some basic sanity checking to ensure we can handle the
-       * contents of this trace
-       */
-      if (!iseries_check_file_type (wth, err, ISERIES_FORMAT_ASCII))
-       {
-         if (*err == 0)
-           return 0;
-         else
-           return -1;
-       }
-      wth->data_offset = 0;
-      wth->file_encap = WTAP_ENCAP_PER_PACKET;
-      wth->file_type = WTAP_FILE_ISERIES;
-      wth->snapshot_length = 0;
-      wth->subtype_read = iseries_read;
-      wth->subtype_seek_read = iseries_seek_read;
-      wth->tsprecision = WTAP_FILE_TSPREC_USEC;
-      if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
-       {
-         return 0;
-       }
-      return 1;
-    }
+      if (memcmp (magic + offset, iseries_hdr_magic_le_ucs_2, sizeof iseries_hdr_magic_le_ucs_2) == 0) {
+        if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
+          {
+            return WTAP_OPEN_ERROR;
+          }
+        /*
+         * Do some basic sanity checking to ensure we can handle the
+         * contents of this trace
+         */
+        if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_UNICODE))
+          {
+            if (*err == 0)
+              return WTAP_OPEN_NOT_MINE;
+            else
+              return WTAP_OPEN_ERROR;
+          }
 
-  /* Check if this is a UNICODE formatted file */
-  if (memcmp (magic, unicodemagic, ISERIES_HDR_MAGIC_LEN) == 0)
-    {
-      if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
-       {
-         return 0;
-       }
-      /*
-       * Do some basic sanity checking to ensure we can handle the
-       * contents of this trace
-       */
-      if (!iseries_check_file_type (wth, err, ISERIES_FORMAT_UNICODE))
-       {
-         if (*err == 0)
-           return 0;
-         else
-           return -1;
-       }
-      wth->data_offset = 0;
-      wth->file_encap = WTAP_ENCAP_PER_PACKET;
-      wth->file_type = WTAP_FILE_ISERIES_UNICODE;
-      wth->snapshot_length = 0;
-      wth->subtype_read = iseries_read;
-      wth->subtype_seek_read = iseries_seek_read;
-      wth->tsprecision = WTAP_FILE_TSPREC_USEC;
-      if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
-       {
-         return 0;
-       }
-      return 1;
+        wth->file_encap        = WTAP_ENCAP_ETHERNET;
+        wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_ISERIES;
+        wth->snapshot_length   = 0;
+        wth->subtype_read      = iseries_read;
+        wth->subtype_seek_read = iseries_seek_read;
+        wth->file_tsprec       = WTAP_TSPREC_USEC;
+
+        if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
+          {
+            return WTAP_OPEN_ERROR;
+          }
+        return WTAP_OPEN_MINE;
+      }
+      offset += 1;
     }
 
-  /* Neither ASCII or UNICODE so not supported */
-  return 0;
-}
+    /*
+     * Check if this is a ASCII formatted file by scanning for the magic string
+     */
+    offset=0;
+    while ((unsigned int)offset < (ISERIES_LINE_LENGTH - sizeof iseries_hdr_magic_ascii))
+      {
+        if (memcmp (magic + offset, iseries_hdr_magic_ascii, sizeof iseries_hdr_magic_ascii) == 0)
+          {
+            if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
+              {
+                return WTAP_OPEN_ERROR;
+              }
+            /*
+             * Do some basic sanity checking to ensure we can handle the
+             * contents of this trace
+             */
+            if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_ASCII))
+              {
+                if (*err == 0)
+                  return WTAP_OPEN_NOT_MINE;
+                else
+                  return WTAP_OPEN_ERROR;
+              }
+
+            wth->file_encap        = WTAP_ENCAP_ETHERNET;
+            wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_ISERIES;
+            wth->snapshot_length   = 0;
+            wth->subtype_read      = iseries_read;
+            wth->subtype_seek_read = iseries_seek_read;
+            wth->file_tsprec       = WTAP_TSPREC_USEC;
+
+            if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
+              {
+                return WTAP_OPEN_ERROR;
+              }
+            return WTAP_OPEN_MINE;
+          }
+        offset += 1;
+      }
+
+    /* Neither ASCII or UNICODE so not supported */
+    return WTAP_OPEN_NOT_MINE;
+    }
 
 /*
  * Do some basic sanity checking to ensure we can handle the
@@ -250,84 +317,71 @@ iseries_open (wtap * wth, int *err, gchar ** err_info _U_)
  * requisit requirements and additional information.
  */
 static gboolean
-iseries_check_file_type (wtap * wth, int *err, int format)
+iseries_check_file_type (wtap * wth, int *err, gchar **err_info, int format)
 {
-  guint line;
-  int num_items_scanned;
-  char buf[ISERIES_LINE_LENGTH], protocol[9], tcpformat[2];
-  guint8 *sdate;
+  gboolean   is_iseries = FALSE;
+  guint      line;
+  int        num_items_scanned;
+  char       buf[ISERIES_LINE_LENGTH], protocol[9];
+  iseries_t *iseries;
 
   /* Save trace format for passing between packets */
-  sdate = g_malloc (10);
-  wth->capture.iseries = g_malloc (sizeof (iseries_t));
-  wth->capture.iseries->sdate = NULL;
-  wth->capture.iseries->format = format;
-  wth->capture.iseries->tcp_formatted = FALSE;
+  iseries                = (iseries_t *) g_malloc (sizeof (iseries_t));
+  iseries->have_date     = FALSE;
+  iseries->format        = format;
 
   for (line = 0; line < ISERIES_HDR_LINES_TO_CHECK; line++)
     {
-      if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) != NULL)
-       {
-         /*
-          * Check that we are dealing with an ETHERNET trace
-          */
-         if (wth->capture.iseries->format == ISERIES_FORMAT_UNICODE)
-           {
-             iseries_UNICODE_to_ASCII (buf, ISERIES_LINE_LENGTH);
-           }
-         g_strup(buf);
-         num_items_scanned = sscanf (buf,
-                                     "   OBJECT PROTOCOL  . . . . . . :  %8s",
-                                     protocol);
-         if (num_items_scanned == 1)
-           {
-             if (memcmp (protocol, "ETHERNET", 8) != 0)
-               return FALSE;
-           }
-
-         /*
-          * Determine if the data has been formatted or not
-          */
-         num_items_scanned = sscanf (buf,
-                                     "   FORMAT TCP/IP DATA ONLY  . . :  %1s",
-                                     tcpformat);
-         if (num_items_scanned == 1)
-           {
-             if (strncmp (tcpformat, "Y", 1) == 0)
-               {
-                 wth->capture.iseries->tcp_formatted = TRUE;
-               }
-             else
-               {
-                 wth->capture.iseries->tcp_formatted = FALSE;
-               }
-           }
-
-         /*
-          * The header is the only place where the date part of the timestamp is held, so
-          * extract it here and store for all packets to access
-          */
-         num_items_scanned = sscanf (buf,
-                                     "   START DATE/TIME  . . . . . . :  %8s",
-                                     sdate);
-         if (num_items_scanned == 1)
-           {
-             wth->capture.iseries->sdate = sdate;
-           }
-
-       }
-      else
-       {
-         /* EOF or error. */
-         if (file_eof (wth->fh))
-           *err = 0;
-         else
-           *err = file_error (wth->fh);
-         return FALSE;
-       }
+      memset(buf, 0x0, sizeof(buf));
+      if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
+        {
+          /* EOF or error. */
+          *err = file_error (wth->fh, err_info);
+          if (*err == WTAP_ERR_SHORT_READ)
+            *err = 0;
+          break;
+        }
+
+      /*
+       * Check that we are dealing with an ETHERNET trace
+       */
+      if (iseries->format == ISERIES_FORMAT_UNICODE)
+        {
+          iseries_UNICODE_to_ASCII ((guint8 *)buf, ISERIES_LINE_LENGTH);
+        }
+      ascii_strup_inplace (buf);
+      num_items_scanned = sscanf (buf,
+                                 "%*[ \n\t]OBJECT PROTOCOL%*[ .:\n\t]%8s",
+                                 protocol);
+      if (num_items_scanned == 1)
+        {
+          if (memcmp (protocol, "ETHERNET", 8) == 0)
+            {
+              *err = 0;
+              is_iseries = TRUE;
+            }
+        }
+
+      /*
+       * The header is the only place where the date part of the timestamp is held, so
+       * extract it here and store for all packets to access
+       */
+      num_items_scanned = sscanf (buf,
+                                  "%*[ \n\t]START DATE/TIME%*[ .:\n\t]%2d/%2d/%2d",
+                                  &iseries->month, &iseries->day,
+                                  &iseries->year);
+      if (num_items_scanned == 3)
+        {
+          iseries->have_date = TRUE;
+        }
     }
-  *err = 0;
-  return TRUE;
+
+  if (is_iseries)
+    wth->priv = (void *) iseries;
+  else
+    g_free(iseries);
+
+  return is_iseries;
 }
 
 /*
@@ -337,97 +391,83 @@ static gboolean
 iseries_read (wtap * wth, int *err, gchar ** err_info, gint64 *data_offset)
 {
   gint64 offset;
-  int pkt_len;
 
   /*
    * Locate the next packet
    */
-  offset = iseries_seek_next_packet (wth, err);
-  if (offset < 1)
+  offset = iseries_seek_next_packet (wth, err, err_info);
+  if (offset < 0)
     return FALSE;
+  *data_offset     = offset;
 
   /*
    * Parse the packet and extract the various fields
    */
-  pkt_len =
-    iseries_parse_packet (wth, wth->fh, &wth->pseudo_header, NULL, err,
-                         err_info);
-  if (pkt_len == -1)
-    return FALSE;
-
-  wth->data_offset = offset;
-  *data_offset = offset;
-  return TRUE;
+  return iseries_parse_packet (wth, wth->fh, &wth->phdr, wth->frame_buffer,
+                               err, err_info);
 }
 
 /*
  * Seeks to the beginning of the next packet, and returns the
- * byte offset.  Returns -1 on failure, and sets "*err" to the error.
+ * byte offset.  Returns -1 on failure or EOF; on EOF, sets
+ * *err to 0, and, on failure, sets *err to the error and *err_info
+ * to null or an additional error string.
  */
 static gint64
-iseries_seek_next_packet (wtap * wth, int *err)
+iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info)
 {
-  char buf[ISERIES_LINE_LENGTH];
-  int line;
-  gint64 cur_off;
-  long buflen;
+  iseries_t *iseries = (iseries_t *)wth->priv;
+  char       buf[ISERIES_LINE_LENGTH],type[5];
+  int        line, num_items_scanned;
+  gint64     cur_off;
+  long       buflen;
 
-  /*
-   * Seeks to the beginning of the next packet, and returns the
-   * byte offset.  Returns -1 on failure, and sets "*err" to the error.
-   */
   for (line = 0; line < ISERIES_MAX_TRACE_LEN; line++)
     {
-      if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) != NULL)
-       {
-
-         /* Convert UNICODE to ASCII if required and determine    */
-         /* the number of bytes to rewind to beginning of record. */
-         if (wth->capture.iseries->format == ISERIES_FORMAT_UNICODE)
-           {
-             /* buflen is #bytes to 1st 0x0A */
-             buflen = iseries_UNICODE_to_ASCII (buf, ISERIES_LINE_LENGTH);
-           }
-         else
-           {
-             /* Else buflen is just length of the ASCII string */
-             buflen = strlen (buf);
-           }
-         /* If packet header found return the offset */
-         if (strncmp (buf + 80, ISERIES_PKT_MAGIC_STR, ISERIES_PKT_MAGIC_LEN)
-             == 0)
-           {
-             /* Rewind to beginning of line */
-             cur_off = file_tell (wth->fh);
-             if (cur_off == -1)
-               {
-                 *err = file_error (wth->fh);
-                 return -1;
-               }
-             if (file_seek (wth->fh, cur_off - buflen, SEEK_SET, err) == -1)
-               {
-                 return -1;
-               }
-             return cur_off - buflen;
-           }
-       }
-      /* Otherwise we got an error or reached EOF */
-      else
-       {
-         if (file_eof (wth->fh))
-           {
-             *err = 0;
-           }
-         else
-           {
-             /* We (presumably) got an error (there's no equivalent to "ferror()"
-                in zlib, alas, so we don't have a wrapper to check for an error). */
-             *err = file_error (wth->fh);
-           }
-         return -1;
-       }
+      if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
+        {
+          /* EOF or error. */
+          *err = file_error (wth->fh, err_info);
+          return -1;
+        }
+        /* Convert UNICODE to ASCII if required and determine    */
+        /* the number of bytes to rewind to beginning of record. */
+        if (iseries->format == ISERIES_FORMAT_UNICODE)
+          {
+            /* buflen is #bytes to 1st 0x0A */
+            buflen = iseries_UNICODE_to_ASCII ((guint8 *) buf, ISERIES_LINE_LENGTH);
+          }
+        else
+          {
+            /* Else buflen is just length of the ASCII string */
+            buflen = (long) strlen (buf);
+          }
+        ascii_strup_inplace (buf);
+        /* If packet header found return the offset */
+        num_items_scanned =
+          sscanf (buf+78,
+                  "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
+        if (num_items_scanned == 1)
+          {
+            /* Rewind to beginning of line */
+            cur_off = file_tell (wth->fh);
+            if (cur_off == -1)
+              {
+                *err = file_error (wth->fh, err_info);
+                return -1;
+              }
+            if (file_seek (wth->fh, cur_off - buflen, SEEK_SET, err) == -1)
+              {
+                return -1;
+              }
+            return cur_off - buflen;
+          }
     }
 
+  *err = WTAP_ERR_BAD_FILE;
+  *err_info =
+    g_strdup_printf ("iseries: next packet header not found within %d lines",
+             ISERIES_MAX_TRACE_LEN);
   return -1;
 }
 
@@ -435,11 +475,9 @@ iseries_seek_next_packet (wtap * wth, int *err)
  * Read packets in random-access fashion
  */
 static gboolean
-iseries_seek_read (wtap * wth, gint64 seek_off,
-                  union wtap_pseudo_header *pseudo_header, guint8 * pd,
-                  int len, int *err, gchar ** err_info)
+iseries_seek_read (wtap * wth, gint64 seek_off, struct wtap_pkthdr *phdr,
+                   Buffer * buf, int *err, gchar ** err_info)
 {
-  int pkt_len;
 
   /* seek to packet location */
   if (file_seek (wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
@@ -448,41 +486,122 @@ iseries_seek_read (wtap * wth, gint64 seek_off,
   /*
    * Parse the packet and extract the various fields
    */
-  pkt_len = iseries_parse_packet (wth, wth->random_fh, pseudo_header, pd,
-                                 err, err_info);
+  return iseries_parse_packet (wth, wth->random_fh, phdr, buf,
+                               err, err_info);
+}
+
+static int
+append_hex_digits(char *ascii_buf, int ascii_offset, int max_offset,
+                  char *data, int *err, gchar **err_info)
+{
+  int in_offset, out_offset;
+  int c;
+  unsigned int i;
+  gboolean overflow = FALSE;
 
-  if (pkt_len != len)
+  in_offset = 0;
+  out_offset = ascii_offset;
+  for (;;)
     {
-      if (pkt_len != -1)
-       {
-         *err = WTAP_ERR_BAD_RECORD;
-         *err_info =
-           g_strdup_printf
-           ("iseries: requested length %d doesn't match record length %d",
-            len, pkt_len);
-       }
-      return FALSE;
+      /*
+       * Process a block of up to 16 hex digits.
+       * The block is terminated early by an end-of-line indication (NUL,
+       * CR, or LF), by a space (which terminates the last block of the
+       * data we're processing), or by a "*", which introduces the ASCII representation
+       * of the data.
+       * All characters in the block must be upper-case hex digits;
+       * there might or might not be a space *after* a block, but, if so,
+       * that will be skipped over after the block is processed.
+       */
+      for (i = 0; i < 16; i++, in_offset++)
+        {
+          /*
+           * If we see an end-of-line indication, or an early-end-of-block
+           * indication (space), we're done.  (Only the last block ends
+           * early.)
+           */
+          c = data[in_offset] & 0xFF;
+          if (c == '\0' || c == ' ' || c == '*' || c == '\r' || c == '\n')
+            {
+              goto done;
+            }
+          if (!g_ascii_isxdigit(c) || g_ascii_islower(c))
+            {
+              /*
+               * Not a hex digit, or a lower-case hex digit.
+               * Treat this as an indication that the line isn't a data
+               * line, so we just ignore it.
+               *
+               * XXX - do so only for continuation lines; treat non-hex-digit
+               * characters as errors for other lines?
+               */
+              return ascii_offset; /* pretend we appended nothing */
+            }
+          if (out_offset >= max_offset)
+            overflow = TRUE;
+          else
+            {
+              ascii_buf[out_offset] = c;
+              out_offset++;
+            }
+        }
+      /*
+       * Skip blanks, if any.
+       */
+      for (; (data[in_offset] & 0xFF) == ' '; in_offset++)
+        ;
     }
-  return TRUE;
+done:
+  /*
+   * If we processed an *odd* number of hex digits, report an error.
+   */
+  if ((i % 2) != 0)
+    {
+      *err = WTAP_ERR_BAD_FILE;
+      *err_info = g_strdup("iseries: odd number of hex digits in a line");
+      return -1;
+    }
+  if (overflow)
+    {
+      *err = WTAP_ERR_BAD_FILE;
+      *err_info = g_strdup("iseries: more packet data than the packet length indicated");
+      return -1;
+    }
+  return out_offset;
+}
+
+/* return the multiplier for nanoseconds */
+static guint32
+csec_multiplier(guint32 csec)
+{
+  if (csec < 10) return 100000000;
+  if (csec < 100) return 10000000;
+  if (csec < 1000) return 1000000;
+  if (csec < 10000) return 100000;
+  if (csec < 100000) return 10000;
+  if (csec < 1000000) return 1000;
+  if (csec < 10000000) return 100;
+  if (csec < 100000000) return 10;
+  return 1;
 }
 
 /* Parses a packet. */
-static int
-iseries_parse_packet (wtap * wth, FILE_T fh,
-                     union wtap_pseudo_header *pseudo_header, guint8 * pd,
-                     int *err, gchar ** err_info)
+static gboolean
+iseries_parse_packet (wtap * wth, FILE_T fh, struct wtap_pkthdr *phdr,
+                      Buffer *buf, int *err, gchar **err_info)
 {
-  gint64 cur_off;
-  gboolean isValid, isCurrentPacket, IPread, TCPread, isDATA;
-  int num_items_scanned, line, pktline, buflen;
-  guint32 pkt_len;
-  int cap_len, pktnum, month, day, year, hr, min, sec, csec;
-  char direction[2], destmac[13], srcmac[13], type[5], ipheader[41],
-    tcpheader[81];
-  char hex1[17], hex2[17], hex3[17], hex4[17];
-  char data[ISERIES_LINE_LENGTH * 2];
-  guint8 *buf, *asciibuf, *tcpdatabuf, *workbuf;
-  struct tm tm;
+  iseries_t *iseries = (iseries_t *)wth->priv;
+  gint64     cur_off;
+  gboolean   isValid, isCurrentPacket;
+  int        num_items_scanned, line, pktline, buflen;
+  int        pkt_len, pktnum, hr, min, sec;
+  char       direction[2], destmac[13], srcmac[13], type[5];
+  guint32    csec;
+  char       data[ISERIES_LINE_LENGTH * 2];
+  int        offset;
+  char      *ascii_buf;
+  int        ascii_offset;
+  struct tm  tm;
 
   /*
    * Check for packet headers in first 3 lines this should handle page breaks
@@ -492,38 +611,133 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
   isValid = FALSE;
   for (line = 1; line < ISERIES_PKT_LINES_TO_CHECK; line++)
     {
-      cur_off = file_tell (fh);
       if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
-       {
-         *err = file_error (fh);
-         if (*err == 0)
-           {
-             *err = WTAP_ERR_SHORT_READ;
-           }
-         return -1;
-       }
+        {
+          *err = file_error (fh, err_info);
+          return FALSE;
+        }
       /* Convert UNICODE data to ASCII */
-      if (wth->capture.iseries->format == ISERIES_FORMAT_UNICODE)
-       {
-         iseries_UNICODE_to_ASCII (data, ISERIES_LINE_LENGTH);
-       }
-      /* look for packet header */
+      if (iseries->format == ISERIES_FORMAT_UNICODE)
+        {
+         iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
+        }
+      ascii_strup_inplace (data);
       num_items_scanned =
-       sscanf (data,
-               "%6d   %1s   %6d  %d:%d:%d.%d               %12s  %12s  ETHV2   Type: %s",
-               &pktnum, direction, &cap_len, &hr, &min, &sec, &csec, destmac,
-               srcmac, type);
+        sscanf (data,
+                "%*[ \n\t]%6d%*[ *\n\t]%1s%*[ \n\t]%6d%*[ \n\t]%2d:%2d:%2d.%9u%*[ \n\t]"
+                "%12s%*[ \n\t]%12s%*[ \n\t]ETHV2%*[ \n\t]TYPE:%*[ \n\t]%4s",
+                &pktnum, direction, &pkt_len, &hr, &min, &sec, &csec, destmac,
+                srcmac, type);
       if (num_items_scanned == 10)
-       {
-         /* OK! We found the packet header line */
-         isValid = TRUE;
-         /*
-          * XXX - The Capture length returned by the iSeries trace doesn't seem to include the src/dest MAC
-          * addresses or the packet type. So we add them here.
-          */
-         cap_len += 14;
-         break;
-       }
+        {
+          if (pktnum < 0)
+            {
+              *err = WTAP_ERR_BAD_FILE;
+              *err_info = g_strdup ("iseries: packet header has a negative packet number");
+              return FALSE;
+            }
+
+          if (pkt_len < 0)
+            {
+              *err = WTAP_ERR_BAD_FILE;
+              *err_info = g_strdup ("iseries: packet header has a negative packet length");
+              return FALSE;
+            }
+
+          if (hr < 0)
+            {
+              *err = WTAP_ERR_BAD_FILE;
+              *err_info = g_strdup ("iseries: packet header has a negative hour in the time stamp");
+              return FALSE;
+            }
+
+          if (hr > 23)
+            {
+              *err = WTAP_ERR_BAD_FILE;
+              *err_info = g_strdup ("iseries: packet header has a hour in the time stamp greater than 23");
+              return FALSE;
+            }
+
+          if (min < 0)
+            {
+              *err = WTAP_ERR_BAD_FILE;
+              *err_info = g_strdup ("iseries: packet header has a negative minute in the time stamp");
+              return FALSE;
+            }
+
+          if (min > 59)
+            {
+              *err = WTAP_ERR_BAD_FILE;
+              *err_info = g_strdup ("iseries: packet header has a minute in the time stamp greater than 59");
+              return FALSE;
+            }
+
+          if (sec < 0)
+            {
+              *err = WTAP_ERR_BAD_FILE;
+              *err_info = g_strdup ("iseries: packet header has a negative second in the time stamp");
+              return FALSE;
+            }
+
+          /*
+           * Yes, 60, even though the time-conversion routines on most OSes
+           * might not handle leap seconds.
+           */
+          if (sec > 60)
+            {
+              *err = WTAP_ERR_BAD_FILE;
+              *err_info = g_strdup ("iseries: packet header has a second in the time stamp greater than 60");
+              return FALSE;
+            }
+
+          if (strlen(destmac) != 12)
+            {
+              *err = WTAP_ERR_BAD_FILE;
+              *err_info = g_strdup ("iseries: packet header has a destination MAC address shorter than 6 bytes");
+              return FALSE;
+            }
+
+          if (strlen(srcmac) != 12)
+            {
+              *err = WTAP_ERR_BAD_FILE;
+              *err_info = g_strdup ("iseries: packet header has a source MAC address shorter than 6 bytes");
+              return FALSE;
+            }
+
+          if (strlen(type) != 4)
+            {
+              *err = WTAP_ERR_BAD_FILE;
+              *err_info = g_strdup ("iseries: packet header has an Ethernet type/length field than 2 bytes");
+              return FALSE;
+            }
+
+          /* OK! We found the packet header line */
+          isValid = TRUE;
+          /*
+           * XXX - The Capture length returned by the iSeries trace doesn't
+           * seem to include the Ethernet header, so we add its length here.
+           *
+           * Check the length first, just in case it's *so* big that, after
+           * adding the Ethernet header length, it overflows.
+           */
+          if (pkt_len > WTAP_MAX_PACKET_SIZE - 14)
+            {
+              /*
+               * Probably a corrupt capture file; don't blow up trying
+               * to allocate space for an immensely-large packet, and
+               * don't think it's a really *small* packet because it
+               * overflowed.  (Calculate the size as a 64-bit value in
+               * the error message, to avoid an overflow.)
+               */
+              *err = WTAP_ERR_BAD_FILE;
+              *err_info = g_strdup_printf("iseries: File has %" G_GUINT64_FORMAT "-byte packet, bigger than maximum of %u",
+                                          (guint64)pkt_len + 14,
+                                          WTAP_MAX_PACKET_SIZE);
+              return FALSE;
+            }
+          pkt_len += 14;
+          break;
+        }
     }
 
   /*
@@ -531,59 +745,70 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
    */
   if (!isValid)
     {
-      *err = WTAP_ERR_BAD_RECORD;
+      *err = WTAP_ERR_BAD_FILE;
       *err_info = g_strdup ("iseries: packet header isn't valid");
-      return -1;
+      return FALSE;
     }
 
+  phdr->rec_type = REC_TYPE_PACKET;
+  phdr->presence_flags = WTAP_HAS_CAP_LEN;
+
   /*
    * If we have Wiretap Header then populate it here
    *
-   * XXX - Timer resolution on the iSeries is hardware dependant, the value for csec may be
-   * different on other platforms though all the traces I've seen seem to show resolution
-   * to Milliseconds (i.e HH:MM:SS.nnnnn) or Nanoseconds (i.e HH:MM:SS.nnnnnn)
+   * Timer resolution on the iSeries is hardware dependent.  We determine
+   * the resolution based on how many digits we see.
    */
-  if (wth->capture.iseries->sdate)
+  if (iseries->have_date)
     {
-      num_items_scanned =
-       sscanf (wth->capture.iseries->sdate, "%d/%d/%d", &month, &day, &year);
-      tm.tm_year = 100 + year;
-      tm.tm_mon = month - 1;
-      tm.tm_mday = day;
-      tm.tm_hour = hr;
-      tm.tm_min = min;
-      tm.tm_sec = sec;
-      tm.tm_isdst = -1;
-      wth->phdr.ts.secs = mktime (&tm);
-      /* Handle Millisecond precision for timer */
-      if (csec > 99999)
-       {
-         wth->phdr.ts.nsecs = csec * 1000;
-       }
-      /* Handle Nanosecond precision for timer */
-      else
-       {
-         wth->phdr.ts.nsecs = csec * 10000;
-       }
-      wth->phdr.caplen = cap_len;
-      wth->phdr.pkt_encap = WTAP_ENCAP_ETHERNET;
-      pseudo_header->eth.fcs_len = -1;
+      phdr->presence_flags |= WTAP_HAS_TS;
+      tm.tm_year        = 100 + iseries->year;
+      tm.tm_mon         = iseries->month - 1;
+      tm.tm_mday        = iseries->day;
+      tm.tm_hour        = hr;
+      tm.tm_min         = min;
+      tm.tm_sec         = sec;
+      tm.tm_isdst       = -1;
+      phdr->ts.secs = mktime (&tm);
+      phdr->ts.nsecs = csec * csec_multiplier(csec);
     }
 
+  phdr->len                       = pkt_len;
+  phdr->pkt_encap                 = WTAP_ENCAP_ETHERNET;
+  phdr->pseudo_header.eth.fcs_len = -1;
+
   /*
-   * Start Reading packet contents
+   * Allocate a buffer big enough to hold the claimed packet length
+   * worth of byte values; each byte will be two hex digits, so the
+   * buffer's size should be twice the packet length.
+   *
+   * (There is no need to null-terminate the buffer.)
    */
-  isCurrentPacket = TRUE;
-  IPread = FALSE;
-  TCPread = FALSE;
-  isDATA = FALSE;
+  ascii_buf = (char *)g_malloc (pkt_len*2);
+  ascii_offset = 0;
+
+  /*
+   * Copy in the Ethernet header.
+   *
+   * The three fields have already been checked to have the right length
+   * (6 bytes, hence 12 characters, of hex-dump destination and source
+   * addresses, and 2 bytes, hence 4 characters, of hex-dump type/length).
+   *
+   * pkt_len is guaranteed to be >= 14, so 2*pkt_len is guaranteed to be
+   * >= 28, so we don't need to do any bounds checking.
+   */
+  memcpy(&ascii_buf[0], destmac, 12);
+  ascii_offset += 12;
+  memcpy(&ascii_buf[12], srcmac, 12);
+  ascii_offset += 12;
+  memcpy(&ascii_buf[24], type, 4);
+  ascii_offset += 4;
+
   /*
-   * Allocate 2 work buffers to handle concatentation of the hex data block
+   * Start reading packet contents
    */
-  tcpdatabuf = g_malloc (ISERIES_PKT_ALLOC_SIZE);
-  g_snprintf (tcpdatabuf, 1, "%s", "");
-  workbuf = g_malloc (ISERIES_PKT_ALLOC_SIZE);
-  g_snprintf (workbuf, 1, "%s", "");
+  isCurrentPacket = TRUE;
+
   /* loop through packet lines and breakout when the next packet header is read */
   pktline = 0;
   while (isCurrentPacket)
@@ -591,189 +816,159 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
       pktline++;
       /* Read the next line */
       if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
-       {
-         if (file_eof (fh))
-           {
-             break;
-           }
-         else
-           {
-             *err = file_error (fh);
-             if (*err == 0)
-               {
-                 *err = WTAP_ERR_SHORT_READ;
-               }
-             return -1;
-           }
-       }
+        {
+          *err = file_error (fh, err_info);
+          if (*err == 0)
+            {
+              /* Hit the EOF without an error */
+              break;
+            }
+          goto errxit;
+        }
 
       /* Convert UNICODE data to ASCII and determine line length */
-      if (wth->capture.iseries->format == ISERIES_FORMAT_UNICODE)
-       {
-         buflen = iseries_UNICODE_to_ASCII (data, ISERIES_LINE_LENGTH);
-       }
+      if (iseries->format == ISERIES_FORMAT_UNICODE)
+        {
+         buflen = iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
+        }
       else
-       {
-         /* Else bytes to rewind is just length of ASCII string */
-         buflen = strlen (data);
-       }
+        {
+          /* Else bytes to rewind is just length of ASCII string */
+          buflen = (int) strlen (data);
+        }
 
-      /* If this is a IP header hex string then set flag */
-      num_items_scanned = sscanf (data + 22, "IP Header  : %40s", ipheader);
-      if (num_items_scanned == 1)
-       {
-         IPread = TRUE;
-       }
+      /*
+       * Skip leading white space.
+       */
+      for (offset = 0; g_ascii_isspace(data[offset]); offset++)
+        ;
 
-      /* If this is TCP header hex string then set flag */
-      num_items_scanned = sscanf (data + 22, "TCP Header : %80s", tcpheader);
-      if (num_items_scanned == 1)
-       {
-         TCPread = TRUE;
-       }
+      /*
+       * The higher-level header information starts at an offset of
+       * 22 characters.  The header tags are 14 characters long.
+       *
+       * XXX - for IPv6, if the next header isn't the last header,
+       * the intermediate headers do *NOT* appear to be shown in
+       * the dump file *at all*, so the packet *cannot* be
+       * reconstructed!
+       */
+      if (offset == 22)
+        {
+          if (strncmp(data + 22, "IP Header  :  ", 14) == 0 ||
+              strncmp(data + 22, "IPv6 Header:  ", 14) == 0 ||
+              strncmp(data + 22, "ARP Header :  ", 14) == 0 ||
+              strncmp(data + 22, "TCP Header :  ", 14) == 0 ||
+              strncmp(data + 22, "UDP Header :  ", 14) == 0 ||
+              strncmp(data + 22, "ICMP Header:  ", 14) == 0 ||
+              strncmp(data + 22, "ICMPv6  Hdr:  ", 14) == 0 ||
+              strncmp(data + 22, "Option  Hdr:  ", 14) == 0)
+            {
+              ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
+                                               pkt_len*2,
+                                               data + 22 + 14, err,
+                                               err_info);
+              if (ascii_offset == -1)
+                {
+                  /* Bad line. */
+                  return FALSE;
+                }
+              continue;
+            }
+        }
 
       /*
-       * If there is data in the packet handle it here.
+       * Is this a data line?
        *
-       * The data header line will have the "Data . . " identifier, subsequent lines don't
+       * The "Data" starts at an offset of 8.
        */
-      num_items_scanned =
-       sscanf (data + 27, "%16[A-Z0-9] %16[A-Z0-9] %16[A-Z0-9] %16[A-Z0-9]",
-               hex1, hex2, hex3, hex4);
-      if (num_items_scanned > 0)
-       {
-         isDATA = TRUE;
-         /*
-          * Scan the data line for data blocks, depending on the number of blocks scanned
-          * add them along with current tcpdata buffer to the work buffer and then copy
-          * work buffer to tcpdata buffer to continue building up tcpdata buffer to contain
-          * a single hex string.
-          */
-         switch (num_items_scanned)
-           {
-           case 1:
-             g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s", tcpdatabuf,
-                         hex1);
-             break;
-           case 2:
-             g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s",
-                         tcpdatabuf, hex1, hex2);
-             break;
-           case 3:
-             g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s",
-                         tcpdatabuf, hex1, hex2, hex3);
-             break;
-           default:
-             g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s%s",
-                         tcpdatabuf, hex1, hex2, hex3, hex4);
-           }
-         memcpy (tcpdatabuf, workbuf, ISERIES_PKT_ALLOC_SIZE);
-       }
+      if (offset == 9)
+        {
+          if (strncmp(data + 9, "Data . . . . . :  ", 18) == 0)
+            {
+              ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
+                                               pkt_len*2,
+                                               data + 9 + 18, err,
+                                               err_info);
+              if (ascii_offset == -1)
+                {
+                  /* Bad line. */
+                  return FALSE;
+                }
+              continue;
+            }
+        }
+
+      /*
+       * Is this a continuation of a previous header or data line?
+       * That's blanks followed by hex digits; first try the
+       * "no column separators" form.
+       *
+       * Continuations of header lines begin at an offset of 36;
+       * continuations of data lines begin at an offset of 27.
+       */
+      if (offset == 36 || offset == 27)
+        {
+          ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
+                                           pkt_len*2,
+                                           data + offset, err,
+                                           err_info);
+          if (ascii_offset == -1)
+            {
+              /* Bad line. */
+              return FALSE;
+            }
+          continue;
+        }
 
       /*
        * If we see the identifier for the next packet then rewind and set
        * isCurrentPacket FALSE
        */
-      if ((strncmp (data + 80, ISERIES_PKT_MAGIC_STR, ISERIES_PKT_MAGIC_LEN)
-          == 0) && pktline > 1)
-       {
-         isCurrentPacket = FALSE;
-         cur_off = file_tell (fh);
-         if (cur_off == -1)
-           {
-             /* Error. */
-             *err = file_error (fh);
-             return -1;
-           }
-         if (file_seek (fh, cur_off - buflen, SEEK_SET, err) == -1)
-           {
-             return -1;
-           }
-       }
-    }
-
-  /*
-   * For a formated trace ensure we have read at least the IP and TCP headers otherwise
-   * exit and pass error message to user.
-   */
-  if (wth->capture.iseries->tcp_formatted)
-    {
-      if (!IPread)
-       {
-         *err = WTAP_ERR_BAD_RECORD;
-         *err_info = g_strdup ("iseries: IP header isn't valid");
-         return -1;
-       }
-      if (!TCPread)
-       {
-         *err = WTAP_ERR_BAD_RECORD;
-         *err_info = g_strdup ("iseries: TCP header isn't valid");
-         return -1;
-       }
-    }
-
-  /*
-   * Create a buffer to hold all the ASCII Hex data and populate with all the
-   * extracted data.
-   */
-  asciibuf = g_malloc (ISERIES_PKT_ALLOC_SIZE);
-  if (isDATA)
-    {
-      /* packet contained data */
-      if (wth->capture.iseries->tcp_formatted)
-       {
-         /* build string for formatted fields */
-         g_snprintf (asciibuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s%s%s",
-                     destmac, srcmac, type, ipheader, tcpheader, tcpdatabuf);
-       }
-      else
-       {
-         /* build string for unformatted data fields */
-         g_snprintf (asciibuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s", destmac,
-                     srcmac, type, tcpdatabuf);
-       }
-    }
-  else
-    {
-      /* No data in the packet */
-      g_snprintf (asciibuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s%s", destmac,
-                 srcmac, type, ipheader, tcpheader);
+      ascii_strup_inplace (data);
+      /* If packet header found return the offset */
+      num_items_scanned =
+          sscanf (data+78,
+          "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
+      if ((num_items_scanned == 1) && pktline > 1)
+        {
+          isCurrentPacket = FALSE;
+          cur_off = file_tell( fh);
+          if (cur_off == -1)
+            {
+              /* Error. */
+              *err = file_error (fh, err_info);
+              goto errxit;
+            }
+          if (file_seek (fh, cur_off - buflen, SEEK_SET, err) == -1)
+            {
+              /* XXX: need to set err_info ?? */
+              goto errxit;
+            }
+        }
     }
 
   /*
-   * Extract the packet length from the actual IP header; this may
-   * differ from the capture length reported by the formatted trace.
-   * Note: if the entire Ethernet packet is present, but the IP
-   * packet is less than 46 bytes long, there will be padding, and
-   * the length in the IP header won't include the padding; if
-   * the packet length is less than the captured length, set the
-   * packet length to the captured length.
+   * Make the captured length be the amount of bytes we've read (which
+   * is half the number of characters of hex dump we have).
+   *
+   * XXX - this can happen for IPv6 packets if the next header isn't the
+   * last header.
    */
-  num_items_scanned = sscanf (asciibuf + 32, "%4x", &pkt_len);
-  wth->phdr.len = pkt_len + 14;
-  if (wth->phdr.caplen > wth->phdr.len)
-    wth->phdr.len = wth->phdr.caplen;
+  phdr->caplen = ((guint32) ascii_offset)/2;
 
-  /* Make sure we have enough room for the packet, only create buffer if none supplied */
-  if (pd == NULL)
-    {
-      buffer_assure_space (wth->frame_buffer, ISERIES_MAX_PACKET_LEN);
-      buf = buffer_start_ptr (wth->frame_buffer);
-      /* Convert ascii data to binary and return in the frame buffer */
-      iseries_parse_hex_string (asciibuf, buf, strlen (asciibuf));
-    }
-  else
-    {
-      /* Convert ascii data to binary and return in the frame buffer */
-      iseries_parse_hex_string (asciibuf, pd, strlen (asciibuf));
-    }
+  /* Make sure we have enough room for the packet. */
+  ws_buffer_assure_space (buf, phdr->caplen);
+  /* Convert ascii data to binary and return in the frame buffer */
+  iseries_parse_hex_string (ascii_buf, ws_buffer_start_ptr (buf), ascii_offset);
 
-  /* free buffers allocs and return */
+  /* free buffer allocs and return */
   *err = 0;
-  free (asciibuf);
-  free (tcpdatabuf);
-  free (workbuf);
-  return wth->phdr.len;
+  g_free (ascii_buf);
+  return TRUE;
+
+errxit:
+  g_free (ascii_buf);
+  return FALSE;
 }
 
 /*
@@ -784,24 +979,25 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
 static int
 iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes)
 {
-  guint i;
+  guint   i;
   guint8 *bufptr;
+
   bufptr = buf;
 
   for (i = 0; i < bytes; i++)
     {
       switch (buf[i])
-       {
-       case 0xFE:
-       case 0xFF:
-       case 0x00:
-         break;
-       default:
-         *bufptr = buf[i];
-         bufptr++;
-       }
+        {
+          case 0xFE:
+          case 0xFF:
+          case 0x00:
+            break;
+          default:
+            *bufptr = buf[i];
+            bufptr++;
+        }
       if (buf[i] == 0x0A)
-       return i;
+        return i;
     }
   return i;
 }
@@ -811,19 +1007,42 @@ iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes)
  * Requires ASCII hex data and buffer to populate with binary data
  */
 static gboolean
-iseries_parse_hex_string (guint8 * ascii, guint8 * buf, int len)
+iseries_parse_hex_string (const char * ascii, guint8 * buf, size_t len)
 {
-  int i, byte;
-  char hexvalue[3] = { 0, 0, 0 };
+  size_t i;
+  int byte;
+  gint   hexvalue;
+  guint8 bytevalue;
 
   byte = 0;
   for (i = 0; i < len; i++)
     {
-      hexvalue[0] = ascii[i];
+      hexvalue = g_ascii_xdigit_value(ascii[i]);
       i++;
-      hexvalue[1] = ascii[i];
-      buf[byte] = (guint8) strtoul (hexvalue, NULL, 16);
+      if (hexvalue == -1)
+        return FALSE;        /* not a valid hex digit */
+      bytevalue = (guint8)(hexvalue << 4);
+      if (i >= len)
+        return FALSE;        /* only one hex digit of the byte is present */
+      hexvalue = g_ascii_xdigit_value(ascii[i]);
+      if (hexvalue == -1)
+        return FALSE;        /* not a valid hex digit */
+      bytevalue |= (guint8) hexvalue;
+      buf[byte] = bytevalue;
       byte++;
     }
   return TRUE;
 }
+
+/*
+ * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */