Add MP3 to the list of magic types
[obnox/wireshark/wip.git] / wiretap / etherpeek.c
index 1f9f18f3741f86e456a648dc1497604b55376b1e..e7202f58a2329bbb790874ce482913bdc9e5b7a3 100644 (file)
@@ -1,26 +1,26 @@
 /* etherpeek.c
- * Routines for opening etherpeek files
+ * Routines for opening EtherPeek and AiroPeek (and TokenPeek?) V5, V6,
+ * and V7 files
  * Copyright (c) 2001, Daniel Thompson <d.thompson@gmx.net>
  *
- * $Id: etherpeek.c,v 1.5 2001/10/04 08:30:35 guy Exp $
+ * $Id$
  *
  * Wiretap Library
- * Copyright (c) 1998 by Gilbert Ramirez <gram@xiexie.org>
- * 
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
- * 
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * 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.
- *
  */
 
 #ifdef HAVE_CONFIG_H
 #include "file_wrappers.h"
 #include "buffer.h"
 #include "etherpeek.h"
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
 /* CREDITS
  *
  * This file decoder could not have been writen without examining how
- * tcptrace (http://www.tcptrace.org/) handles etherpeek files.
+ * tcptrace (http://www.tcptrace.org/) handles EtherPeek files.
+ */
+
+/*
+ * NOTE: it says "etherpeek" because the first files seen that use this
+ * format were EtherPeek files; however, AiroPeek files using it have
+ * also been seen, and I suspect TokenPeek uses it as well.
  */
 
 /* master header */
@@ -49,38 +51,54 @@ typedef struct etherpeek_master_header {
 } etherpeek_master_header_t;
 #define ETHERPEEK_MASTER_HDR_SIZE 2
 
-/* secondary header (Mac V5,V6,V7) */
-typedef struct etherpeek_m567_header {
+/* secondary header (V5,V6,V7) */
+typedef struct etherpeek_v567_header {
        guint32 filelength;
        guint32 numPackets;
        guint32 timeDate;
        guint32 timeStart;
        guint32 timeStop;
-       guint32 reserved[7];
-} etherpeek_m567_header_t;
-#define ETHERPEEK_M567_HDR_SIZE 48
+       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];
+} etherpeek_v567_header_t;
+#define ETHERPEEK_V567_HDR_SIZE 48
 
 /* full header */
 typedef struct etherpeek_header {
        etherpeek_master_header_t master;
        union {
-               etherpeek_m567_header_t m567;
+               etherpeek_v567_header_t v567;
        } secondary;
 } etherpeek_header_t;
 
-/* packet header (Mac V5, V6) */
-typedef struct etherpeek_m56_packet {
-       guint16 length;
-       guint16 sliceLength;
-       guint8  flags;
-       guint8  status;
-       guint32 timestamp;
-       guint16 destNum;
-       guint16 srcNum;
-       guint16 protoNum;
-       char    protoStr[8];
-} etherpeek_m56_packet_t;
-#define ETHERPEEK_M56_PKT_SIZE 24
+/*
+ * 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 ETHERPEEK_V56_LENGTH_OFFSET            0
+#define ETHERPEEK_V56_SLICE_LENGTH_OFFSET      2
+#define ETHERPEEK_V56_FLAGS_OFFSET             4
+#define ETHERPEEK_V56_STATUS_OFFSET            5
+#define ETHERPEEK_V56_TIMESTAMP_OFFSET         6
+#define ETHERPEEK_V56_DESTNUM_OFFSET           10
+#define ETHERPEEK_V56_SRCNUM_OFFSET            12
+#define ETHERPEEK_V56_PROTONUM_OFFSET          14
+#define ETHERPEEK_V56_PROTOSTR_OFFSET          16
+#define ETHERPEEK_V56_FILTERNUM_OFFSET         24
+#define ETHERPEEK_V56_PKT_SIZE                 26
 
 /* 64-bit time in micro seconds from the (Mac) epoch */
 typedef struct etherpeek_utime {
@@ -88,17 +106,29 @@ typedef struct etherpeek_utime {
        guint32 lower;
 } etherpeek_utime;
 
-/* packet header (Mac V7) */
-typedef struct etherpeek_m7_packet {
-       guint16 protoNum;
-       guint16 length;
-       guint16 sliceLength;
-       guint8  flags;
-       guint8  status;
-       etherpeek_utime
-               timestamp;
-} etherpeek_m7_packet_t;
-#define ETHERPEEK_M7_PKT_SIZE 16
+/*
+ * Packet header (V7).
+ *
+ * This doesn't have the same alignment problem, but we do it with
+ * #defines anyway.
+ */
+#define ETHERPEEK_V7_PROTONUM_OFFSET           0
+#define ETHERPEEK_V7_LENGTH_OFFSET             2
+#define ETHERPEEK_V7_SLICE_LENGTH_OFFSET       4
+#define ETHERPEEK_V7_FLAGS_OFFSET              6
+#define ETHERPEEK_V7_STATUS_OFFSET             7
+#define ETHERPEEK_V7_TIMESTAMP_OFFSET          8
+#define ETHERPEEK_V7_PKT_SIZE                  16
+
+/*
+ * AiroPeek radio information, at the beginning of every packet.
+ */
+typedef struct {
+       guint8  data_rate;
+       guint8  channel;
+       guint8  signal_level;
+       guint8  unused;
+} airopeek_radio_hdr_t;
 
 typedef struct etherpeek_encap_lookup {
        guint16 protoNum;
@@ -112,200 +142,515 @@ static const etherpeek_encap_lookup_t etherpeek_encap[] = {
 #define NUM_ETHERPEEK_ENCAPS \
        (sizeof (etherpeek_encap) / sizeof (etherpeek_encap[0]))
 
-static gboolean etherpeek_read_m7(wtap *wth, int *err, long *data_offset);
-static gboolean etherpeek_read_m56(wtap *wth, int *err, long *data_offset);
-
-int etherpeek_open(wtap *wth, int *err)
+static gboolean etherpeek_read_v7(wtap *wth, int *err, gchar **err_info,
+    gint64 *data_offset);
+static gboolean etherpeek_seek_read_v7(wtap *wth, gint64 seek_off,
+    union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
+    int *err, gchar **err_info);
+static void etherpeek_fill_pseudo_header_v7(
+    union wtap_pseudo_header *pseudo_header, airopeek_radio_hdr_t *radio_hdr);
+static gboolean etherpeek_read_v56(wtap *wth, int *err, gchar **err_info,
+    gint64 *data_offset);
+static gboolean etherpeek_seek_read_v56(wtap *wth, gint64 seek_off,
+    union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
+    int *err, gchar **err_info);
+static void etherpeek_close(wtap *wth);
+
+int etherpeek_open(wtap *wth, int *err, gchar **err_info _U_)
 {
        etherpeek_header_t ep_hdr;
+       struct timeval reference_time;
+       int file_encap;
 
-       /* etherpeek files to 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
+       /* EtherPeek 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; there is an obvious flaw here so this algorithm will
-        *      probably need to be revisiting when improving etherpeek
-        *      support
+        *      is zero, and check some other fields; this isn't perfect,
+        *      and we may have to add more checks at some point.
         */
-       
        g_assert(sizeof(ep_hdr.master) == ETHERPEEK_MASTER_HDR_SIZE);
        wtap_file_read_unknown_bytes(
                &ep_hdr.master, sizeof(ep_hdr.master), wth->fh, err);
        wth->data_offset += sizeof(ep_hdr.master);
 
+       /*
+        * 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 */
-                       g_assert(sizeof(ep_hdr.secondary.m567) ==
-                               ETHERPEEK_M567_HDR_SIZE);
-                       wtap_file_read_unknown_bytes(
-                               &ep_hdr.secondary.m567,
-                               sizeof(ep_hdr.secondary.m567), wth->fh, err);
-                       wth->data_offset += sizeof(ep_hdr.secondary.m567);
-                       
-                       if ((0 != ep_hdr.secondary.m567.reserved[0]) ||
-                           (0 != ep_hdr.secondary.m567.reserved[1]) ||
-                           (0 != ep_hdr.secondary.m567.reserved[2]) ||
-                           (0 != ep_hdr.secondary.m567.reserved[3])) {
-                               /* still unknown */
+
+       case 5:
+       case 6:
+       case 7:
+               /* get the secondary header */
+               g_assert(sizeof(ep_hdr.secondary.v567) ==
+                       ETHERPEEK_V567_HDR_SIZE);
+               wtap_file_read_unknown_bytes(
+                       &ep_hdr.secondary.v567,
+                       sizeof(ep_hdr.secondary.v567), wth->fh, err);
+               wth->data_offset += sizeof(ep_hdr.secondary.v567);
+
+               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 0;
+               }
+
+               /*
+                * Check the mediaType and physMedium fields.
+                * We assume it's not an EtherPeek/TokenPeek/AiroPeek
+                * file if these aren't values we know, rather than
+                * reporting them as invalid *Peek 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 file.
+                                */
                                return 0;
                        }
+                       break;
 
-                       /* we have a match for a Mac V5, V6 or V7,
-                        * so it is worth preforming byte swaps
-                        */
-                       ep_hdr.secondary.m567.filelength =
-                               ntohl(ep_hdr.secondary.m567.filelength);
-                       ep_hdr.secondary.m567.numPackets =
-                               ntohl(ep_hdr.secondary.m567.numPackets);
-                       ep_hdr.secondary.m567.timeDate =
-                               ntohl(ep_hdr.secondary.m567.timeDate);
-                       ep_hdr.secondary.m567.timeStart =
-                               ntohl(ep_hdr.secondary.m567.timeStart);
-                       ep_hdr.secondary.m567.timeStop =
-                               ntohl(ep_hdr.secondary.m567.timeStop);
-
-                       /* populate the pseudo header */
-                       wth->pseudo_header.etherpeek.reference_time.tv_sec  =
-                               ep_hdr.secondary.m567.timeDate - mac2unix;
-                       wth->pseudo_header.etherpeek.reference_time.tv_usec =
-                               0;
+               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.
+                                *
+                                * We supply the private header as
+                                * the WTAP_ENCAP_IEEE_802_11_WITH_RADIO
+                                * pseudo-header, rather than as frame
+                                * data.
+                                */
+                               file_encap = WTAP_ENCAP_IEEE_802_11_WITH_RADIO;
+                               break;
+
+                       default:
+                               /*
+                                * Assume this isn't a *Peek file.
+                                */
+                               return 0;
+                       }
                        break;
+
                default:
+                       /*
+                        * Assume this isn't a *Peek file.
+                        */
                        return 0;
+               }
+
+
+               /*
+                * Assume this is a V5, V6 or V7 *Peek 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 "struct timeval" */
+               reference_time.tv_sec  =
+                   ep_hdr.secondary.v567.timeDate - mac2unix;
+               reference_time.tv_usec = 0;
+               break;
+
+       default:
+               /*
+                * Assume this isn't a *Peek file.
+                */
+               return 0;
        }
 
-       /* at this point we have recognised the file type and have populated
-        * the whole ep_hdr structure in host byte order
+       /*
+        * This is an EtherPeek (or TokenPeek or AiroPeek?) file.
+        *
+        * At this point we have recognised the file type and have populated
+        * the whole ep_hdr structure in host byte order.
         */
-       
+       wth->capture.etherpeek = g_malloc(sizeof(etherpeek_t));
+       wth->capture.etherpeek->reference_time = reference_time;
+       wth->subtype_close = etherpeek_close;
        switch (ep_hdr.master.version) {
-               case 5:
-               case 6:
-                       wth->file_type = WTAP_FILE_ETHERPEEK_MAC_V56;
-                       wth->subtype_read = etherpeek_read_m56;
-                       wth->subtype_seek_read = wtap_def_seek_read;
-                       break;
-               case 7:
-                       wth->file_type = WTAP_FILE_ETHERPEEK_MAC_V7;
-                       wth->subtype_read = etherpeek_read_m7;
-                       wth->subtype_seek_read = wtap_def_seek_read;
-                       break;
-               default:
-                       /* this is impossible */
-                       g_assert_not_reached();
-       };
 
-       wth->file_encap        = WTAP_ENCAP_PER_PACKET;
-       wth->snapshot_length   = 16384; /* just guessing */
+       case 5:
+       case 6:
+               wth->file_type = WTAP_FILE_ETHERPEEK_V56;
+               /*
+                * XXX - can we get the file encapsulation from the
+                * header in the same way we do for V7 files?
+                */
+               wth->file_encap = WTAP_ENCAP_PER_PACKET;
+               wth->subtype_read = etherpeek_read_v56;
+               wth->subtype_seek_read = etherpeek_seek_read_v56;
+               break;
+
+       case 7:
+               wth->file_type = WTAP_FILE_ETHERPEEK_V7;
+               wth->file_encap = file_encap;
+               wth->subtype_read = etherpeek_read_v7;
+               wth->subtype_seek_read = etherpeek_seek_read_v7;
+               break;
+
+       default:
+               /* this is impossible */
+               g_assert_not_reached();
+       }
+
+       wth->snapshot_length   = 0; /* not available in header */
+    wth->tsprecision = WTAP_FILE_TSPREC_USEC;
 
        return 1;
 }
 
-static gboolean etherpeek_read_m7(wtap *wth, int *err, long *data_offset)
+static void etherpeek_close(wtap *wth)
 {
-       etherpeek_m7_packet_t ep_pkt;
-       double  t;
-       unsigned int i;
+       g_free(wth->capture.etherpeek);
+}
+
+static gboolean etherpeek_read_v7(wtap *wth, int *err, gchar **err_info,
+    gint64 *data_offset)
+{
+       guchar ep_pkt[ETHERPEEK_V7_PKT_SIZE];
+       guint16 protoNum;
+       guint16 length;
+       guint16 sliceLength;
+       guint8  flags;
+       guint8  status;
+       guint64 timestamp;
+       time_t tsecs;
+       guint32 tusecs;
+       airopeek_radio_hdr_t radio_hdr;
+
+       *data_offset = wth->data_offset;
 
-       g_assert(sizeof(ep_pkt) == ETHERPEEK_M7_PKT_SIZE);
-       wtap_file_read_expected_bytes(&ep_pkt, sizeof(ep_pkt), wth->fh, err);
+       wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), wth->fh, err);
        wth->data_offset += sizeof(ep_pkt);
 
-       /* byte swaps */
-       ep_pkt.protoNum = ntohs(ep_pkt.protoNum);
-       ep_pkt.length = ntohs(ep_pkt.length);
-       ep_pkt.sliceLength = ntohs(ep_pkt.sliceLength);
-       ep_pkt.timestamp.upper = ntohl(ep_pkt.timestamp.upper);
-       ep_pkt.timestamp.lower = ntohl(ep_pkt.timestamp.lower);
+       /* Extract the fields from the packet */
+       protoNum = pntohs(&ep_pkt[ETHERPEEK_V7_PROTONUM_OFFSET]);
+       length = pntohs(&ep_pkt[ETHERPEEK_V7_LENGTH_OFFSET]);
+       sliceLength = pntohs(&ep_pkt[ETHERPEEK_V7_SLICE_LENGTH_OFFSET]);
+       flags = ep_pkt[ETHERPEEK_V7_FLAGS_OFFSET];
+       status = ep_pkt[ETHERPEEK_V7_STATUS_OFFSET];
+       timestamp = pntohll(&ep_pkt[ETHERPEEK_V7_TIMESTAMP_OFFSET]);
 
        /* force sliceLength to be the actual length of the packet */
-       if (0 == ep_pkt.sliceLength) {
-               ep_pkt.sliceLength = ep_pkt.length;
+       if (0 == sliceLength) {
+               sliceLength = length;
        }
 
-       /* test for corrupt data */
-       if (ep_pkt.sliceLength > WTAP_MAX_PACKET_SIZE) {
-               *err = WTAP_ERR_BAD_RECORD;
-               return FALSE;
-       }
+       /* fill in packet header length values before slicelength may be
+          adjusted */
+       wth->phdr.len    = length;
+       wth->phdr.caplen = sliceLength;
 
-       *data_offset = wth->data_offset;
+       if (sliceLength % 2) /* packets are padded to an even length */
+               sliceLength++;
+
+       switch (wth->file_encap) {
+
+       case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
+               /*
+                * The first 4 bytes of the packet data are radio
+                * information (including a reserved byte).
+                */
+               if (sliceLength < 4) {
+                       /*
+                        * We don't *have* 4 bytes of packet data.
+                        */
+                       *err = WTAP_ERR_BAD_RECORD;
+                       *err_info = g_strdup("etherpeek: packet not long enough for 802.11 radio header");
+                       return FALSE;
+               }
+               wtap_file_read_expected_bytes(&radio_hdr, 4, wth->fh, err);
+
+               /*
+                * We don't treat the radio information as packet data.
+                */
+               sliceLength -= 4;
+               wth->phdr.len -= 4;
+               wth->phdr.caplen -= 4;
+               wth->data_offset += 4;
+
+               etherpeek_fill_pseudo_header_v7(&wth->pseudo_header,
+                   &radio_hdr);
+               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. */
+               wth->pseudo_header.eth.fcs_len = (status & 0x01) ? 0 : 4;
+               break;
+       }
 
        /* read the frame data */
-       buffer_assure_space(wth->frame_buffer, ep_pkt.sliceLength);
+       buffer_assure_space(wth->frame_buffer, sliceLength);
        wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
-                                     ep_pkt.sliceLength, wth->fh, err);
-       wth->data_offset += ep_pkt.sliceLength;
-       
+                                     sliceLength, wth->fh, err);
+       wth->data_offset += sliceLength;
+
        /* fill in packet header values */
-       wth->phdr.len    = ep_pkt.length;
-       wth->phdr.caplen = ep_pkt.sliceLength;
-       
-       t =  (double) ep_pkt.timestamp.lower +
-            (double) ep_pkt.timestamp.upper * 4294967296.0;
-       t -= (double) mac2unix * 1000000.0;
-       wth->phdr.ts.tv_sec  = (time_t)  (t/1000000.0);
-       wth->phdr.ts.tv_usec = (guint32) (t - (double) wth->phdr.ts.tv_sec *
-                                                      1000000.0);
-       wth->phdr.pkt_encap = WTAP_ENCAP_UNKNOWN;
-       for (i=0; i<NUM_ETHERPEEK_ENCAPS; i++) {
-               if (etherpeek_encap[i].protoNum == ep_pkt.protoNum) {
-                       wth->phdr.pkt_encap = etherpeek_encap[i].encap;
+       tsecs = (time_t) (timestamp/1000000);
+       tusecs = (guint32) (timestamp - tsecs*1000000);
+       wth->phdr.ts.secs  = tsecs - mac2unix;
+       wth->phdr.ts.nsecs = tusecs * 1000;
+
+       if (wth->file_encap == WTAP_ENCAP_IEEE_802_11_WITH_RADIO) {
+               /*
+                * 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.
+                */
+                wth->phdr.len -= 4;
+                wth->phdr.caplen -= 4;
+       }
+
+       return TRUE;
+}
+
+static gboolean
+etherpeek_seek_read_v7(wtap *wth, gint64 seek_off,
+    union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
+    int *err, gchar **err_info)
+{
+       guchar ep_pkt[ETHERPEEK_V7_PKT_SIZE];
+       guint8  status;
+       airopeek_radio_hdr_t radio_hdr;
+
+       if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+               return FALSE;
+
+       /* Read the packet header. */
+       wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), wth->random_fh,
+           err);
+       status = ep_pkt[ETHERPEEK_V7_STATUS_OFFSET];
+
+       switch (wth->file_encap) {
+
+       case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
+               /*
+                * The first 4 bytes of the packet data are radio
+                * information (including a reserved byte).
+                */
+               if (length < 4) {
+                       /*
+                        * We don't *have* 4 bytes of packet data.
+                        */
+                       *err = WTAP_ERR_BAD_RECORD;
+                       *err_info = g_strdup("etherpeek: packet not long enough for 802.11 radio header");
+                       return FALSE;
                }
+               wtap_file_read_expected_bytes(&radio_hdr, 4, wth->random_fh,
+                   err);
+
+               etherpeek_fill_pseudo_header_v7(pseudo_header,
+                   &radio_hdr);
+               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. */
+               pseudo_header->eth.fcs_len = (status & 0x01) ? 0 : 4;
+               break;
        }
 
+       /*
+        * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
+        */
+       errno = WTAP_ERR_CANT_READ;
+       wtap_file_read_expected_bytes(pd, length, wth->random_fh, err);
        return TRUE;
 }
 
-static gboolean etherpeek_read_m56(wtap *wth, int *err, long *data_offset)
+static void
+etherpeek_fill_pseudo_header_v7(union wtap_pseudo_header *pseudo_header,
+    airopeek_radio_hdr_t *radio_hdr)
+{
+       pseudo_header->ieee_802_11.fcs_len = 0;         /* no FCS */
+       pseudo_header->ieee_802_11.channel = radio_hdr->channel;
+       pseudo_header->ieee_802_11.data_rate = radio_hdr->data_rate;
+       pseudo_header->ieee_802_11.signal_level = radio_hdr->signal_level;
+}
+
+static gboolean etherpeek_read_v56(wtap *wth, int *err, gchar **err_info _U_,
+    gint64 *data_offset)
 {
-       etherpeek_m56_packet_t ep_pkt;
+       guchar ep_pkt[ETHERPEEK_V56_PKT_SIZE];
+       guint16 length;
+       guint16 sliceLength;
+       guint8  flags;
+       guint8  status;
+       guint32 timestamp;
+       guint16 destNum;
+       guint16 srcNum;
+       guint16 protoNum;
+       char    protoStr[8];
        unsigned int i;
 
-       g_assert(sizeof(ep_pkt) == ETHERPEEK_M56_PKT_SIZE);
-       wtap_file_read_expected_bytes(&ep_pkt, sizeof(ep_pkt), wth->fh, err);
+       /*
+        * XXX - in order to figure out whether this packet is an
+        * Ethernet packet or not, we have to look at the packet
+        * header, so we have to remember the address of the header,
+        * not the address of the data, for random access.
+        *
+        * If we can determine that from the file header, rather than
+        * the packet header, we can remember the offset of the data,
+        * and not have the seek_read routine read the header.
+        */
+       *data_offset = wth->data_offset;
+
+       wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), wth->fh, err);
        wth->data_offset += sizeof(ep_pkt);
 
-       /* byte swaps */
-       ep_pkt.length = ntohs(ep_pkt.length);
-       ep_pkt.sliceLength = ntohs(ep_pkt.sliceLength);
-       ep_pkt.timestamp = ntohl(ep_pkt.timestamp);
-       ep_pkt.destNum = ntohs(ep_pkt.destNum);
-       ep_pkt.srcNum = ntohs(ep_pkt.srcNum);
-       ep_pkt.protoNum = ntohs(ep_pkt.protoNum);
+       /* Extract the fields from the packet */
+       length = pntohs(&ep_pkt[ETHERPEEK_V56_LENGTH_OFFSET]);
+       sliceLength = pntohs(&ep_pkt[ETHERPEEK_V56_SLICE_LENGTH_OFFSET]);
+       flags = ep_pkt[ETHERPEEK_V56_FLAGS_OFFSET];
+       status = ep_pkt[ETHERPEEK_V56_STATUS_OFFSET];
+       timestamp = pntohl(&ep_pkt[ETHERPEEK_V56_TIMESTAMP_OFFSET]);
+       destNum = pntohs(&ep_pkt[ETHERPEEK_V56_DESTNUM_OFFSET]);
+       srcNum = pntohs(&ep_pkt[ETHERPEEK_V56_SRCNUM_OFFSET]);
+       protoNum = pntohs(&ep_pkt[ETHERPEEK_V56_PROTONUM_OFFSET]);
+       memcpy(protoStr, &ep_pkt[ETHERPEEK_V56_PROTOSTR_OFFSET],
+           sizeof protoStr);
+
+       /*
+        * 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 == ep_pkt.sliceLength) {
-               ep_pkt.sliceLength = ep_pkt.length;
-       }
-
-       /* test for corrupt data */
-       if (ep_pkt.sliceLength > WTAP_MAX_PACKET_SIZE) {
-               *err = WTAP_ERR_BAD_RECORD;
-               return FALSE;
+       if (0 == sliceLength) {
+               sliceLength = length;
        }
 
-       *data_offset = wth->data_offset;
+       /* read the frame data */
+       buffer_assure_space(wth->frame_buffer, sliceLength);
+       wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
+                                     sliceLength, wth->fh, err);
+       wth->data_offset += sliceLength;
 
        /* fill in packet header values */
-       wth->phdr.len        = ep_pkt.length;
-       wth->phdr.caplen     = ep_pkt.sliceLength;
+       wth->phdr.len        = length;
+       wth->phdr.caplen     = sliceLength;
        /* timestamp is in milliseconds since reference_time */
-       wth->phdr.ts.tv_sec  = wth->pseudo_header.etherpeek.
-               reference_time.tv_sec + (ep_pkt.timestamp / 1000);
-       wth->phdr.ts.tv_usec = 1000 * (ep_pkt.timestamp % 1000);
-       
+       wth->phdr.ts.secs  = wth->capture.etherpeek->reference_time.tv_sec
+           + (timestamp / 1000);
+       wth->phdr.ts.nsecs = 1000 * (timestamp % 1000) * 1000;
+
        wth->phdr.pkt_encap = WTAP_ENCAP_UNKNOWN;
        for (i=0; i<NUM_ETHERPEEK_ENCAPS; i++) {
-               if (etherpeek_encap[i].protoNum == ep_pkt.protoNum) {
+               if (etherpeek_encap[i].protoNum == protoNum) {
                        wth->phdr.pkt_encap = etherpeek_encap[i].encap;
                }
        }
 
+       switch (wth->phdr.pkt_encap) {
+
+       case WTAP_ENCAP_ETHERNET:
+               /* We assume there's no FCS in this frame. */
+               wth->pseudo_header.eth.fcs_len = 0;
+               break;
+       }
+       return TRUE;
+}
+
+static gboolean
+etherpeek_seek_read_v56(wtap *wth, gint64 seek_off,
+    union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
+    int *err, gchar **err_info _U_)
+{
+       guchar ep_pkt[ETHERPEEK_V56_PKT_SIZE];
+       int pkt_encap;
+       guint16 protoNum;
+       unsigned int i;
+
+       if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+               return FALSE;
+
+       wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), wth->random_fh,
+           err);
+
+       protoNum = pntohs(&ep_pkt[ETHERPEEK_V56_PROTONUM_OFFSET]);
+       pkt_encap = WTAP_ENCAP_UNKNOWN;
+       for (i=0; i<NUM_ETHERPEEK_ENCAPS; i++) {
+               if (etherpeek_encap[i].protoNum == protoNum) {
+                       pkt_encap = etherpeek_encap[i].encap;
+               }
+       }
+
+       switch (pkt_encap) {
+
+       case WTAP_ENCAP_ETHERNET:
+               /* We assume there's no FCS in this frame. */
+               pseudo_header->eth.fcs_len = 0;
+               break;
+       }
+
+       /*
+        * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
+        */
+       errno = WTAP_ERR_CANT_READ;
+       wtap_file_read_expected_bytes(pd, length, wth->random_fh, err);
        return TRUE;
 }