summaryrefslogtreecommitdiffstats
path: root/wiretap/ascendtext.c
diff options
context:
space:
mode:
Diffstat (limited to 'wiretap/ascendtext.c')
-rw-r--r--wiretap/ascendtext.c483
1 files changed, 483 insertions, 0 deletions
diff --git a/wiretap/ascendtext.c b/wiretap/ascendtext.c
new file mode 100644
index 0000000..71e7273
--- /dev/null
+++ b/wiretap/ascendtext.c
@@ -0,0 +1,483 @@
+/* ascendtext.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include "wtap-int.h"
+#include "ascendtext.h"
+#include "ascend-int.h"
+#include "file_wrappers.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <string.h>
+
+/* Last updated: Feb 03 2005: Josh Bailey (joshbailey@lucent.com).
+
+ This module reads the text hex dump output of various TAOS
+ (Avaya/Alcatel/Lucent/Ascend Max, Max TNT, APX, etc) debug commands, including:
+
+ * pridisplay traces primary rate ISDN
+ * ether-display traces Ethernet packets (dangerous! CPU intensive)
+ * wanopening, wandisplay, wannext, wandsess
+ traces PPP or other WAN connections
+
+ Please see ascend_parser.lemon for examples.
+
+ Detailed documentation on TAOS products was at http://support.lucent.com;
+ that no longer works, and appears not to be available on the Wayback
+ Machine.
+
+ Some online manuals and other information include:
+
+ MAX Administration Guide:
+ https://downloads.avaya.com/elmodocs2/definity/def_r10_new/max/0678_002.pdf
+
+ Other MAX documentation:
+ https://support.avaya.com/products/P1192/max
+ https://web.archive.org/web/20201127014004/https://support.avaya.com/products/P1192/max#Tab4
+
+ Ascend Router Information:
+ http://maxrouter.rde.net/
+ https://web.archive.org/web/20200807215418/http://maxrouter.rde.net/
+
+ */
+
+typedef struct _ascend_magic_string {
+ guint type;
+ const gchar *strptr;
+ size_t strlength;
+} ascend_magic_string;
+
+/* 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_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"),
+};
+
+#define ASCEND_MAGIC_STRINGS G_N_ELEMENTS(ascend_magic)
+
+#define ASCEND_DATE "Date:"
+
+static gboolean ascend_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean ascend_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info);
+
+static int ascend_file_type_subtype = -1;
+
+void register_ascend(void);
+
+/* Seeks to the beginning of the next packet, and returns the
+ byte offset at which the header for that packet begins.
+ Returns -1 on failure. */
+static gint64 ascend_find_next_packet(wtap *wth, int *err, gchar **err_info)
+{
+ int byte;
+ gint64 date_off = -1, cur_off, packet_off;
+ size_t string_level[ASCEND_MAGIC_STRINGS];
+ guint string_i = 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--;
+
+ if (!excessive_read_count) {
+ *err = 0;
+ 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 = 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. */
+ *err = file_error(wth->fh, err_info);
+ return -1;
+ }
+
+ /* 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 {
+ /* This packet has a date/time header; a read of it should
+ start at the beginning of *that* header. */
+ packet_off = date_off;
+ }
+
+ goto found;
+ }
+ } else {
+ /*
+ * Not a match for this string, so reset the match process.
+ */
+ string_level[string_i] = 0;
+ }
+ }
+
+ /*
+ * 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:
+ /*
+ * Move to where the read for this packet should start, and return
+ * that seek offset.
+ */
+ if (file_seek(wth->fh, packet_off, SEEK_SET, err) == -1)
+ return -1;
+
+ return packet_off;
+}
+
+wtap_open_return_val ascend_open(wtap *wth, int *err, gchar **err_info)
+{
+ gint64 offset;
+ guint8 buf[ASCEND_MAX_PKT_LEN];
+ ascend_state_t parser_state = {0};
+ ws_statb64 statbuf;
+ ascend_t *ascend;
+ wtap_rec rec;
+
+ /* We haven't yet allocated a data structure for our private stuff;
+ set the pointer to null, so that "ascend_find_next_packet()" knows
+ not to fill it in. */
+ wth->priv = NULL;
+
+ offset = ascend_find_next_packet(wth, err, err_info);
+ if (offset == -1) {
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR; /* read error */
+ return WTAP_OPEN_NOT_MINE; /* EOF */
+ }
+
+ /* Do a trial parse of the first packet just found to see if we might
+ really have an Ascend file. If it fails with an actual error,
+ fail; those will be I/O errors. */
+ parser_state.fh = wth->fh;
+ parser_state.pseudo_header = &rec.rec_header.packet_header.pseudo_header.ascend;
+ if (run_ascend_parser(buf, &parser_state, err, err_info) != 0 && *err != 0) {
+ /* An I/O error. */
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Either the parse succeeded, or it failed but didn't get an I/O
+ error.
+
+ 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 == 0) {
+ /* We read no data, so this presumably isn't an Ascend file. */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ wth->file_type_subtype = ascend_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_ASCEND;
+
+ wth->snapshot_length = ASCEND_MAX_PKT_LEN;
+ wth->subtype_read = ascend_read;
+ wth->subtype_seek_read = ascend_seek_read;
+ ascend = g_new(ascend_t, 1);
+ wth->priv = (void *)ascend;
+
+ /* The first packet we want to read is the one that
+ "ascend_find_next_packet()" just found; start searching
+ for it at the offset at which it found it. */
+ ascend->next_packet_seek_start = offset;
+
+ /* MAXen and Pipelines report the time since reboot. In order to keep
+ from reporting packet times near the epoch, we subtract the first
+ packet's timestamp from the capture file's ctime, which gives us an
+ offset that we can apply to each packet.
+ */
+ if (wtap_fstat(wth, &statbuf, err) == -1) {
+ return WTAP_OPEN_ERROR;
+ }
+ ascend->inittime = statbuf.st_ctime;
+ ascend->adjusted = FALSE;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Parse the capture file.
+ Returns TRUE if we got a packet, FALSE otherwise. */
+static gboolean
+parse_ascend(ascend_t *ascend, FILE_T fh, wtap_rec *rec, Buffer *buf,
+ guint length, gint64 *next_packet_seek_start_ret,
+ int *err, gchar **err_info)
+{
+ ascend_state_t parser_state = {0};
+ int retval;
+
+ ws_buffer_assure_space(buf, length);
+ parser_state.fh = fh;
+ parser_state.pseudo_header = &rec->rec_header.packet_header.pseudo_header.ascend;
+
+ retval = run_ascend_parser(ws_buffer_start_ptr(buf), &parser_state, err, err_info);
+
+ /* Did we see any data (hex bytes)? */
+ if (parser_state.first_hexbyte) {
+ /* Yes. Provide the offset of the first byte so that our caller can
+ tip off ascend_find_next_packet() as to where to look for the next
+ packet, if any. */
+ if (next_packet_seek_start_ret != NULL)
+ *next_packet_seek_start_ret = parser_state.first_hexbyte;
+ } else {
+ /* No. Maybe this record was broken; 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. Provide our current offset so that our caller can tell
+ ascend_find_next_packet() to skip over what we've read so far so
+ we can try reading a new packet.
+
+ . That keeps us from getting into an infinite loop reading a broken
+ trace. */
+ if (next_packet_seek_start_ret != NULL)
+ *next_packet_seek_start_ret = file_tell(fh);
+
+ /* Don't treat that as a fatal error; pretend the parse succeeded. */
+ retval = 0;
+ }
+
+ /* 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;
+ }
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ rec->ts.secs = parser_state.secs + ascend->inittime;
+ rec->ts.nsecs = parser_state.usecs * 1000;
+ rec->rec_header.packet_header.caplen = parser_state.caplen;
+ rec->rec_header.packet_header.len = parser_state.wirelen;
+
+ return TRUE;
+ }
+
+ /* Didn't see any data. Still, perhaps the parser was happy. */
+ if (retval) {
+ if (*err == 0) {
+ /* Parser failed, but didn't report an I/O error, 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");
+ }
+ } else {
+ if (*err == 0) {
+ /* Parser succeeded, but got no data, and didn't report an I/O error.
+ Return WTAP_ERR_BAD_FILE, with a "got no data" error string. */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("no data returned by parse");
+ }
+ }
+ return FALSE;
+}
+
+/* Read the next packet; called from wtap_read(). */
+static gboolean ascend_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ ascend_t *ascend = (ascend_t *)wth->priv;
+ gint64 offset;
+
+ /* 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
+ start of the last packet's data). We have to get past the last
+ packet's header because we might mistake part of it for a new header. */
+ if (file_seek(wth->fh, ascend->next_packet_seek_start,
+ SEEK_SET, err) == -1)
+ return FALSE;
+
+ offset = ascend_find_next_packet(wth, err, err_info);
+ if (offset == -1) {
+ /* EOF or read error */
+ return FALSE;
+ }
+ if (!parse_ascend(ascend, wth->fh, rec, buf, wth->snapshot_length,
+ &ascend->next_packet_seek_start, err, err_info))
+ return FALSE;
+
+ /* Flex might have gotten an EOF and caused *err to be set to
+ WTAP_ERR_SHORT_READ. If so, that's not an error, as the parser
+ didn't return an error; set *err to 0, and get rid of any error
+ string. */
+ *err = 0;
+ if (*err_info != NULL) {
+ g_free(*err_info);
+ *err_info = NULL;
+ }
+ *data_offset = offset;
+ return TRUE;
+}
+
+static gboolean ascend_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, 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(ascend, wth->random_fh, rec, buf,
+ wth->snapshot_length, NULL, err, err_info))
+ return FALSE;
+
+ /* Flex might have gotten an EOF and caused *err to be set to
+ WTAP_ERR_SHORT_READ. If so, that's not an error, as the parser
+ didn't return an error; set *err to 0, and get rid of any error
+ string. */
+ *err = 0;
+ if (*err_info != NULL) {
+ g_free(*err_info);
+ *err_info = NULL;
+ }
+ return TRUE;
+}
+
+static const struct supported_block_type ascend_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info ascend_info = {
+ "Lucent/Ascend access server trace", "ascend", "txt", NULL,
+ FALSE, BLOCKS_SUPPORTED(ascend_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_ascend(void)
+{
+ ascend_file_type_subtype = wtap_register_file_type_subtype(&ascend_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("ASCEND",
+ ascend_file_type_subtype);
+}