iseries: rework the read routine.
[metze/wireshark/wip.git] / wiretap / iseries.c
index 45911b2fac101e537272b5dd19473be6026b2c93..aec989203d98368bd6e848efcf1973389ba523fc 100644 (file)
@@ -1,6 +1,4 @@
 /* iseries.c
- *
- * $Id$
  *
  * Wiretap Library
  * Copyright (c) 2011 by Martin Warnes <Martin_Warnes@uk.ibm.com>
@@ -24,7 +22,7 @@
 
 /*
  * 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
  *
@@ -152,29 +150,41 @@ Number  S/R  Length    Timer                        MAC Address   MAC Address
 
 #include "config.h"
 #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>
 
 #include <wsutil/str_util.h>
+#include <wsutil/strtoi.h>
 
-#define ISERIES_HDR_MAGIC_STR         "COMMUNICATIONS TRACE"
-#define ISERIES_HDR_MAGIC_LEN         20
 #define ISERIES_LINE_LENGTH           270
 #define ISERIES_HDR_LINES_TO_CHECK    100
 #define ISERIES_PKT_LINES_TO_CHECK    4
-#define ISERIES_MAX_PACKET_LEN        16384
 #define ISERIES_MAX_TRACE_LEN         99999999
-#define ISERIES_PKT_ALLOC_SIZE        (pkt_len*2)+1
 #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 */
@@ -185,54 +195,49 @@ static gboolean iseries_read (wtap * wth, int *err, gchar ** err_info,
                               gint64 *data_offset);
 static gboolean iseries_seek_read (wtap * wth, gint64 seek_off,
                                    struct wtap_pkthdr *phdr,
-                                   guint8 * pd, int len, int *err,
-                                   gchar ** err_info);
+                                   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 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);
 static int iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes);
 static gboolean iseries_parse_hex_string (const char * ascii, guint8 * buf,
                                           size_t len);
 
-int
+/*
+ * 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;
   gint offset;
   char magic[ISERIES_LINE_LENGTH];
-  char unicodemagic[] =
-    { '\x43', '\x00', '\x4F', '\x00', '\x4D',
-    '\x00', '\x4D', '\x00', '\x55', '\x00', '\x4E', '\x00', '\x49', '\x00',
-    '\x43', '\x00', '\x41'
-  };
 
   /*
    * 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, 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, err_info);
-      if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
-        return -1;
-      return 0;
+      if (*err != WTAP_ERR_SHORT_READ)
+        return WTAP_OPEN_ERROR;
+      return WTAP_OPEN_NOT_MINE;
     }
 
   /*
-   * Check if this is a UNICODE formatted file by scanning for the magic string
+   * Check if this is a little-endian UCS-2 Unicode formatted file by scanning
+   * for the magic string
    */
   offset=0;
-  while ((unsigned)offset < (ISERIES_LINE_LENGTH - (sizeof unicodemagic)))
+  while ((unsigned int)offset < (ISERIES_LINE_LENGTH - (sizeof iseries_hdr_magic_le_ucs_2)))
     {
-      if (memcmp (magic + offset, unicodemagic, sizeof unicodemagic) == 0) {
+      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 0;
+            return WTAP_OPEN_ERROR;
           }
         /*
          * Do some basic sanity checking to ensure we can handle the
@@ -241,23 +246,23 @@ iseries_open (wtap * wth, int *err, gchar ** err_info)
         if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_UNICODE))
           {
             if (*err == 0)
-              return 0;
+              return WTAP_OPEN_NOT_MINE;
             else
-              return -1;
+              return WTAP_OPEN_ERROR;
           }
 
         wth->file_encap        = WTAP_ENCAP_ETHERNET;
-        wth->file_type         = WTAP_FILE_ISERIES;
+        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->tsprecision       = WTAP_FILE_TSPREC_USEC;
+        wth->file_tsprec       = WTAP_TSPREC_USEC;
 
         if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
           {
-            return 0;
+            return WTAP_OPEN_ERROR;
           }
-        return 1;
+        return WTAP_OPEN_MINE;
       }
       offset += 1;
     }
@@ -266,13 +271,13 @@ iseries_open (wtap * wth, int *err, gchar ** err_info)
      * Check if this is a ASCII formatted file by scanning for the magic string
      */
     offset=0;
-    while (offset < (ISERIES_LINE_LENGTH - ISERIES_HDR_MAGIC_LEN))
+    while ((unsigned int)offset < (ISERIES_LINE_LENGTH - sizeof iseries_hdr_magic_ascii))
       {
-        if (memcmp (magic + offset, ISERIES_HDR_MAGIC_STR, ISERIES_HDR_MAGIC_LEN) == 0)
+        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 0;
+                return WTAP_OPEN_ERROR;
               }
             /*
              * Do some basic sanity checking to ensure we can handle the
@@ -281,29 +286,29 @@ iseries_open (wtap * wth, int *err, gchar ** err_info)
             if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_ASCII))
               {
                 if (*err == 0)
-                  return 0;
+                  return WTAP_OPEN_NOT_MINE;
                 else
-                  return -1;
+                  return WTAP_OPEN_ERROR;
               }
 
             wth->file_encap        = WTAP_ENCAP_ETHERNET;
-            wth->file_type         = WTAP_FILE_ISERIES;
+            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->tsprecision       = WTAP_FILE_TSPREC_USEC;
+            wth->file_tsprec       = WTAP_TSPREC_USEC;
 
             if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
               {
-                return 0;
+                return WTAP_OPEN_ERROR;
               }
-            return 1;
+            return WTAP_OPEN_MINE;
           }
         offset += 1;
       }
 
     /* Neither ASCII or UNICODE so not supported */
-    return 0;
+    return WTAP_OPEN_NOT_MINE;
     }
 
 /*
@@ -314,6 +319,7 @@ iseries_open (wtap * wth, int *err, gchar ** err_info)
 static gboolean
 iseries_check_file_type (wtap * wth, int *err, gchar **err_info, int format)
 {
+  gboolean   is_iseries = FALSE;
   guint      line;
   int        num_items_scanned;
   char       buf[ISERIES_LINE_LENGTH], protocol[9];
@@ -321,53 +327,61 @@ iseries_check_file_type (wtap * wth, int *err, gchar **err_info, int format)
 
   /* Save trace format for passing between packets */
   iseries                = (iseries_t *) g_malloc (sizeof (iseries_t));
-  wth->priv              = (void *) iseries;
   iseries->have_date     = FALSE;
   iseries->format        = format;
 
   for (line = 0; line < ISERIES_HDR_LINES_TO_CHECK; line++)
     {
+      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;
-          return FALSE;
+          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)
-              return FALSE;
-          }
+      /*
+       * 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;
-          }
+      /*
+       * 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;
 }
 
 /*
@@ -377,32 +391,27 @@ static gboolean
 iseries_read (wtap * wth, int *err, gchar ** err_info, gint64 *data_offset)
 {
   gint64 offset;
-  int    cap_len;
 
   /*
    * Locate the next packet
    */
   offset = iseries_seek_next_packet (wth, err, err_info);
-  if (offset < 1)
+  if (offset < 0)
     return FALSE;
+  *data_offset     = offset;
 
   /*
    * Parse the packet and extract the various fields
    */
-  cap_len =
-    iseries_parse_packet (wth, wth->fh, &wth->phdr.pseudo_header, NULL, err,
-                          err_info);
-  if (cap_len == -1)
-    return FALSE;
-
-  *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
- * and *err_info to null or an additional error string.
+ * 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, gchar **err_info)
@@ -413,29 +422,20 @@ iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info)
   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
-   * and *err_info to null or an additional error string.
-   */
   for (line = 0; line < ISERIES_MAX_TRACE_LEN; line++)
     {
       if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
         {
           /* EOF or error. */
           *err = file_error (wth->fh, err_info);
-          if (*err != 0)
-            {
-              return -1;
-            }
-          return 0;
+          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);
+            buflen = iseries_UNICODE_to_ASCII ((guint8 *) buf, ISERIES_LINE_LENGTH);
           }
         else
           {
@@ -475,12 +475,9 @@ iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info)
  * Read packets in random-access fashion
  */
 static gboolean
-iseries_seek_read (wtap * wth, gint64 seek_off,
-                   struct wtap_pkthdr *phdr, 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)
 {
-  union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
-  int cap_len;
 
   /* seek to packet location */
   if (file_seek (wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
@@ -489,21 +486,8 @@ iseries_seek_read (wtap * wth, gint64 seek_off,
   /*
    * Parse the packet and extract the various fields
    */
-  cap_len = iseries_parse_packet (wth, wth->random_fh, pseudo_header, pd,
-                                  err, err_info);
-
-  if (cap_len != len)
-    {
-      if (cap_len != -1)
-        {
-          *err = WTAP_ERR_BAD_FILE;
-          *err_info =
-            g_strdup_printf ("iseries: requested length %d doesn't match record length %d",
-             len, cap_len);
-        }
-      return FALSE;
-    }
-  return TRUE;
+  return iseries_parse_packet (wth, wth->random_fh, phdr, buf,
+                               err, err_info);
 }
 
 static int
@@ -541,7 +525,7 @@ append_hex_digits(char *ascii_buf, int ascii_offset, int max_offset,
             {
               goto done;
             }
-          if (!isxdigit(c) || islower(c))
+          if (!g_ascii_isxdigit(c) || g_ascii_islower(c))
             {
               /*
                * Not a hex digit, or a lower-case hex digit.
@@ -586,21 +570,35 @@ done:
   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)
 {
   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], csec[9+1];
+  char       direction[2], destmac[13], srcmac[13], type[5];
+  guint32    csec;
   char       data[ISERIES_LINE_LENGTH * 2];
   int        offset;
-  guint8    *buf;
   char      *ascii_buf;
   int        ascii_offset;
   struct tm  tm;
@@ -616,11 +614,7 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
       if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
         {
           *err = file_error (fh, err_info);
-          if (*err != 0)
-            {
-              return -1;
-            }
-          return 0;
+          return FALSE;
         }
       /* Convert UNICODE data to ASCII */
       if (iseries->format == ISERIES_FORMAT_UNICODE)
@@ -630,18 +624,117 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
       ascii_strup_inplace (data);
       num_items_scanned =
         sscanf (data,
-                "%*[ \n\t]%6d%*[ *\n\t]%1s%*[ \n\t]%6d%*[ \n\t]%2d:%2d:%2d.%9[0-9]%*[ \n\t]"
+                "%*[ \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,
+                &pktnum, direction, &pkt_len, &hr, &min, &sec, &csec, destmac,
                 srcmac, type);
       if (num_items_scanned == 10)
         {
+          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;
         }
@@ -654,10 +747,11 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
     {
       *err = WTAP_ERR_BAD_FILE;
       *err_info = g_strdup ("iseries: packet header isn't valid");
-      return -1;
+      return FALSE;
     }
 
-  wth->phdr.presence_flags = WTAP_HAS_CAP_LEN;
+  phdr->rec_type = REC_TYPE_PACKET;
+  phdr->presence_flags = WTAP_HAS_CAP_LEN;
 
   /*
    * If we have Wiretap Header then populate it here
@@ -667,7 +761,7 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
    */
   if (iseries->have_date)
     {
-      wth->phdr.presence_flags |= WTAP_HAS_TS;
+      phdr->presence_flags |= WTAP_HAS_TS;
       tm.tm_year        = 100 + iseries->year;
       tm.tm_mon         = iseries->month - 1;
       tm.tm_mday        = iseries->day;
@@ -675,49 +769,40 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
       tm.tm_min         = min;
       tm.tm_sec         = sec;
       tm.tm_isdst       = -1;
-      wth->phdr.ts.secs = mktime (&tm);
-      switch (strlen(csec))
-        {
-          case 0:
-            wth->phdr.ts.nsecs = 0;
-            break;
-          case 1:
-            wth->phdr.ts.nsecs = atoi(csec) * 100000000;
-            break;
-          case 2:
-            wth->phdr.ts.nsecs = atoi(csec) * 10000000;
-            break;
-          case 3:
-            wth->phdr.ts.nsecs = atoi(csec) * 1000000;
-            break;
-          case 4:
-            wth->phdr.ts.nsecs = atoi(csec) * 100000;
-            break;
-          case 5:
-            wth->phdr.ts.nsecs = atoi(csec) * 10000;
-            break;
-          case 6:
-            wth->phdr.ts.nsecs = atoi(csec) * 1000;
-            break;
-          case 7:
-            wth->phdr.ts.nsecs = atoi(csec) * 100;
-            break;
-          case 8:
-            wth->phdr.ts.nsecs = atoi(csec) * 10;
-            break;
-          case 9:
-            wth->phdr.ts.nsecs = atoi(csec);
-            break;
-        }
+      phdr->ts.secs = mktime (&tm);
+      phdr->ts.nsecs = csec * csec_multiplier(csec);
     }
 
-  wth->phdr.len              = pkt_len;
-  wth->phdr.pkt_encap        = WTAP_ENCAP_ETHERNET;
-  pseudo_header->eth.fcs_len = -1;
+  phdr->len                       = pkt_len;
+  phdr->pkt_encap                 = WTAP_ENCAP_ETHERNET;
+  phdr->pseudo_header.eth.fcs_len = -1;
 
-  ascii_buf = (char *)g_malloc (ISERIES_PKT_ALLOC_SIZE);
-  g_snprintf(ascii_buf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s", destmac, srcmac, type);
-  ascii_offset = 14*2; /* 14-byte Ethernet header, 2 characters per byte */
+  /*
+   * 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.)
+   */
+  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;
 
   /*
    * Start reading packet contents
@@ -755,7 +840,7 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
       /*
        * Skip leading white space.
        */
-      for (offset = 0; isspace(data[offset]); offset++)
+      for (offset = 0; g_ascii_isspace(data[offset]); offset++)
         ;
 
       /*
@@ -779,13 +864,13 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
               strncmp(data + 22, "Option  Hdr:  ", 14) == 0)
             {
               ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
-                                               ISERIES_PKT_ALLOC_SIZE - 1,
+                                               pkt_len*2,
                                                data + 22 + 14, err,
                                                err_info);
               if (ascii_offset == -1)
                 {
                   /* Bad line. */
-                  return -1;
+                  return FALSE;
                 }
               continue;
             }
@@ -801,13 +886,13 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
           if (strncmp(data + 9, "Data . . . . . :  ", 18) == 0)
             {
               ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
-                                               ISERIES_PKT_ALLOC_SIZE - 1,
+                                               pkt_len*2,
                                                data + 9 + 18, err,
                                                err_info);
               if (ascii_offset == -1)
                 {
                   /* Bad line. */
-                  return -1;
+                  return FALSE;
                 }
               continue;
             }
@@ -824,13 +909,13 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
       if (offset == 36 || offset == 27)
         {
           ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
-                                           ISERIES_PKT_ALLOC_SIZE - 1,
+                                           pkt_len*2,
                                            data + offset, err,
                                            err_info);
           if (ascii_offset == -1)
             {
               /* Bad line. */
-              return -1;
+              return FALSE;
             }
           continue;
         }
@@ -861,7 +946,6 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
             }
         }
     }
-  ascii_buf[ascii_offset] = '\0';
 
   /*
    * Make the captured length be the amount of bytes we've read (which
@@ -870,30 +954,21 @@ iseries_parse_packet (wtap * wth, FILE_T fh,
    * XXX - this can happen for IPv6 packets if the next header isn't the
    * last header.
    */
-  wth->phdr.caplen = ((guint32) strlen (ascii_buf))/2;
+  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 (ascii_buf, buf, strlen (ascii_buf));
-    }
-  else
-    {
-      /* Convert ascii data to binary and return in the frame buffer */
-      iseries_parse_hex_string (ascii_buf, pd, strlen (ascii_buf));
-    }
+  /* 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 buffer allocs and return */
   *err = 0;
   g_free (ascii_buf);
-  return wth->phdr.caplen;
+  return TRUE;
 
 errxit:
   g_free (ascii_buf);
-  return -1;
+  return FALSE;
 }
 
 /*