iseries: rework the read routine.
[metze/wireshark/wip.git] / wiretap / iseries.c
index 4f446eee335771b195c1828a5e374f4b884979bd..aec989203d98368bd6e848efcf1973389ba523fc 100644 (file)
@@ -158,13 +158,12 @@ Number  S/R  Length    Timer                        MAC Address   MAC Address
 #include <errno.h>
 
 #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_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
 
@@ -571,6 +570,21 @@ 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 gboolean
 iseries_parse_packet (wtap * wth, FILE_T fh, struct wtap_pkthdr *phdr,
@@ -581,7 +595,8 @@ iseries_parse_packet (wtap * wth, FILE_T fh, struct wtap_pkthdr *phdr,
   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;
   char      *ascii_buf;
@@ -609,9 +624,9 @@ iseries_parse_packet (wtap * wth, FILE_T fh, struct wtap_pkthdr *phdr,
       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)
         {
@@ -675,12 +690,51 @@ iseries_parse_packet (wtap * wth, FILE_T fh, struct wtap_pkthdr *phdr,
               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;
         }
@@ -716,49 +770,39 @@ iseries_parse_packet (wtap * wth, FILE_T fh, struct wtap_pkthdr *phdr,
       tm.tm_sec         = sec;
       tm.tm_isdst       = -1;
       phdr->ts.secs = mktime (&tm);
-      csec[sizeof(csec) - 1] = '\0';
-      switch (strlen(csec))
-        {
-          case 0:
-            phdr->ts.nsecs = 0;
-            break;
-          case 1:
-            phdr->ts.nsecs = atoi(csec) * 100000000;
-            break;
-          case 2:
-            phdr->ts.nsecs = atoi(csec) * 10000000;
-            break;
-          case 3:
-            phdr->ts.nsecs = atoi(csec) * 1000000;
-            break;
-          case 4:
-            phdr->ts.nsecs = atoi(csec) * 100000;
-            break;
-          case 5:
-            phdr->ts.nsecs = atoi(csec) * 10000;
-            break;
-          case 6:
-            phdr->ts.nsecs = atoi(csec) * 1000;
-            break;
-          case 7:
-            phdr->ts.nsecs = atoi(csec) * 100;
-            break;
-          case 8:
-            phdr->ts.nsecs = atoi(csec) * 10;
-            break;
-          case 9:
-            phdr->ts.nsecs = atoi(csec);
-            break;
-        }
+      phdr->ts.nsecs = csec * csec_multiplier(csec);
     }
 
   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
@@ -820,7 +864,7 @@ iseries_parse_packet (wtap * wth, FILE_T fh, struct wtap_pkthdr *phdr,
               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)
@@ -842,7 +886,7 @@ iseries_parse_packet (wtap * wth, FILE_T fh, struct wtap_pkthdr *phdr,
           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)
@@ -865,7 +909,7 @@ iseries_parse_packet (wtap * wth, FILE_T fh, struct wtap_pkthdr *phdr,
       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)
@@ -902,7 +946,6 @@ iseries_parse_packet (wtap * wth, FILE_T fh, struct wtap_pkthdr *phdr,
             }
         }
     }
-  ascii_buf[ascii_offset] = '\0';
 
   /*
    * Make the captured length be the amount of bytes we've read (which
@@ -911,12 +954,12 @@ iseries_parse_packet (wtap * wth, FILE_T fh, struct wtap_pkthdr *phdr,
    * XXX - this can happen for IPv6 packets if the next header isn't the
    * last header.
    */
-  phdr->caplen = ((guint32) strlen (ascii_buf))/2;
+  phdr->caplen = ((guint32) ascii_offset)/2;
 
   /* Make sure we have enough room for the packet. */
-  ws_buffer_assure_space (buf, ISERIES_MAX_PACKET_LEN);
+  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), strlen (ascii_buf));
+  iseries_parse_hex_string (ascii_buf, ws_buffer_start_ptr (buf), ascii_offset);
 
   /* free buffer allocs and return */
   *err = 0;