randpktdump: fix a crash.
[metze/wireshark/wip.git] / wiretap / ascendtext.c
index 612a202b097faf0322a818b10cd4b39dba1bc895..7259fa94bf82304b74777b302fb597626e96c06e 100644 (file)
@@ -1,6 +1,4 @@
 /* ascendtext.c
- *
- * $Id$
  *
  * Wiretap Library
  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
  *
  * 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.
  */
 
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 #include "wtap-int.h"
-#include "buffer.h"
 #include "ascendtext.h"
 #include "ascend-int.h"
 #include "file_wrappers.h"
-#include <wsutil/file_util.h>
 
 #include <errno.h>
 
@@ -36,7 +30,6 @@
 #include <unistd.h>
 #endif
 
-#include <ctype.h>
 #include <string.h>
 
 /* Last updated: Feb 03 2005: Josh Bailey (joshbailey@lucent.com).
    This module reads the text hex dump output of various TAOS
    (Lucent/Ascend Max, Max TNT, APX, etc) debug commands, including:
 
-   * pridisplay                traces primary rate ISDN
-   * ether-display     traces Ethernet packets (dangerous! CPU intensive)
+   * pridisplay         traces primary rate ISDN
+   * ether-display      traces Ethernet packets (dangerous! CPU intensive)
    * wanopening, wandisplay, wannext, wandsess
-                       traces PPP or other WAN connections
+                        traces PPP or other WAN connections
 
    Please see ascend.y for examples.
 
 
 typedef struct _ascend_magic_string {
   guint        type;
-  const gchar   *strptr;
+  const gchar *strptr;
+  size_t       strlength;
 } ascend_magic_string;
 
-#define ASCEND_MAGIC_STRINGS   11
-#define ASCEND_DATE            "Date:"
-
 /* these magic strings signify the headers of a supported debug commands */
+#define ASCEND_MAGIC_ENTRY(type, string) \
+  { type, string, sizeof string - 1 } /* strlen of a constant string */
 static const ascend_magic_string ascend_magic[] = {
-  { ASCEND_PFX_ISDN_X, "PRI-XMIT-" },
-  { ASCEND_PFX_ISDN_R, "PRI-RCV-" },
-  { ASCEND_PFX_WDS_X,  "XMIT-" },
-  { ASCEND_PFX_WDS_R,  "RECV-" },
-  { ASCEND_PFX_WDS_X,  "XMIT:" },
-  { ASCEND_PFX_WDS_R,  "RECV:" },
-  { ASCEND_PFX_WDS_X,   "PPP-OUT" },
-  { ASCEND_PFX_WDS_R,   "PPP-IN" },
-  { ASCEND_PFX_WDD,    ASCEND_DATE },
-  { ASCEND_PFX_WDD,    "WD_DIALOUT_DISP:" },
-  { ASCEND_PFX_ETHER,  "ETHER" },
+  ASCEND_MAGIC_ENTRY(ASCEND_PFX_ISDN_X,  "PRI-XMIT-"),
+  ASCEND_MAGIC_ENTRY(ASCEND_PFX_ISDN_R,  "PRI-RCV-"),
+  ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_X,   "XMIT-"),
+  ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_R,   "RECV-"),
+  ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_X,   "XMIT:"),
+  ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_R,   "RECV:"),
+  ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_X,   "PPP-OUT"),
+  ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_R,   "PPP-IN"),
+  ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDD,     "WD_DIALOUT_DISP:"),
+  ASCEND_MAGIC_ENTRY(ASCEND_PFX_ETHER,   "ETHER"),
 };
 
-typedef struct {
-       time_t inittime;
-       int adjusted;
-       gint64 next_packet_seek_start;
-} ascend_t;
+#define ASCEND_MAGIC_STRINGS    G_N_ELEMENTS(ascend_magic)
+
+#define ASCEND_DATE             "Date:"
 
 static gboolean ascend_read(wtap *wth, int *err, gchar **err_info,
-       gint64 *data_offset);
+        gint64 *data_offset);
 static gboolean ascend_seek_read(wtap *wth, gint64 seek_off,
-       union wtap_pseudo_header *pseudo_head, guint8 *pd, int len,
-       int *err, gchar **err_info);
+        struct wtap_pkthdr *phdr, Buffer *buf,
+        int *err, gchar **err_info);
 
 /* Seeks to the beginning of the next packet, and returns the
    byte offset at which the header for that packet begins.
@@ -99,9 +89,13 @@ static gint64 ascend_seek(wtap *wth, int *err, gchar **err_info)
   gint64 date_off = -1, cur_off, packet_off;
   size_t string_level[ASCEND_MAGIC_STRINGS];
   guint string_i = 0, type = 0;
+  static const gchar ascend_date[] = ASCEND_DATE;
+  size_t ascend_date_len           = sizeof ascend_date - 1; /* strlen of a constant string */
+  size_t ascend_date_string_level;
   guint excessive_read_count = 262144;
 
   memset(&string_level, 0, sizeof(string_level));
+  ascend_date_string_level = 0;
 
   while (((byte = file_getc(wth->fh)) != EOF)) {
     excessive_read_count--;
@@ -111,13 +105,28 @@ static gint64 ascend_seek(wtap *wth, int *err, gchar **err_info)
       return -1;
     }
 
+    /*
+     * See whether this is the string_level[string_i]th character of
+     * Ascend magic string string_i.
+     */
     for (string_i = 0; string_i < ASCEND_MAGIC_STRINGS; string_i++) {
       const gchar *strptr = ascend_magic[string_i].strptr;
-      size_t len          = strlen(strptr);
+      size_t len          = ascend_magic[string_i].strlength;
 
       if (byte == *(strptr + string_level[string_i])) {
+        /*
+         * Yes, it is, so we need to check for the next character of
+         * that string.
+         */
         string_level[string_i]++;
+
+        /*
+         * Have we matched the entire string?
+         */
         if (string_level[string_i] >= len) {
+          /*
+           * Yes.
+           */
           cur_off = file_tell(wth->fh);
           if (cur_off == -1) {
             /* Error. */
@@ -125,39 +134,74 @@ static gint64 ascend_seek(wtap *wth, int *err, gchar **err_info)
             return -1;
           }
 
-          /* Date: header is a special case. Remember the offset,
-             but keep looking for other headers. */
-         if (strcmp(strptr, ASCEND_DATE) == 0) {
-            date_off = cur_off - len;
+          /* We matched some other type of header. */
+          if (date_off == -1) {
+            /* We haven't yet seen a date header, so this packet
+               doesn't have one.
+               Back up over the header we just read; that's where a read
+               of this packet should start. */
+            packet_off = cur_off - len;
           } else {
-            if (date_off == -1) {
-              /* Back up over the header we just read; that's where a read
-                 of this packet should start. */
-              packet_off = cur_off - len;
-            } else {
-              /* This packet has a date/time header; a read of it should
-                 start at the beginning of *that* header. */
-              packet_off = date_off;
-            }
-
-            type = ascend_magic[string_i].type;
-            goto found;
+            /* This packet has a date/time header; a read of it should
+               start at the beginning of *that* header. */
+            packet_off = date_off;
           }
+
+          type = ascend_magic[string_i].type;
+          goto found;
         }
       } else {
+        /*
+         * Not a match for this string, so reset the match process.
+         */
         string_level[string_i] = 0;
       }
     }
-  }
 
-  if (byte != EOF || file_eof(wth->fh)) {
-    /* Either we didn't find the offset, or we got an EOF. */
-    *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, err_info);
+    /*
+     * See whether this is the date_string_level'th character of
+     * ASCEND_DATE.
+     */
+    if (byte == *(ascend_date + ascend_date_string_level)) {
+      /*
+       * Yes, it is, so we need to check for the next character of
+       * that string.
+       */
+      ascend_date_string_level++;
+
+      /*
+       * Have we matched the entire string?
+       */
+      if (ascend_date_string_level >= ascend_date_len) {
+        /* We matched a Date: header.  It's a special case;
+           remember the offset, but keep looking for other
+           headers.
+
+           Reset the amount of Date: header that we've matched,
+           so that we start the process of matching a Date:
+           header all over again.
+
+           XXX - what if we match multiple Date: headers before
+           matching some other header? */
+        cur_off = file_tell(wth->fh);
+        if (cur_off == -1) {
+          /* Error. */
+          *err = file_error(wth->fh, err_info);
+          return -1;
+        }
+
+        date_off = cur_off - ascend_date_len;
+        ascend_date_string_level = 0;
+      }
+    } else {
+      /*
+       * Not a match for the Date: string, so reset the match process.
+       */
+      ascend_date_string_level = 0;
+    }
   }
+
+  *err = file_error(wth->fh, err_info);
   return -1;
 
 found:
@@ -168,18 +212,17 @@ found:
   if (file_seek(wth->fh, packet_off, SEEK_SET, err) == -1)
     return -1;
 
-  wth->pseudo_header.ascend.type = type;
+  wth->phdr.pseudo_header.ascend.type = type;
 
   return packet_off;
 }
 
-int ascend_open(wtap *wth, int *err, gchar **err_info)
+wtap_open_return_val ascend_open(wtap *wth, int *err, gchar **err_info)
 {
   gint64 offset;
-  ws_statb64 statbuf;
   guint8 buf[ASCEND_MAX_PKT_LEN];
-  ascend_pkthdr header;
-  gint64 dummy_seek_start;
+  ascend_state_t parser_state;
+  ws_statb64 statbuf;
   ascend_t *ascend;
 
   /* We haven't yet allocated a data structure for our private stuff;
@@ -189,23 +232,31 @@ int ascend_open(wtap *wth, int *err, gchar **err_info)
 
   offset = ascend_seek(wth, err, err_info);
   if (offset == -1) {
-    if (*err == 0)
-      return 0;
-    else
-      return -1;
+    if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+      return WTAP_OPEN_ERROR;
+    return WTAP_OPEN_NOT_MINE;
   }
 
   /* Do a trial parse of the first packet just found to see if we might really have an Ascend file */
-  init_parse_ascend();
-  if (parse_ascend(wth->fh, buf, &wth->pseudo_header.ascend, &header,
-      &dummy_seek_start) != PARSED_RECORD) {
-    return 0;
+  if (run_ascend_parser(wth->fh, &wth->phdr, buf, &parser_state, err, err_info) != 0) {
+    if (*err != 0) {
+        /* An I/O error. */
+        return WTAP_OPEN_ERROR;
+    }
+  }
+
+  /* if we got at least some data, and didn't get an I/O error, return
+     success even if the parser reported an error. This is because the
+     debug header gives the number of bytes on the wire, not actually
+     how many bytes are in the trace.  We won't know where the data ends
+     until we run into the next packet. */
+  if (parser_state.caplen == 0) {
+    return WTAP_OPEN_NOT_MINE;
   }
 
-  wth->data_offset = offset;
-  wth->file_type = WTAP_FILE_ASCEND;
+  wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_ASCEND;
 
-  switch(wth->pseudo_header.ascend.type) {
+  switch(wth->phdr.pseudo_header.ascend.type) {
     case ASCEND_PFX_ISDN_X:
     case ASCEND_PFX_ISDN_R:
       wth->file_encap = WTAP_ENCAP_ISDN;
@@ -236,45 +287,125 @@ int ascend_open(wtap *wth, int *err, gchar **err_info)
      offset that we can apply to each packet.
    */
   if (wtap_fstat(wth, &statbuf, err) == -1) {
-    g_free(ascend);
-    return -1;
+    return WTAP_OPEN_ERROR;
   }
   ascend->inittime = statbuf.st_ctime;
-  ascend->adjusted = 0;
-  wth->tsprecision = WTAP_FILE_TSPREC_USEC;
-
-  init_parse_ascend();
+  ascend->adjusted = FALSE;
+  wth->file_tsprec = WTAP_TSPREC_USEC;
 
-  return 1;
+  return WTAP_OPEN_MINE;
 }
 
-static void config_pseudo_header(union wtap_pseudo_header *pseudo_head)
+typedef enum {
+    PARSED_RECORD,
+    PARSED_NONRECORD,
+    PARSE_FAILED
+} parse_t;
+
+/* Parse the capture file.
+   Returns:
+     PARSED_RECORD if we got a packet
+     PARSED_NONRECORD if the parser succeeded but didn't see a packet
+     PARSE_FAILED if the parser failed. */
+static parse_t
+parse_ascend(ascend_t *ascend, FILE_T fh, struct wtap_pkthdr *phdr, Buffer *buf,
+             guint length, int *err, gchar **err_info)
 {
-  switch(pseudo_head->ascend.type) {
-    case ASCEND_PFX_ISDN_X:
-      pseudo_head->isdn.uton = TRUE;
-      pseudo_head->isdn.channel = 0;
-      break;
-
-    case ASCEND_PFX_ISDN_R:
-      pseudo_head->isdn.uton = FALSE;
-      pseudo_head->isdn.channel = 0;
-      break;
+  ascend_state_t parser_state;
+  int retval;
+
+  ws_buffer_assure_space(buf, length);
+  retval = run_ascend_parser(fh, phdr, ws_buffer_start_ptr(buf), &parser_state,
+                             err, err_info);
+
+  /* did we see any data (hex bytes)? if so, tip off ascend_seek()
+     as to where to look for the next packet, if any. If we didn't,
+     maybe this record was broken. Advance so we don't get into
+     an infinite loop reading a broken trace. */
+  if (parser_state.first_hexbyte) {
+    ascend->next_packet_seek_start = parser_state.first_hexbyte;
+  } else {
+    /* Sometimes, a header will be printed but the data will be omitted, or
+       worse -- two headers will be printed, followed by the data for each.
+       Because of this, we need to be fairly tolerant of what we accept
+       here.  If we didn't find any hex bytes, skip over what we've read so
+       far so we can try reading a new packet. */
+    ascend->next_packet_seek_start = file_tell(fh);
+    retval = 0;
+  }
 
-    case ASCEND_PFX_ETHER:
-      pseudo_head->eth.fcs_len = 0;
-      break;
+  /* if we got at least some data, return success even if the parser
+     reported an error. This is because the debug header gives the number
+     of bytes on the wire, not actually how many bytes are in the trace.
+     We won't know where the data ends until we run into the next packet. */
+  if (parser_state.caplen) {
+    if (! ascend->adjusted) {
+      ascend->adjusted = TRUE;
+      if (parser_state.saw_timestamp) {
+        /*
+         * Capture file contained a date and time.
+         * We do this only if this is the very first packet we've seen -
+         * i.e., if "ascend->adjusted" is false - because
+         * if we get a date and time after the first packet, we can't
+         * go back and adjust the time stamps of the packets we've already
+         * processed, and basing the time stamps of this and following
+         * packets on the time stamp from the file text rather than the
+         * ctime of the capture file means times before this and after
+         * this can't be compared.
+         */
+        ascend->inittime = parser_state.timestamp;
+      }
+      if (ascend->inittime > parser_state.secs)
+        ascend->inittime -= parser_state.secs;
+    }
+    phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+    phdr->ts.secs = parser_state.secs + ascend->inittime;
+    phdr->ts.nsecs = parser_state.usecs * 1000;
+    phdr->caplen = parser_state.caplen;
+    phdr->len = parser_state.wirelen;
+
+    /*
+     * For these types, the encapsulation we use is not WTAP_ENCAP_ASCEND,
+     * so set the pseudo-headers appropriately for the type (WTAP_ENCAP_ISDN
+     * or WTAP_ENCAP_ETHERNET).
+     */
+    switch(phdr->pseudo_header.ascend.type) {
+      case ASCEND_PFX_ISDN_X:
+        phdr->pseudo_header.isdn.uton = TRUE;
+        phdr->pseudo_header.isdn.channel = 0;
+        break;
+
+      case ASCEND_PFX_ISDN_R:
+        phdr->pseudo_header.isdn.uton = FALSE;
+        phdr->pseudo_header.isdn.channel = 0;
+        break;
+
+      case ASCEND_PFX_ETHER:
+        phdr->pseudo_header.eth.fcs_len = 0;
+        break;
+    }
+    return PARSED_RECORD;
   }
+
+  /* Didn't see any data. Still, perhaps the parser was happy.  */
+  if (retval) {
+    if (*err == 0) {
+      /* Not a bad record, so a parse error.  Return WTAP_ERR_BAD_FILE,
+         with the parse error as the error string. */
+      *err = WTAP_ERR_BAD_FILE;
+      *err_info = g_strdup((parser_state.ascend_parse_error != NULL) ? parser_state.ascend_parse_error : "parse error");
+    }
+    return PARSE_FAILED;
+  } else
+    return PARSED_NONRECORD;
 }
 
 /* Read the next packet; called from wtap_read(). */
 static gboolean ascend_read(wtap *wth, int *err, gchar **err_info,
-       gint64 *data_offset)
+        gint64 *data_offset)
 {
   ascend_t *ascend = (ascend_t *)wth->priv;
   gint64 offset;
-  guint8 *buf = buffer_start_ptr(wth->frame_buffer);
-  ascend_pkthdr header;
 
   /* parse_ascend() will advance the point at which to look for the next
      packet's header, to just after the last packet's header (ie. at the
@@ -284,64 +415,41 @@ static gboolean ascend_read(wtap *wth, int *err, gchar **err_info,
                 SEEK_SET, err) == -1)
     return FALSE;
 
-    offset = ascend_seek(wth, err, err_info);
-    if (offset == -1)
-      return FALSE;
-  if (parse_ascend(wth->fh, buf, &wth->pseudo_header.ascend, &header,
-      &(ascend->next_packet_seek_start)) != PARSED_RECORD) {
-    *err = WTAP_ERR_BAD_FILE;
-    *err_info = g_strdup((ascend_parse_error != NULL) ? ascend_parse_error : "parse error");
+  offset = ascend_seek(wth, err, err_info);
+  if (offset == -1)
+    return FALSE;
+  if (parse_ascend(ascend, wth->fh, &wth->phdr, wth->frame_buffer,
+                   wth->snapshot_length, err, err_info) != PARSED_RECORD)
     return FALSE;
-  }
-
-  buffer_assure_space(wth->frame_buffer, wth->snapshot_length);
-
-  config_pseudo_header(&wth->pseudo_header);
-
-  if (! ascend->adjusted) {
-    ascend->adjusted = 1;
-    if (header.start_time != 0) {
-      /*
-       * Capture file contained a date and time.
-       * We do this only if this is the very first packet we've seen -
-       * i.e., if "ascend->adjusted" is false - because
-       * if we get a date and time after the first packet, we can't
-       * go back and adjust the time stamps of the packets we've already
-       * processed, and basing the time stamps of this and following
-       * packets on the time stamp from the file text rather than the
-       * ctime of the capture file means times before this and after
-       * this can't be compared.
-       */
-      ascend->inittime = header.start_time;
-    }
-    if (ascend->inittime > header.secs)
-      ascend->inittime -= header.secs;
-  }
-  wth->phdr.ts.secs = header.secs + ascend->inittime;
-  wth->phdr.ts.nsecs = header.usecs * 1000;
-  wth->phdr.caplen = header.caplen;
-  wth->phdr.len = header.len;
-  wth->data_offset = offset;
 
   *data_offset = offset;
   return TRUE;
 }
 
 static gboolean ascend_seek_read(wtap *wth, gint64 seek_off,
-       union wtap_pseudo_header *pseudo_head, guint8 *pd, int len _U_,
-       int *err, gchar **err_info)
+        struct wtap_pkthdr *phdr, Buffer *buf,
+        int *err, gchar **err_info)
 {
   ascend_t *ascend = (ascend_t *)wth->priv;
 
   if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
     return FALSE;
-  if (parse_ascend(wth->random_fh, pd, &pseudo_head->ascend, NULL,
-      &(ascend->next_packet_seek_start)) != PARSED_RECORD) {
-    *err = WTAP_ERR_BAD_FILE;
-    *err_info = g_strdup((ascend_parse_error != NULL) ? ascend_parse_error : "parse error");
+  if (parse_ascend(ascend, wth->random_fh, phdr, buf,
+                   wth->snapshot_length, err, err_info) != PARSED_RECORD)
     return FALSE;
-  }
 
-  config_pseudo_header(pseudo_head);
   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:
+ */