summaryrefslogtreecommitdiffstats
path: root/wiretap/peekclassic.c
diff options
context:
space:
mode:
Diffstat (limited to 'wiretap/peekclassic.c')
-rw-r--r--wiretap/peekclassic.c746
1 files changed, 746 insertions, 0 deletions
diff --git a/wiretap/peekclassic.c b/wiretap/peekclassic.c
new file mode 100644
index 00000000..18b926dd
--- /dev/null
+++ b/wiretap/peekclassic.c
@@ -0,0 +1,746 @@
+/* peekclassic.c
+ * Routines for opening files in what Savvius (formerly WildPackets) calls
+ * the classic file format in the description of their "PeekRdr Sample
+ * Application" (C++ source code to read their capture files, downloading
+ * of which requires a maintenance contract, so it's not free as in beer
+ * and probably not as in speech, either).
+ *
+ * As that description says, it's used by AiroPeek and AiroPeek NX prior
+ * to 2.0, EtherPeek prior to 6.0, and EtherPeek NX prior to 3.0. It
+ * was probably also used by TokenPeek.
+ *
+ * This handles versions 5, 6, and 7 of that format (the format version
+ * number is what appears in the file, and is distinct from the application
+ * version number).
+ *
+ * Copyright (c) 2001, Daniel Thompson <d.thompson@gmx.net>
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <string.h>
+
+#include <wsutil/epochs.h>
+#include <wsutil/802_11-utils.h>
+#include <wsutil/ws_assert.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "peekclassic.h"
+
+/* CREDITS
+ *
+ * This file decoder could not have been written without examining how
+ * tcptrace (http://www.tcptrace.org/) handles EtherPeek files.
+ */
+
+/* master header */
+typedef struct peekclassic_master_header {
+ guint8 version;
+ guint8 status;
+} peekclassic_master_header_t;
+#define PEEKCLASSIC_MASTER_HDR_SIZE 2
+
+/* secondary header (V5,V6,V7) */
+typedef struct peekclassic_v567_header {
+ guint32 filelength;
+ guint32 numPackets;
+ guint32 timeDate;
+ guint32 timeStart;
+ guint32 timeStop;
+ guint32 mediaType; /* Media Type Ethernet=0 Token Ring = 1 */
+ guint32 physMedium; /* Physical Medium native=0 802.1=1 */
+ guint32 appVers; /* App Version Number Maj.Min.Bug.Build */
+ guint32 linkSpeed; /* Link Speed Bits/sec */
+ guint32 reserved[3];
+} peekclassic_v567_header_t;
+#define PEEKCLASSIC_V567_HDR_SIZE 48
+
+/* full header */
+typedef struct peekclassic_header {
+ peekclassic_master_header_t master;
+ union {
+ peekclassic_v567_header_t v567;
+ } secondary;
+} peekclassic_header_t;
+
+/*
+ * Packet header (V5, V6).
+ *
+ * NOTE: the time stamp, although it's a 32-bit number, is only aligned
+ * on a 16-bit boundary. (Does this date back to 68K Macs? The 68000
+ * only required 16-bit alignment of 32-bit quantities, as did the 68010,
+ * and the 68020/68030/68040 required no alignment.)
+ *
+ * As such, we cannot declare this as a C structure, as compilers on
+ * most platforms will put 2 bytes of padding before the time stamp to
+ * align it on a 32-bit boundary.
+ *
+ * So, instead, we #define numbers as the offsets of the fields.
+ */
+#define PEEKCLASSIC_V56_LENGTH_OFFSET 0
+#define PEEKCLASSIC_V56_SLICE_LENGTH_OFFSET 2
+#define PEEKCLASSIC_V56_FLAGS_OFFSET 4
+#define PEEKCLASSIC_V56_STATUS_OFFSET 5
+#define PEEKCLASSIC_V56_TIMESTAMP_OFFSET 6
+#define PEEKCLASSIC_V56_DESTNUM_OFFSET 10
+#define PEEKCLASSIC_V56_SRCNUM_OFFSET 12
+#define PEEKCLASSIC_V56_PROTONUM_OFFSET 14
+#define PEEKCLASSIC_V56_PROTOSTR_OFFSET 16
+#define PEEKCLASSIC_V56_FILTERNUM_OFFSET 24
+#define PEEKCLASSIC_V56_PKT_SIZE 26
+
+/* 64-bit time in micro seconds from the (Mac) epoch */
+typedef struct peekclassic_utime {
+ guint32 upper;
+ guint32 lower;
+} peekclassic_utime;
+
+/*
+ * Packet header (V7).
+ *
+ * This doesn't have the same alignment problem, but we do it with
+ * #defines anyway.
+ */
+#define PEEKCLASSIC_V7_PROTONUM_OFFSET 0
+#define PEEKCLASSIC_V7_LENGTH_OFFSET 2
+#define PEEKCLASSIC_V7_SLICE_LENGTH_OFFSET 4
+#define PEEKCLASSIC_V7_FLAGS_OFFSET 6
+#define PEEKCLASSIC_V7_STATUS_OFFSET 7
+#define PEEKCLASSIC_V7_TIMESTAMP_OFFSET 8
+#define PEEKCLASSIC_V7_PKT_SIZE 16
+
+/*
+ * Flag bits.
+ */
+#define FLAGS_CONTROL_FRAME 0x01 /* Frame is a control frame */
+#define FLAGS_HAS_CRC_ERROR 0x02 /* Frame has a CRC error */
+#define FLAGS_HAS_FRAME_ERROR 0x04 /* Frame has a frame error */
+#define FLAGS_ROUTE_INFO 0x08 /* Frame has token ring routing information */
+#define FLAGS_FRAME_TOO_LONG 0x10 /* Frame too long */
+#define FLAGS_FRAME_TOO_SHORT 0x20 /* Frame too short (runt) */
+#define FLAGS_TRIGGER 0x40 /* Trigger packet (?) */
+#define FLAGS_SNAP 0x80 /* SNAP packet (SNAP header?) */
+
+/*
+ * Status bits.
+ */
+#define STATUS_SELECTED 0x01 /* Selected (in the *Peek GUI?) */
+#define STATUS_TRUNCATED 0x02 /* Truncated (?) */
+#define STATUS_APPLEPEEK 0x10 /* ApplePeek packet (?) */
+#define STATUS_SLICED 0x20 /* Sliced (cut short by snaplen?) */
+#define STATUS_HIDDEN 0x80 /* Hidden (in the *Peek GUI?) */
+
+typedef struct {
+ time_t reference_time;
+} peekclassic_t;
+
+static gboolean peekclassic_read_v7(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean peekclassic_seek_read_v7(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static int peekclassic_read_packet_v7(wtap *wth, FILE_T fh,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean peekclassic_read_v56(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean peekclassic_seek_read_v56(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean peekclassic_read_packet_v56(wtap *wth, FILE_T fh,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+
+static int peekclassic_v56_file_type_subtype = -1;
+static int peekclassic_v7_file_type_subtype = -1;
+
+void register_peekclassic(void);
+
+wtap_open_return_val peekclassic_open(wtap *wth, int *err, gchar **err_info)
+{
+ peekclassic_header_t ep_hdr;
+ time_t reference_time;
+ int file_encap;
+ peekclassic_t *peekclassic;
+
+ /* Peek classic files do not start with a magic value large enough
+ * to be unique; hence we use the following algorithm to determine
+ * the type of an unknown file:
+ * - populate the master header and reject file if there is no match
+ * - populate the secondary header and check that the reserved space
+ * is zero, and check some other fields; this isn't perfect,
+ * and we may have to add more checks at some point.
+ */
+ ws_assert(sizeof(ep_hdr.master) == PEEKCLASSIC_MASTER_HDR_SIZE);
+ if (!wtap_read_bytes(wth->fh, &ep_hdr.master,
+ (int)sizeof(ep_hdr.master), err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * It appears that EtherHelp (a free application from WildPackets
+ * that did blind capture, saving to a file, so that you could
+ * give the resulting file to somebody with EtherPeek) saved
+ * captures in EtherPeek format except that it ORed the 0x80
+ * bit on in the version number.
+ *
+ * We therefore strip off the 0x80 bit in the version number.
+ * Perhaps there's some reason to care whether the capture
+ * came from EtherHelp; if we discover one, we should check
+ * that bit.
+ */
+ ep_hdr.master.version &= ~0x80;
+
+ /* switch on the file version */
+ switch (ep_hdr.master.version) {
+
+ case 5:
+ case 6:
+ case 7:
+ /* get the secondary header */
+ ws_assert(sizeof(ep_hdr.secondary.v567) ==
+ PEEKCLASSIC_V567_HDR_SIZE);
+ if (!wtap_read_bytes(wth->fh, &ep_hdr.secondary.v567,
+ (int)sizeof(ep_hdr.secondary.v567), err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if ((0 != ep_hdr.secondary.v567.reserved[0]) ||
+ (0 != ep_hdr.secondary.v567.reserved[1]) ||
+ (0 != ep_hdr.secondary.v567.reserved[2])) {
+ /* still unknown */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * Check the mediaType and physMedium fields.
+ * We assume it's not a Peek classic file if
+ * these aren't values we know, rather than
+ * reporting them as invalid Peek classic files,
+ * as, given the lack of a magic number, we need
+ * all the checks we can get.
+ */
+ ep_hdr.secondary.v567.mediaType =
+ g_ntohl(ep_hdr.secondary.v567.mediaType);
+ ep_hdr.secondary.v567.physMedium =
+ g_ntohl(ep_hdr.secondary.v567.physMedium);
+
+ switch (ep_hdr.secondary.v567.physMedium) {
+
+ case 0:
+ /*
+ * "Native" format, presumably meaning
+ * Ethernet or Token Ring.
+ */
+ switch (ep_hdr.secondary.v567.mediaType) {
+
+ case 0:
+ file_encap = WTAP_ENCAP_ETHERNET;
+ break;
+
+ case 1:
+ file_encap = WTAP_ENCAP_TOKEN_RING;
+ break;
+
+ default:
+ /*
+ * Assume this isn't a Peek classic file.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+ break;
+
+ case 1:
+ switch (ep_hdr.secondary.v567.mediaType) {
+
+ case 0:
+ /*
+ * 802.11, with a private header giving
+ * some radio information. Presumably
+ * this is from AiroPeek.
+ */
+ file_encap = WTAP_ENCAP_IEEE_802_11_WITH_RADIO;
+ break;
+
+ default:
+ /*
+ * Assume this isn't a Peek classic file.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+ break;
+
+ default:
+ /*
+ * Assume this isn't a Peek classic file.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+
+ /*
+ * Assume this is a V5, V6 or V7 Peek classic file, and
+ * byte swap the rest of the fields in the secondary header.
+ *
+ * XXX - we could check the file length if the file were
+ * uncompressed, but it might be compressed.
+ */
+ ep_hdr.secondary.v567.filelength =
+ g_ntohl(ep_hdr.secondary.v567.filelength);
+ ep_hdr.secondary.v567.numPackets =
+ g_ntohl(ep_hdr.secondary.v567.numPackets);
+ ep_hdr.secondary.v567.timeDate =
+ g_ntohl(ep_hdr.secondary.v567.timeDate);
+ ep_hdr.secondary.v567.timeStart =
+ g_ntohl(ep_hdr.secondary.v567.timeStart);
+ ep_hdr.secondary.v567.timeStop =
+ g_ntohl(ep_hdr.secondary.v567.timeStop);
+ ep_hdr.secondary.v567.appVers =
+ g_ntohl(ep_hdr.secondary.v567.appVers);
+ ep_hdr.secondary.v567.linkSpeed =
+ g_ntohl(ep_hdr.secondary.v567.linkSpeed);
+
+ /* Get the reference time as a time_t */
+ reference_time = ep_hdr.secondary.v567.timeDate - EPOCH_DELTA_1904_01_01_00_00_00_UTC;
+ break;
+
+ default:
+ /*
+ * Assume this isn't a Peek classic file.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * This is a Peek classic file.
+ *
+ * At this point we have recognised the file type and have populated
+ * the whole ep_hdr structure in host byte order.
+ */
+ peekclassic = g_new(peekclassic_t, 1);
+ wth->priv = (void *)peekclassic;
+ peekclassic->reference_time = reference_time;
+ wth->file_encap = file_encap;
+ switch (ep_hdr.master.version) {
+
+ case 5:
+ case 6:
+ wth->file_type_subtype = peekclassic_v56_file_type_subtype;
+ wth->subtype_read = peekclassic_read_v56;
+ wth->subtype_seek_read = peekclassic_seek_read_v56;
+ break;
+
+ case 7:
+ wth->file_type_subtype = peekclassic_v7_file_type_subtype;
+ wth->subtype_read = peekclassic_read_v7;
+ wth->subtype_seek_read = peekclassic_seek_read_v7;
+ break;
+
+ default:
+ /* this is impossible */
+ ws_assert_not_reached();
+ }
+
+ wth->snapshot_length = 0; /* not available in header */
+ 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;
+}
+
+static gboolean peekclassic_read_v7(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ int sliceLength;
+
+ *data_offset = file_tell(wth->fh);
+
+ /* Read the packet. */
+ sliceLength = peekclassic_read_packet_v7(wth, wth->fh, rec, buf,
+ err, err_info);
+ if (sliceLength < 0)
+ return FALSE;
+
+ /* Skip extra ignored data at the end of the packet. */
+ if ((guint32)sliceLength > rec->rec_header.packet_header.caplen) {
+ if (!wtap_read_bytes(wth->fh, NULL, sliceLength - rec->rec_header.packet_header.caplen,
+ err, err_info))
+ return FALSE;
+ }
+
+ /* Records are padded to an even length, so if the slice length
+ is odd, read the padding byte. */
+ if (sliceLength & 0x01) {
+ if (!wtap_read_bytes(wth->fh, NULL, 1, err, err_info))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean peekclassic_seek_read_v7(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* Read the packet. */
+ if (peekclassic_read_packet_v7(wth, wth->random_fh, rec, buf,
+ err, err_info) == -1) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#define RADIO_INFO_SIZE 4
+
+static int peekclassic_read_packet_v7(wtap *wth, FILE_T fh,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ guint8 ep_pkt[PEEKCLASSIC_V7_PKT_SIZE];
+#if 0
+ guint16 protoNum;
+#endif
+ guint16 length;
+ guint16 sliceLength;
+ guint8 flags;
+ guint8 status;
+ guint64 timestamp;
+ time_t tsecs;
+ guint32 tusecs;
+ guint32 pack_flags;
+ guint8 radio_info[RADIO_INFO_SIZE];
+
+ if (!wtap_read_bytes_or_eof(fh, ep_pkt, sizeof(ep_pkt), err, err_info))
+ return -1;
+
+ /* Extract the fields from the packet */
+#if 0
+ protoNum = pntoh16(&ep_pkt[PEEKCLASSIC_V7_PROTONUM_OFFSET]);
+#endif
+ length = pntoh16(&ep_pkt[PEEKCLASSIC_V7_LENGTH_OFFSET]);
+ sliceLength = pntoh16(&ep_pkt[PEEKCLASSIC_V7_SLICE_LENGTH_OFFSET]);
+ flags = ep_pkt[PEEKCLASSIC_V7_FLAGS_OFFSET];
+ status = ep_pkt[PEEKCLASSIC_V7_STATUS_OFFSET];
+ timestamp = pntoh64(&ep_pkt[PEEKCLASSIC_V7_TIMESTAMP_OFFSET]);
+
+ /* force sliceLength to be the actual length of the packet */
+ if (0 == sliceLength) {
+ sliceLength = length;
+ }
+ /*
+ * The maximum value of sliceLength and length are 65535, which
+ * are less than WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't
+ * need to check them.
+ */
+
+ /* fill in packet header values */
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ tsecs = (time_t) (timestamp/1000000);
+ tusecs = (guint32) (timestamp - tsecs*1000000);
+ rec->ts.secs = tsecs - EPOCH_DELTA_1904_01_01_00_00_00_UTC;
+ rec->ts.nsecs = tusecs * 1000;
+ rec->rec_header.packet_header.len = length;
+ rec->rec_header.packet_header.caplen = sliceLength;
+ pack_flags = 0;
+ if (flags & FLAGS_HAS_CRC_ERROR)
+ pack_flags |= PACK_FLAGS_CRC_ERROR;
+ if (flags & FLAGS_FRAME_TOO_LONG)
+ pack_flags |= PACK_FLAGS_PACKET_TOO_LONG;
+ if (flags & FLAGS_FRAME_TOO_SHORT)
+ pack_flags |= PACK_FLAGS_PACKET_TOO_SHORT;
+ wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, pack_flags);
+
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
+ memset(&rec->rec_header.packet_header.pseudo_header.ieee_802_11, 0, sizeof(rec->rec_header.packet_header.pseudo_header.ieee_802_11));
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.fcs_len = 0; /* no FCS */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.decrypted = FALSE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.datapad = FALSE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_UNKNOWN;
+
+ /*
+ * Now process the radio information pseudo-header.
+ * It's a 4-byte pseudo-header, consisting of:
+ *
+ * 1 byte of data rate, in units of 500 kb/s;
+ *
+ * 1 byte of channel number;
+ *
+ * 1 byte of signal strength as a percentage of
+ * the maximum, i.e. (RXVECTOR RSSI/RXVECTOR RSSI_Max)*100,
+ * or, at least, that's what I infer it is, given what
+ * the WildPackets note "Converting Signal Strength
+ * Percentage to dBm Values" says (it also says that
+ * the conversion the percentage to a dBm value is
+ * an adapter-dependent process, so, as we don't know
+ * what type of adapter was used to do the capture,
+ * we can't do the conversion);
+ *
+ * 1 byte of unknown content (padding?).
+ */
+ if (rec->rec_header.packet_header.len < RADIO_INFO_SIZE || rec->rec_header.packet_header.caplen < RADIO_INFO_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("peekclassic: 802.11 packet has length < 4");
+ return -1;
+ }
+ rec->rec_header.packet_header.len -= RADIO_INFO_SIZE;
+ rec->rec_header.packet_header.caplen -= RADIO_INFO_SIZE;
+ sliceLength -= RADIO_INFO_SIZE;
+
+ /* read the pseudo-header */
+ if (!wtap_read_bytes(fh, radio_info, RADIO_INFO_SIZE, err, err_info))
+ return -1;
+
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_data_rate = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate = radio_info[0];
+
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_channel = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.channel = radio_info[1];
+
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_percent = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_percent = radio_info[2];
+
+ /*
+ * We don't know they PHY, but we do have the data rate;
+ * try to guess it based on the data rate and channel.
+ */
+ if (RATE_IS_DSSS(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate)) {
+ /* 11b */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11B;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11b.has_short_preamble = FALSE;
+ } else if (RATE_IS_OFDM(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate)) {
+ /* 11a or 11g, depending on the band. */
+ if (CHAN_IS_BG(rec->rec_header.packet_header.pseudo_header.ieee_802_11.channel)) {
+ /* 11g */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11G;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.has_mode = FALSE;
+ } else {
+ /* 11a */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11A;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.has_channel_type = FALSE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.has_turbo_type = FALSE;
+ }
+ }
+
+ /*
+ * The last 4 bytes appear to be random data - the length
+ * might include the FCS - so we reduce the length by 4.
+ *
+ * Or maybe this is just the same kind of random 4 bytes
+ * of junk at the end you get in Wireless Sniffer
+ * captures.
+ */
+ if (rec->rec_header.packet_header.len < 4 || rec->rec_header.packet_header.caplen < 4) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("peekclassic: 802.11 packet has length < 8");
+ return -1;
+ }
+ rec->rec_header.packet_header.len -= 4;
+ rec->rec_header.packet_header.caplen -= 4;
+ break;
+
+ case WTAP_ENCAP_ETHERNET:
+ /* XXX - it appears that if the low-order bit of
+ "status" is 0, there's an FCS in this frame,
+ and if it's 1, there's 4 bytes of 0. */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = (status & 0x01) ? 0 : 4;
+ break;
+ }
+
+ /* read the packet data */
+ if (!wtap_read_packet_bytes(fh, buf, rec->rec_header.packet_header.caplen, err, err_info))
+ return -1;
+
+ return sliceLength;
+}
+
+static gboolean peekclassic_read_v56(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ /* read the packet */
+ if (!peekclassic_read_packet_v56(wth, wth->fh, rec, buf,
+ err, err_info))
+ return FALSE;
+
+ /*
+ * XXX - is the captured packet data padded to a multiple
+ * of 2 bytes?
+ */
+ return TRUE;
+}
+
+static gboolean peekclassic_seek_read_v56(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* read the packet */
+ if (!peekclassic_read_packet_v56(wth, wth->random_fh, rec, buf,
+ err, err_info)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean peekclassic_read_packet_v56(wtap *wth, FILE_T fh,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ peekclassic_t *peekclassic = (peekclassic_t *)wth->priv;
+ guint8 ep_pkt[PEEKCLASSIC_V56_PKT_SIZE];
+ guint16 length;
+ guint16 sliceLength;
+ guint8 flags;
+#if 0
+ guint8 status;
+#endif
+ guint32 timestamp;
+#if 0
+ guint16 destNum;
+ guint16 srcNum;
+#endif
+#if 0
+ guint16 protoNum;
+ char protoStr[8];
+#endif
+ guint32 pack_flags;
+
+ if (!wtap_read_bytes_or_eof(fh, ep_pkt, sizeof(ep_pkt), err, err_info))
+ return FALSE;
+
+ /* Extract the fields from the packet */
+ length = pntoh16(&ep_pkt[PEEKCLASSIC_V56_LENGTH_OFFSET]);
+ sliceLength = pntoh16(&ep_pkt[PEEKCLASSIC_V56_SLICE_LENGTH_OFFSET]);
+ flags = ep_pkt[PEEKCLASSIC_V56_FLAGS_OFFSET];
+#if 0
+ status = ep_pkt[PEEKCLASSIC_V56_STATUS_OFFSET];
+#endif
+ timestamp = pntoh32(&ep_pkt[PEEKCLASSIC_V56_TIMESTAMP_OFFSET]);
+#if 0
+ destNum = pntoh16(&ep_pkt[PEEKCLASSIC_V56_DESTNUM_OFFSET]);
+ srcNum = pntoh16(&ep_pkt[PEEKCLASSIC_V56_SRCNUM_OFFSET]);
+ protoNum = pntoh16(&ep_pkt[PEEKCLASSIC_V56_PROTONUM_OFFSET]);
+ memcpy(protoStr, &ep_pkt[PEEKCLASSIC_V56_PROTOSTR_OFFSET],
+ sizeof protoStr);
+#endif
+
+ /*
+ * XXX - is the captured packet data padded to a multiple
+ * of 2 bytes?
+ */
+
+ /* force sliceLength to be the actual length of the packet */
+ if (0 == sliceLength) {
+ sliceLength = length;
+ }
+ /*
+ * The maximum value of sliceLength and length are 65535, which
+ * are less than WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't
+ * need to check them.
+ */
+
+ /* fill in packet header values */
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ /* timestamp is in milliseconds since reference_time */
+ rec->ts.secs = peekclassic->reference_time + (timestamp / 1000);
+ rec->ts.nsecs = 1000 * (timestamp % 1000) * 1000;
+ rec->rec_header.packet_header.len = length;
+ rec->rec_header.packet_header.caplen = sliceLength;
+ pack_flags = 0;
+ if (flags & FLAGS_HAS_CRC_ERROR)
+ pack_flags |= PACK_FLAGS_CRC_ERROR;
+ if (flags & FLAGS_FRAME_TOO_LONG)
+ pack_flags |= PACK_FLAGS_PACKET_TOO_LONG;
+ if (flags & FLAGS_FRAME_TOO_SHORT)
+ pack_flags |= PACK_FLAGS_PACKET_TOO_SHORT;
+ wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, pack_flags);
+
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_ETHERNET:
+ /* We assume there's no FCS in this frame. */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
+ break;
+ }
+
+ /* read the packet data */
+ return wtap_read_packet_bytes(fh, buf, sliceLength, err, err_info);
+}
+
+static const struct supported_block_type peekclassic_v56_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 peekclassic_v56_info = {
+ "Savvius classic (V5 and V6)", "peekclassic56", "pkt", "tpc;apc;wpz",
+ FALSE, BLOCKS_SUPPORTED(peekclassic_v56_blocks_supported),
+ NULL, NULL, NULL
+};
+
+static const struct supported_block_type peekclassic_v7_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 peekclassic_v7_info = {
+ "Savvius classic (V7)", "peekclassic7", "pkt", "tpc;apc;wpz",
+ FALSE, BLOCKS_SUPPORTED(peekclassic_v7_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_peekclassic(void)
+{
+ peekclassic_v56_file_type_subtype = wtap_register_file_type_subtype(&peekclassic_v56_info);
+ peekclassic_v7_file_type_subtype = wtap_register_file_type_subtype(&peekclassic_v7_info);
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("PEEKCLASSIC_V56",
+ peekclassic_v56_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("PEEKCLASSIC_V7",
+ peekclassic_v7_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */