change all file offsets from long to gint64 so we can - theoretically - handle files...
[obnox/wireshark/wip.git] / wiretap / airopeek9.c
index 8b04a36e59f61cc5c29801710be0c620a7580b64..9fa878f382253240a2f554178076cacda9fc1c0d 100644 (file)
@@ -1,7 +1,7 @@
 /* airopeek9.c
  * Routines for opening EtherPeek and AiroPeek V9 files
  *
- * $Id: airopeek9.c,v 1.7 2004/02/06 03:12:21 guy Exp $
+ * $Id$
  *
  * Wiretap Library
  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
@@ -52,6 +52,12 @@ typedef struct airopeek_section_header {
        guint32 section_const;
 } airopeek_section_header_t;
 
+/* network subtype values */
+#define AIROPEEK_V9_NST_ETHERNET               0
+#define AIROPEEK_V9_NST_802_11                 1       /* 802.11 with 0's at the end */
+#define AIROPEEK_V9_NST_802_11_WITH_FCS                3       /* 802.11 with FCS at the end */
+
+/* tags for fields in packet header */
 #define TAG_AIROPEEK_V9_LENGTH                 0x0000
 #define TAG_AIROPEEK_V9_TIMESTAMP_LOWER                0x0001
 #define TAG_AIROPEEK_V9_TIMESTAMP_UPPER                0x0002
@@ -65,24 +71,23 @@ typedef struct airopeek_section_header {
 #define TAG_AIROPEEK_V9_UNKNOWN_0x000D         0x000D
 #define TAG_AIROPEEK_V9_SLICE_LENGTH           0xffff
 
-/* 64-bit time in nano seconds from the (Mac) epoch */
+/* 64-bit time in nanoseconds from the (Windows FILETIME) epoch */
 typedef struct airopeek_utime {
        guint32 upper;
        guint32 lower;
 } airopeek_utime;
 
-static const unsigned int mac2unix = 2082844800u;
-
 static gboolean airopeekv9_read(wtap *wth, int *err, gchar **err_info,
-    long *data_offset);
-static gboolean airopeekv9_seek_read(wtap *wth, long seek_off,
+    gint64 *data_offset);
+static gboolean airopeekv9_seek_read(wtap *wth, gint64 seek_off,
     union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
     int *err, gchar **err_info);
+static void airopeekv9_close(wtap *wth);
 
-static int wtap_file_read_pattern (wtap *wth, char *pattern, int *err)
+static int wtap_file_read_pattern (wtap *wth, const char *pattern, int *err)
 {
     int c;
-    char *cp;
+    const char *cp;
 
     cp = pattern;
     while (*cp)
@@ -114,7 +119,7 @@ static int wtap_file_read_pattern (wtap *wth, char *pattern, int *err)
 
 
 static int wtap_file_read_till_separator (wtap *wth, char *buffer, int buflen,
-                                       char *separators, int *err)
+                                       const char *separators, int *err)
 {
     int c;
     char *cp;
@@ -178,7 +183,7 @@ int airopeek9_open(wtap *wth, int *err, gchar **err_info)
     int file_encap;
     static const int airopeek9_encap[] = {
        WTAP_ENCAP_ETHERNET,
-       WTAP_ENCAP_UNKNOWN,
+       WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
        WTAP_ENCAP_UNKNOWN,
        WTAP_ENCAP_IEEE_802_11_WITH_RADIO
     };
@@ -193,7 +198,7 @@ int airopeek9_open(wtap *wth, int *err, gchar **err_info)
      * XXX - we should get the length of the "\177ver" section, check
      * that it's followed by a little-endian 0x00000200, and then,
      * when reading the XML, make sure we don't go past the end of
-     * that section, and skip to the end of tha section when
+     * that section, and skip to the end of that section when
      * we have the file version (and possibly check to make sure all
      * tags are properly opened and closed).
      */
@@ -212,7 +217,7 @@ int airopeek9_open(wtap *wth, int *err, gchar **err_info)
 
     /* If we got this far, we assume it's an AiroPeek V9 file. */
     if (fileVersion != 9) {
-       /* We only support version 9 and later. */
+       /* We only support version 9. */
        *err = WTAP_ERR_UNSUPPORTED;
        *err_info = g_strdup_printf("airopeekv9: version %u unsupported",
            fileVersion);
@@ -285,9 +290,8 @@ int airopeek9_open(wtap *wth, int *err, gchar **err_info)
        return 0;
 
     /*
-     * This is an AiroPeek V9 file.
+     * This is an EtherPeek or AiroPeek V9 file.
      */
-
     wth->data_offset = file_tell (wth->fh);
 
     file_encap = airopeek9_encap[mediaSubType];
@@ -296,6 +300,21 @@ int airopeek9_open(wtap *wth, int *err, gchar **err_info)
     wth->file_encap = file_encap;
     wth->subtype_read = airopeekv9_read;
     wth->subtype_seek_read = airopeekv9_seek_read;
+    wth->subtype_close = airopeekv9_close;
+    wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
+
+    wth->capture.airopeek9 = g_malloc(sizeof(airopeek9_t));
+    switch (mediaSubType) {
+
+    case AIROPEEK_V9_NST_ETHERNET:
+    case AIROPEEK_V9_NST_802_11:
+       wth->capture.airopeek9->has_fcs = FALSE;
+       break;
+
+    case AIROPEEK_V9_NST_802_11_WITH_FCS:
+       wth->capture.airopeek9->has_fcs = TRUE;
+       break;
+    }
 
     wth->snapshot_length   = 0; /* not available in header */
 
@@ -319,14 +338,16 @@ typedef struct {
  * are present.
  */
 static int
-airopeekv9_process_header(FILE_T fh, hdr_info_t *hdr_info, int *err)
+airopeekv9_process_header(FILE_T fh, hdr_info_t *hdr_info, int *err,
+    gchar **err_info)
 {
     long header_len = 0;
     int bytes_read;
     guint8 tag_value[6];
     guint16 tag;
-
-    hdr_info->ieee_802_11.fcs_len = 0;         /* assume no FCS for 802.11 */
+    gboolean saw_length = FALSE;
+    gboolean saw_timestamp_lower = FALSE;
+    gboolean saw_timestamp_upper = FALSE;
 
     /* Extract the fields from the packet header */
     do {
@@ -354,15 +375,33 @@ airopeekv9_process_header(FILE_T fh, hdr_info_t *hdr_info, int *err)
        switch (tag) {
 
        case TAG_AIROPEEK_V9_LENGTH:
+           if (saw_length) {
+               *err = WTAP_ERR_BAD_RECORD;
+               *err_info = g_strdup("airopeekv9: record has two length fields");
+               return FALSE;
+           }
            hdr_info->length = pletohl(&tag_value[2]);
+           saw_length = TRUE;
            break;
     
        case TAG_AIROPEEK_V9_TIMESTAMP_LOWER:
+           if (saw_timestamp_lower) {
+               *err = WTAP_ERR_BAD_RECORD;
+               *err_info = g_strdup("airopeekv9: record has two timestamp-lower fields");
+               return FALSE;
+           }
            hdr_info->timestamp.lower = pletohl(&tag_value[2]);
+           saw_timestamp_lower = TRUE;
            break;
 
        case TAG_AIROPEEK_V9_TIMESTAMP_UPPER:
+           if (saw_timestamp_upper) {
+               *err = WTAP_ERR_BAD_RECORD;
+               *err_info = g_strdup("airopeekv9: record has two timestamp-upper fields");
+               return FALSE;
+           }
            hdr_info->timestamp.upper = pletohl(&tag_value[2]);
+           saw_timestamp_upper = TRUE;
            break;
 
        case TAG_AIROPEEK_V9_FLAGS_AND_STATUS:
@@ -406,11 +445,37 @@ airopeekv9_process_header(FILE_T fh, hdr_info_t *hdr_info, int *err)
         }
     } while (tag != TAG_AIROPEEK_V9_SLICE_LENGTH);     /* last tag */
 
+    if (!saw_length) {
+       *err = WTAP_ERR_BAD_RECORD;
+       *err_info = g_strdup("airopeekv9: record has no length field");
+       return FALSE;
+    }
+    if (!saw_timestamp_lower) {
+       *err = WTAP_ERR_BAD_RECORD;
+       *err_info = g_strdup("airopeekv9: record has no timestamp-lower field");
+       return FALSE;
+    }
+    if (!saw_timestamp_upper) {
+       *err = WTAP_ERR_BAD_RECORD;
+       *err_info = g_strdup("airopeekv9: record has no timestamp-upper field");
+       return FALSE;
+    }
+
     return header_len;
 }
 
-static gboolean airopeekv9_read(wtap *wth, int *err, gchar **err_info _U_,
-    long *data_offset)
+/*
+ * Time stamps appear to be in nanoseconds since the Windows epoch
+ * as used in FILETIMEs, i.e. midnight, January 1, 1601.
+ *
+ * This magic number came from "nt_time_to_nstime()" in "packet-smb.c".
+ * 1970-1601 is 369; I'm not sure what the extra 3 days and 6 hours are
+ * that are being subtracted.
+ */
+#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+
+static gboolean airopeekv9_read(wtap *wth, int *err, gchar **err_info,
+    gint64 *data_offset)
 {
     hdr_info_t hdr_info;
     int hdrlen;
@@ -419,7 +484,7 @@ static gboolean airopeekv9_read(wtap *wth, int *err, gchar **err_info _U_,
     *data_offset = wth->data_offset;
 
     /* Process the packet header. */
-    hdrlen = airopeekv9_process_header(wth->fh, &hdr_info, err);
+    hdrlen = airopeekv9_process_header(wth->fh, &hdr_info, err, err_info);
     if (hdrlen == -1)
        return FALSE;
     wth->data_offset += hdrlen;
@@ -433,17 +498,6 @@ static gboolean airopeekv9_read(wtap *wth, int *err, gchar **err_info _U_,
     wth->phdr.len    = hdr_info.length;
     wth->phdr.caplen = hdr_info.sliceLength;
 
-    switch (wth->file_encap) {
-
-    case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
-       wth->pseudo_header.ieee_802_11 = hdr_info.ieee_802_11;
-       break;
-
-    case WTAP_ENCAP_ETHERNET:
-       wth->pseudo_header.eth.fcs_len = 0;     /* XXX - always? */
-       break;
-    }
-
     /* read the frame data */
     buffer_assure_space(wth->frame_buffer, hdr_info.sliceLength);
     wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
@@ -454,11 +508,10 @@ static gboolean airopeekv9_read(wtap *wth, int *err, gchar **err_info _U_,
     t =  (double) hdr_info.timestamp.lower +
         (double) hdr_info.timestamp.upper * 4294967296.0;
 
-    t = t / 1000.0;    /* nano seconds -> micro seconds */
-    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);
+    t *= 1.0e-9;
+    t -= TIME_FIXUP_CONSTANT;
+    wth->phdr.ts.secs  = (time_t) t;
+    wth->phdr.ts.nsecs = (guint32) ((t - wth->phdr.ts.secs)*1000000000);
 
     switch (wth->file_encap) {
 
@@ -472,8 +525,14 @@ static gboolean airopeekv9_read(wtap *wth, int *err, gchar **err_info _U_,
         * whether it's an FCS or not, we should use that to determine
         * whether to supply it as an FCS or discard it.
         */
-       wth->phdr.len -= 4;
-       wth->phdr.caplen -= 4;
+       wth->pseudo_header.ieee_802_11 = hdr_info.ieee_802_11;
+       if (wth->capture.airopeek9->has_fcs)
+           wth->pseudo_header.ieee_802_11.fcs_len = 4;
+       else {
+           wth->pseudo_header.ieee_802_11.fcs_len = 0;
+           wth->phdr.len -= 4;
+           wth->phdr.caplen -= 4;
+       }
        break;
 
     case WTAP_ENCAP_ETHERNET:
@@ -481,20 +540,20 @@ static gboolean airopeekv9_read(wtap *wth, int *err, gchar **err_info _U_,
         * The last 4 bytes appear to be 0 in the captures I've seen;
         * are there any captures where it's an FCS?
         */
+       wth->pseudo_header.eth.fcs_len = 0;
        wth->phdr.len -= 4;
        wth->phdr.caplen -= 4;
        break;
     }
 
-    wth->phdr.pkt_encap = wth->file_encap;
     return TRUE;
 }
 
 
 static gboolean
-airopeekv9_seek_read(wtap *wth, long seek_off,
+airopeekv9_seek_read(wtap *wth, gint64 seek_off,
     union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
-    int *err, gchar **err_info _U_)
+    int *err, gchar **err_info)
 {
     hdr_info_t hdr_info;
 
@@ -502,17 +561,21 @@ airopeekv9_seek_read(wtap *wth, long seek_off,
        return FALSE;
 
     /* Process the packet header. */
-    if (airopeekv9_process_header(wth->random_fh, &hdr_info, err) == -1)
+    if (airopeekv9_process_header(wth->random_fh, &hdr_info, err, err_info) == -1)
        return FALSE;
 
     switch (wth->file_encap) {
 
     case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
        pseudo_header->ieee_802_11 = hdr_info.ieee_802_11;
+       if (wth->capture.airopeek9->has_fcs)
+           pseudo_header->ieee_802_11.fcs_len = 4;
+       else
+           pseudo_header->ieee_802_11.fcs_len = 0;
        break;
 
     case WTAP_ENCAP_ETHERNET:
-       pseudo_header->eth.fcs_len = 0; /* XXX - always? */
+       pseudo_header->eth.fcs_len = 0;
        break;
     }
 
@@ -523,3 +586,9 @@ airopeekv9_seek_read(wtap *wth, long seek_off,
     wtap_file_read_expected_bytes(pd, length, wth->random_fh, err);
     return TRUE;
 }
+
+static void
+airopeekv9_close(wtap *wth)
+{
+    g_free(wth->capture.airopeek9);
+}