From Owen Kirby :
[obnox/wireshark/wip.git] / wiretap / netmon.c
index 614660433f61a226160577948109d40b7b18aabe..c09f54c1aaf01be4caf8f054278673ac9a9d81c1 100644 (file)
@@ -1,33 +1,34 @@
 /* netmon.c
  *
- * $Id: netmon.c,v 1.16 1999/10/05 07:06:06 guy Exp $
+ * $Id$
  *
  * Wiretap Library
- * Copyright (c) 1998 by Gilbert Ramirez <gram@verdict.uthscsa.edu>
- * 
+ * 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 "config.h"
 #endif
 #include <errno.h>
-#include <time.h>
-#include "wtap.h"
-#include "file.h"
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
 #include "buffer.h"
+#include "atm.h"
 #include "netmon.h"
 
 /* The file at
@@ -76,7 +77,7 @@ struct netmon_hdr {
        guint32 networkinfolength;      /* length of network info structure */
 };
 
-/* Network Monitor record header; not defined in STRUCT.H, but deduced by
+/* Network Monitor 1.x record header; not defined in STRUCT.H, but deduced by
  * looking at capture files. */
 struct netmonrec_1_x_hdr {
        guint32 ts_delta;       /* time stamp - msecs since start of capture */
@@ -84,6 +85,8 @@ struct netmonrec_1_x_hdr {
        guint16 incl_len;       /* number of octets captured in file */
 };
 
+/* Network Monitor 2.x record header; not defined in STRUCT.H, but deduced by
+ * looking at capture files. */
 struct netmonrec_2_x_hdr {
        guint32 ts_delta_lo;    /* time stamp - usecs since start of capture */
        guint32 ts_delta_hi;    /* time stamp - usecs since start of capture */
@@ -91,9 +94,32 @@ struct netmonrec_2_x_hdr {
        guint32 incl_len;       /* number of octets captured in file */
 };
 
-static int netmon_read(wtap *wth, int *err);
+/*
+ * The link-layer header on ATM packets.
+ */
+struct netmon_atm_hdr {
+       guint8  dest[6];        /* "Destination address" - what is it? */
+       guint8  src[6];         /* "Source address" - what is it? */
+       guint16 vpi;            /* VPI */
+       guint16 vci;            /* VCI */
+};
 
-int netmon_open(wtap *wth, int *err)
+static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
+    gint64 *data_offset);
+static gboolean netmon_seek_read(wtap *wth, gint64 seek_off,
+    union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
+    int *err, gchar **err_info);
+static gboolean netmon_read_atm_pseudoheader(FILE_T fh,
+    union wtap_pseudo_header *pseudo_header, int *err);
+static gboolean netmon_read_rec_data(FILE_T fh, guchar *pd, int length,
+    int *err);
+static void netmon_sequential_close(wtap *wth);
+static void netmon_close(wtap *wth);
+static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
+    const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err);
+static gboolean netmon_dump_close(wtap_dumper *wdh, int *err);
+
+int netmon_open(wtap *wth, int *err, gchar **err_info)
 {
        int bytes_read;
        char magic[sizeof netmon_1_x_magic];
@@ -102,23 +128,29 @@ int netmon_open(wtap *wth, int *err)
        static const int netmon_encap[] = {
                WTAP_ENCAP_UNKNOWN,
                WTAP_ENCAP_ETHERNET,
-               WTAP_ENCAP_TR,
+               WTAP_ENCAP_TOKEN_RING,
                WTAP_ENCAP_FDDI_BITSWAPPED,
-               WTAP_ENCAP_UNKNOWN,     /* WAN */
-               WTAP_ENCAP_UNKNOWN,     /* LocalTalk */
-               WTAP_ENCAP_UNKNOWN,     /* "DIX" - should not occur */
-               WTAP_ENCAP_UNKNOWN,     /* ARCNET raw */
-               WTAP_ENCAP_UNKNOWN,     /* ARCNET 878.2 */
-               WTAP_ENCAP_UNKNOWN,     /* ATM */
-               WTAP_ENCAP_UNKNOWN,     /* Wireless WAN */
-               WTAP_ENCAP_UNKNOWN      /* IrDA */
+               WTAP_ENCAP_ATM_PDUS,    /* NDIS WAN - this is what's used for ATM */
+               WTAP_ENCAP_UNKNOWN,     /* NDIS LocalTalk */
+               WTAP_ENCAP_UNKNOWN,     /* NDIS "DIX" - should not occur */
+               WTAP_ENCAP_UNKNOWN,     /* NDIS ARCNET raw */
+               WTAP_ENCAP_UNKNOWN,     /* NDIS ARCNET 878.2 */
+               WTAP_ENCAP_UNKNOWN,     /* NDIS ATM (no, this is NOT used for ATM) */
+               WTAP_ENCAP_UNKNOWN,     /* NDIS Wireless WAN */
+               WTAP_ENCAP_UNKNOWN      /* NDIS IrDA */
        };
        #define NUM_NETMON_ENCAPS (sizeof netmon_encap / sizeof netmon_encap[0])
        struct tm tm;
+       int frame_table_offset;
+       guint32 frame_table_length;
+       guint32 frame_table_size;
+       guint32 *frame_table;
+#ifdef WORDS_BIGENDIAN
+       unsigned int i;
+#endif
 
        /* Read in the string that should be at the start of a Network
         * Monitor file */
-       file_seek(wth->fh, 0, SEEK_SET);
        errno = WTAP_ERR_CANT_READ;
        bytes_read = file_read(magic, 1, sizeof magic, wth->fh);
        if (bytes_read != sizeof magic) {
@@ -154,17 +186,17 @@ int netmon_open(wtap *wth, int *err)
                break;
 
        default:
-               g_message("netmon: major version %u unsupported", hdr.ver_major);
                *err = WTAP_ERR_UNSUPPORTED;
+               *err_info = g_strdup_printf("netmon: major version %u unsupported", hdr.ver_major);
                return -1;
        }
 
        hdr.network = pletohs(&hdr.network);
        if (hdr.network >= NUM_NETMON_ENCAPS
            || netmon_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
-               g_message("netmon: network type %u unknown or unsupported",
+               *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+               *err_info = g_strdup_printf("netmon: network type %u unknown or unsupported",
                    hdr.network);
-               *err = WTAP_ERR_UNSUPPORTED;
                return -1;
        }
 
@@ -172,8 +204,11 @@ int netmon_open(wtap *wth, int *err)
        wth->file_type = file_type;
        wth->capture.netmon = g_malloc(sizeof(netmon_t));
        wth->subtype_read = netmon_read;
+       wth->subtype_seek_read = netmon_seek_read;
+       wth->subtype_sequential_close = netmon_sequential_close;
+       wth->subtype_close = netmon_close;
        wth->file_encap = netmon_encap[hdr.network];
-       wth->snapshot_length = 16384;   /* XXX - not available in header */
+       wth->snapshot_length = 0;       /* not available in header */
        /*
         * Convert the time stamp to a "time_t" and a number of
         * milliseconds.
@@ -205,41 +240,113 @@ int netmon_open(wtap *wth, int *err)
        wth->capture.netmon->version_major = hdr.ver_major;
 
        /*
-        * The "frame index table" appears to come after the last
-        * packet; remember its offset, so we know when we have no
-        * more packets to read.
+        * Get the offset of the frame index table.
+        */
+       frame_table_offset = pletohl(&hdr.frametableoffset);
+
+       /*
+        * It appears that some NetMon 2.x files don't have the
+        * first packet starting exactly 128 bytes into the file.
+        *
+        * Furthermore, it also appears that there are "holes" in
+        * the file, i.e. frame N+1 doesn't always follow immediately
+        * after frame N.
+        *
+        * Therefore, we must read the frame table, and use the offsets
+        * in it as the offsets of the frames.
+        */
+       frame_table_length = pletohl(&hdr.frametablelength);
+       frame_table_size = frame_table_length / sizeof (guint32);
+       if ((frame_table_size * sizeof (guint32)) != frame_table_length) {
+               *err = WTAP_ERR_UNSUPPORTED;
+               *err_info = g_strdup_printf("netmon: frame table length is %u, which is not a multiple of the size of an entry",
+                   frame_table_length);
+               g_free(wth->capture.netmon);
+               return -1;
+       }
+       if (frame_table_size == 0) {
+               *err = WTAP_ERR_UNSUPPORTED;
+               *err_info = g_strdup_printf("netmon: frame table length is %u, which means it's less than one entry in size",
+                   frame_table_length);
+               g_free(wth->capture.netmon);
+               return -1;
+       }
+       if (file_seek(wth->fh, frame_table_offset, SEEK_SET, err) == -1) {
+               g_free(wth->capture.netmon);
+               return -1;
+       }
+       frame_table = g_malloc(frame_table_length);
+       errno = WTAP_ERR_CANT_READ;
+       bytes_read = file_read(frame_table, 1, frame_table_length, wth->fh);
+       if ((guint32)bytes_read != frame_table_length) {
+               *err = file_error(wth->fh);
+               if (*err == 0)
+                       *err = WTAP_ERR_SHORT_READ;
+               g_free(frame_table);
+               g_free(wth->capture.netmon);
+               return -1;
+       }
+       wth->capture.netmon->frame_table_size = frame_table_size;
+       wth->capture.netmon->frame_table = frame_table;
+
+#ifdef WORDS_BIGENDIAN
+       /*
+        * OK, now byte-swap the frame table.
         */
-       wth->capture.netmon->end_offset = pletohl(&hdr.frametableoffset);
+       for (i = 0; i < frame_table_size; i++)
+               frame_table[i] = pletohl(&frame_table[i]);
+#endif
 
-       /* Seek to the beginning of the data records. */
-       file_seek(wth->fh, CAPTUREFILE_HEADER_SIZE, SEEK_SET);
-       wth->data_offset = CAPTUREFILE_HEADER_SIZE;
+       /* Set up to start reading at the first frame. */
+       wth->capture.netmon->current_frame = 0;
+       wth->tsprecision = WTAP_FILE_TSPREC_USEC;
 
        return 1;
 }
 
 /* Read the next packet */
-static int netmon_read(wtap *wth, int *err)
+static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
+    gint64 *data_offset)
 {
+       netmon_t *netmon = wth->capture.netmon;
        guint32 packet_size = 0;
+       guint32 orig_size = 0;
        int     bytes_read;
        union {
                struct netmonrec_1_x_hdr hdr_1_x;
                struct netmonrec_2_x_hdr hdr_2_x;
        }       hdr;
        int     hdr_size = 0;
-       int     data_offset;
+       int     rec_offset;
+       guint8  *data_ptr;
        time_t  secs;
        guint32 usecs;
        double  t;
 
        /* Have we reached the end of the packet data? */
-       if (wth->data_offset >= wth->capture.netmon->end_offset) {
-               /* Yes. */
-               return 0;
+       if (netmon->current_frame >= netmon->frame_table_size) {
+               /* Yes.  We won't need the frame table any more;
+                  free it. */
+               g_free(wth->capture.netmon->frame_table);
+               wth->capture.netmon->frame_table = NULL;
+               *err = 0;       /* it's just an EOF, not an error */
+               return FALSE;
        }
+
+       /* Seek to the beginning of the current record, if we're
+          not there already (seeking to the current position
+          may still cause a seek and a read of the underlying file,
+          so we don't want to do it unconditionally). */
+       rec_offset = netmon->frame_table[netmon->current_frame];
+       if (wth->data_offset != rec_offset) {
+               wth->data_offset = rec_offset;
+               if (file_seek(wth->fh, wth->data_offset, SEEK_SET, err) == -1)
+                       return FALSE;
+       }
+       netmon->current_frame++;
+
        /* Read record header. */
-       switch (wth->capture.netmon->version_major) {
+       switch (netmon->version_major) {
 
        case 1:
                hdr_size = sizeof (struct netmonrec_1_x_hdr);
@@ -250,26 +357,26 @@ static int netmon_read(wtap *wth, int *err)
                break;
        }
        errno = WTAP_ERR_CANT_READ;
+
        bytes_read = file_read(&hdr, 1, hdr_size, wth->fh);
        if (bytes_read != hdr_size) {
                *err = file_error(wth->fh);
-               if (*err != 0)
-                       return -1;
-               if (bytes_read != 0) {
+               if (*err == 0 && bytes_read != 0) {
                        *err = WTAP_ERR_SHORT_READ;
-                       return -1;
                }
-               return 0;
+               return FALSE;
        }
        wth->data_offset += hdr_size;
 
-       switch (wth->capture.netmon->version_major) {
+       switch (netmon->version_major) {
 
        case 1:
+               orig_size = pletohs(&hdr.hdr_1_x.orig_len);
                packet_size = pletohs(&hdr.hdr_1_x.incl_len);
                break;
 
        case 2:
+               orig_size = pletohl(&hdr.hdr_2_x.orig_len);
                packet_size = pletohl(&hdr.hdr_2_x.incl_len);
                break;
        }
@@ -278,27 +385,62 @@ static int netmon_read(wtap *wth, int *err)
                 * Probably a corrupt capture file; don't blow up trying
                 * to allocate space for an immensely-large packet.
                 */
-               g_message("netmon: File has %u-byte packet, bigger than maximum of %u",
-                   packet_size, WTAP_MAX_PACKET_SIZE);
                *err = WTAP_ERR_BAD_RECORD;
-               return -1;
+               *err_info = g_strdup_printf("netmon: File has %u-byte packet, bigger than maximum of %u",
+                   packet_size, WTAP_MAX_PACKET_SIZE);
+               return FALSE;
        }
-       buffer_assure_space(wth->frame_buffer, packet_size);
-       data_offset = wth->data_offset;
-       errno = WTAP_ERR_CANT_READ;
-       bytes_read = file_read(buffer_start_ptr(wth->frame_buffer), 1,
-                       packet_size, wth->fh);
 
-       if (bytes_read != packet_size) {
-               *err = file_error(wth->fh);
-               if (*err == 0)
-                       *err = WTAP_ERR_SHORT_READ;
-               return -1;
+       *data_offset = wth->data_offset;
+
+       /*
+        * If this is an ATM packet, the first
+        * "sizeof (struct netmon_atm_hdr)" bytes have destination and
+        * source addresses (6 bytes - MAC addresses of some sort?)
+        * and the VPI and VCI; read them and generate the pseudo-header
+        * from them.
+        */
+       switch (wth->file_encap) {
+
+       case WTAP_ENCAP_ATM_PDUS:
+               if (packet_size < sizeof (struct netmon_atm_hdr)) {
+                       /*
+                        * Uh-oh, the packet isn't big enough to even
+                        * have a pseudo-header.
+                        */
+                       *err = WTAP_ERR_BAD_RECORD;
+                       *err_info = g_strdup_printf("netmon: ATM file has a %u-byte packet, too small to have even an ATM pseudo-header\n",
+                           packet_size);
+                       return FALSE;
+               }
+               if (!netmon_read_atm_pseudoheader(wth->fh, &wth->pseudo_header,
+                   err))
+                       return FALSE;   /* Read error */
+
+               /*
+                * Don't count the pseudo-header as part of the packet.
+                */
+               orig_size -= sizeof (struct netmon_atm_hdr);
+               packet_size -= sizeof (struct netmon_atm_hdr);
+               wth->data_offset += sizeof (struct netmon_atm_hdr);
+               break;
+
+       case WTAP_ENCAP_ETHERNET:
+               /*
+                * We assume there's no FCS in this frame.
+                */
+               wth->pseudo_header.eth.fcs_len = 0;
+               break;
        }
+
+       buffer_assure_space(wth->frame_buffer, packet_size);
+       data_ptr = buffer_start_ptr(wth->frame_buffer);
+       if (!netmon_read_rec_data(wth->fh, data_ptr, packet_size, err))
+               return FALSE;   /* Read error */
        wth->data_offset += packet_size;
 
-       t = (double)wth->capture.netmon->start_usecs;
-       switch (wth->capture.netmon->version_major) {
+       t = (double)netmon->start_usecs;
+       switch (netmon->version_major) {
 
        case 1:
                t += ((double)pletohl(&hdr.hdr_1_x.ts_delta))*1000;
@@ -310,21 +452,439 @@ static int netmon_read(wtap *wth, int *err)
                break;
        }
        secs = (time_t)(t/1000000);
-       usecs = (guint32)(t - secs*1000000);
-       wth->phdr.ts.tv_sec = wth->capture.netmon->start_secs + secs;
-       wth->phdr.ts.tv_usec = usecs;
+       usecs = (guint32)(t - (double)secs*1000000);
+       wth->phdr.ts.secs = netmon->start_secs + secs;
+       wth->phdr.ts.nsecs = usecs * 1000;
        wth->phdr.caplen = packet_size;
-       switch (wth->capture.netmon->version_major) {
+       wth->phdr.len = orig_size;
 
-       case 1:
-               wth->phdr.len = pletohs(&hdr.hdr_1_x.orig_len);
+       /*
+        * Attempt to guess from the packet data, the VPI, and the VCI
+        * information about the type of traffic.
+        */
+       if (wth->file_encap == WTAP_ENCAP_ATM_PDUS) {
+               atm_guess_traffic_type(data_ptr, packet_size,
+                   &wth->pseudo_header);
+       }
+
+       return TRUE;
+}
+
+static gboolean
+netmon_seek_read(wtap *wth, gint64 seek_off,
+    union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
+    int *err, gchar **err_info _U_)
+{
+       if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+               return FALSE;
+
+       switch (wth->file_encap) {
+
+       case WTAP_ENCAP_ATM_PDUS:
+               if (!netmon_read_atm_pseudoheader(wth->random_fh, pseudo_header,
+                   err)) {
+                       /* Read error */
+                       return FALSE;
+               }
                break;
 
-       case 2:
-               wth->phdr.len = pletohl(&hdr.hdr_2_x.orig_len);
+       case WTAP_ENCAP_ETHERNET:
+               /*
+                * We assume there's no FCS in this frame.
+                */
+               pseudo_header->eth.fcs_len = 0;
+               break;
+       }
+
+       /*
+        * Read the packet data.
+        */
+       if (!netmon_read_rec_data(wth->random_fh, pd, length, err))
+               return FALSE;
+
+       /*
+        * Attempt to guess from the packet data, the VPI, and the VCI
+        * information about the type of traffic.
+        */
+       if (wth->file_encap == WTAP_ENCAP_ATM_PDUS)
+               atm_guess_traffic_type(pd, length, pseudo_header);
+
+       return TRUE;
+}
+
+static gboolean
+netmon_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
+    int *err)
+{
+       struct netmon_atm_hdr atm_phdr;
+       int     bytes_read;
+       guint16 vpi, vci;
+
+       errno = WTAP_ERR_CANT_READ;
+       bytes_read = file_read(&atm_phdr, 1, sizeof (struct netmon_atm_hdr), fh);
+       if (bytes_read != sizeof (struct netmon_atm_hdr)) {
+               *err = file_error(fh);
+               if (*err == 0)
+                       *err = WTAP_ERR_SHORT_READ;
+               return FALSE;
+       }
+
+       vpi = g_ntohs(atm_phdr.vpi);
+       vci = g_ntohs(atm_phdr.vci);
+
+       pseudo_header->atm.vpi = vpi;
+       pseudo_header->atm.vci = vci;
+
+       /* We don't have this information */
+       pseudo_header->atm.flags = 0;
+       pseudo_header->atm.channel = 0;
+       pseudo_header->atm.cells = 0;
+       pseudo_header->atm.aal5t_u2u = 0;
+       pseudo_header->atm.aal5t_len = 0;
+       pseudo_header->atm.aal5t_chksum = 0;
+
+       return TRUE;
+}
+
+static gboolean
+netmon_read_rec_data(FILE_T fh, guchar *pd, int length, int *err)
+{
+       int     bytes_read;
+
+       errno = WTAP_ERR_CANT_READ;
+       bytes_read = file_read(pd, 1, length, fh);
+
+       if (bytes_read != length) {
+               *err = file_error(fh);
+               if (*err == 0)
+                       *err = WTAP_ERR_SHORT_READ;
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/* Throw away the frame table used by the sequential I/O stream. */
+static void
+netmon_sequential_close(wtap *wth)
+{
+       if (wth->capture.netmon->frame_table != NULL) {
+               g_free(wth->capture.netmon->frame_table);
+               wth->capture.netmon->frame_table = NULL;
+       }
+}
+
+/* Close stuff used by the random I/O stream, if any, and free up any
+   private data structures.  (If there's a "sequential_close" routine
+   for a capture file type, it'll be called before the "close" routine
+   is called, so we don't have to free the frame table here.) */
+static void
+netmon_close(wtap *wth)
+{
+       g_free(wth->capture.netmon);
+}
+
+static const int wtap_encap[] = {
+       -1,             /* WTAP_ENCAP_UNKNOWN -> unsupported */
+       1,              /* WTAP_ENCAP_ETHERNET -> NDIS Ethernet */
+       2,              /* WTAP_ENCAP_TOKEN_RING -> NDIS Token Ring */
+       -1,             /* WTAP_ENCAP_SLIP -> unsupported */
+       -1,             /* WTAP_ENCAP_PPP -> unsupported */
+       3,              /* WTAP_ENCAP_FDDI -> NDIS FDDI */
+       3,              /* WTAP_ENCAP_FDDI_BITSWAPPED -> NDIS FDDI */
+       -1,             /* WTAP_ENCAP_RAW_IP -> unsupported */
+       -1,             /* WTAP_ENCAP_ARCNET -> unsupported */
+       -1,             /* WTAP_ENCAP_ATM_RFC1483 -> unsupported */
+       -1,             /* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */
+       -1,             /* WTAP_ENCAP_LAPB -> unsupported*/
+       4,              /* WTAP_ENCAP_ATM_PDUS -> NDIS WAN (*NOT* ATM!) */
+       -1              /* WTAP_ENCAP_NULL -> unsupported */
+};
+#define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
+
+/* Returns 0 if we could write the specified encapsulation type,
+   an error indication otherwise. */
+int netmon_dump_can_write_encap(int encap)
+{
+       /* Per-packet encapsulations aren't supported. */
+       if (encap == WTAP_ENCAP_PER_PACKET)
+               return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+       if (encap < 0 || (unsigned) encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
+               return WTAP_ERR_UNSUPPORTED_ENCAP;
+
+       return 0;
+}
+
+/* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+   failure */
+gboolean netmon_dump_open(wtap_dumper *wdh, gboolean cant_seek, int *err)
+{
+       /* This is a NetMon file.  We can't fill in some fields in the
+          header until all the packets have been written, so we can't
+          write to a pipe. */
+       if (cant_seek) {
+               *err = WTAP_ERR_CANT_WRITE_TO_PIPE;
+               return FALSE;
+       }
+
+       /* We can't fill in all the fields in the file header, as we
+          haven't yet written any packets.  As we'll have to rewrite
+          the header when we've written out all the packets, we just
+          skip over the header for now. */
+       if (fseek(wdh->fh, CAPTUREFILE_HEADER_SIZE, SEEK_SET) == -1) {
+               *err = errno;
+               return FALSE;
+       }
+
+       wdh->subtype_write = netmon_dump;
+       wdh->subtype_close = netmon_dump_close;
+
+       wdh->dump.netmon = g_malloc(sizeof(netmon_dump_t));
+       wdh->dump.netmon->frame_table_offset = CAPTUREFILE_HEADER_SIZE;
+       wdh->dump.netmon->got_first_record_time = FALSE;
+       wdh->dump.netmon->frame_table = NULL;
+       wdh->dump.netmon->frame_table_index = 0;
+       wdh->dump.netmon->frame_table_size = 0;
+
+       return TRUE;
+}
+
+/* Write a record for a packet to a dump file.
+   Returns TRUE on success, FALSE on failure. */
+static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
+    const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err)
+{
+       netmon_dump_t *netmon = wdh->dump.netmon;
+       struct netmonrec_1_x_hdr rec_1_x_hdr;
+       struct netmonrec_2_x_hdr rec_2_x_hdr;
+       char *hdrp;
+       size_t hdr_size;
+       size_t nwritten;
+       double t;
+       guint32 time_low, time_high;
+       struct netmon_atm_hdr atm_hdr;
+       int atm_hdrsize;
+
+       /* NetMon files have a capture start time in the file header,
+          and have times relative to that in the packet headers;
+          pick the time of the first packet as the capture start
+          time. */
+       if (!netmon->got_first_record_time) {
+               netmon->first_record_time = phdr->ts;
+               netmon->got_first_record_time = TRUE;
+       }
+
+       if (wdh->encap == WTAP_ENCAP_ATM_PDUS)
+               atm_hdrsize = sizeof (struct netmon_atm_hdr);
+       else
+               atm_hdrsize = 0;
+       switch (wdh->file_type) {
+
+       case WTAP_FILE_NETMON_1_x:
+               rec_1_x_hdr.ts_delta = htolel(
+                   (phdr->ts.secs - netmon->first_record_time.secs)*1000
+                 + (phdr->ts.nsecs - netmon->first_record_time.nsecs + 500000)/1000000);
+               rec_1_x_hdr.orig_len = htoles(phdr->len + atm_hdrsize);
+               rec_1_x_hdr.incl_len = htoles(phdr->caplen + atm_hdrsize);
+               hdrp = (char *)&rec_1_x_hdr;
+               hdr_size = sizeof rec_1_x_hdr;
+               break;
+
+       case WTAP_FILE_NETMON_2_x:
+               /*
+                * Unfortunately, not all the platforms on which we run
+                * support 64-bit integral types, even though most do
+                * (even on 32-bit processors), so we do it in floating
+                * point.
+                */
+               t = (phdr->ts.secs - netmon->first_record_time.secs)*1000000.0
+                 + (phdr->ts.nsecs - netmon->first_record_time.nsecs) / 1000;
+               time_high = (guint32) (t/4294967296.0);
+               time_low  = (guint32) (t - (time_high*4294967296.0));
+               rec_2_x_hdr.ts_delta_lo = htolel(time_low);
+               rec_2_x_hdr.ts_delta_hi = htolel(time_high);
+               rec_2_x_hdr.orig_len = htolel(phdr->len + atm_hdrsize);
+               rec_2_x_hdr.incl_len = htolel(phdr->caplen + atm_hdrsize);
+               hdrp = (char *)&rec_2_x_hdr;
+               hdr_size = sizeof rec_2_x_hdr;
+               break;
+
+       default:
+               /* We should never get here - our open routine
+                  should only get called for the types above. */
+               *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
+               return FALSE;
+       }
+
+       nwritten = fwrite(hdrp, 1, hdr_size, wdh->fh);
+       if (nwritten != hdr_size) {
+               if (nwritten == 0 && ferror(wdh->fh))
+                       *err = errno;
+               else
+                       *err = WTAP_ERR_SHORT_WRITE;
+               return FALSE;
+       }
+
+       if (wdh->encap == WTAP_ENCAP_ATM_PDUS) {
+               /*
+                * Write the ATM header.
+                * We supply all-zero destination and source addresses.
+                */
+               memset(&atm_hdr.dest, 0, sizeof atm_hdr.dest);
+               memset(&atm_hdr.src, 0, sizeof atm_hdr.src);
+               atm_hdr.vpi = g_htons(pseudo_header->atm.vpi);
+               atm_hdr.vci = g_htons(pseudo_header->atm.vci);
+               nwritten = fwrite(&atm_hdr, 1, sizeof atm_hdr, wdh->fh);
+               if (nwritten != sizeof atm_hdr) {
+                       if (nwritten == 0 && ferror(wdh->fh))
+                               *err = errno;
+                       else
+                               *err = WTAP_ERR_SHORT_WRITE;
+                       return FALSE;
+               }
+       }
+
+       nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
+       if (nwritten != phdr->caplen) {
+               if (nwritten == 0 && ferror(wdh->fh))
+                       *err = errno;
+               else
+                       *err = WTAP_ERR_SHORT_WRITE;
+               return FALSE;
+       }
+
+       /*
+        * Stash the file offset of this frame.
+        */
+       if (netmon->frame_table_size == 0) {
+               /*
+                * Haven't yet allocated the buffer for the frame table.
+                */
+               netmon->frame_table = g_malloc(1024 * sizeof *netmon->frame_table);
+               netmon->frame_table_size = 1024;
+       } else {
+               /*
+                * We've allocated it; are we at the end?
+                */
+               if (netmon->frame_table_index >= netmon->frame_table_size) {
+                       /*
+                        * Yes - double the size of the frame table.
+                        */
+                       netmon->frame_table_size *= 2;
+                       netmon->frame_table = g_realloc(netmon->frame_table,
+                           netmon->frame_table_size * sizeof *netmon->frame_table);
+               }
+       }
+       netmon->frame_table[netmon->frame_table_index] =
+           htolel(netmon->frame_table_offset);
+       netmon->frame_table_index++;
+       netmon->frame_table_offset += hdr_size + phdr->caplen + atm_hdrsize;
+
+       return TRUE;
+}
+
+/* Finish writing to a dump file.
+   Returns TRUE on success, FALSE on failure. */
+static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
+{
+       netmon_dump_t *netmon = wdh->dump.netmon;
+       size_t n_to_write;
+       size_t nwritten;
+       struct netmon_hdr file_hdr;
+       const char *magicp;
+       size_t magic_size;
+       struct tm *tm;
+
+       /* Write out the frame table.  "netmon->frame_table_index" is
+          the number of entries we've put into it. */
+       n_to_write = netmon->frame_table_index * sizeof *netmon->frame_table;
+       nwritten = fwrite(netmon->frame_table, 1, n_to_write, wdh->fh);
+       if (nwritten != n_to_write) {
+               if (err != NULL) {
+                       if (nwritten == 0 && ferror(wdh->fh))
+                               *err = errno;
+                       else
+                               *err = WTAP_ERR_SHORT_WRITE;
+               }
+               return FALSE;
+       }
+
+       /* Now go fix up the file header. */
+       fseek(wdh->fh, 0, SEEK_SET);
+       memset(&file_hdr, '\0', sizeof file_hdr);
+       switch (wdh->file_type) {
+
+       case WTAP_FILE_NETMON_1_x:
+               magicp = netmon_1_x_magic;
+               magic_size = sizeof netmon_1_x_magic;
+               /* NetMon file version, for 1.x, is 1.1 */
+               file_hdr.ver_major = 1;
+               file_hdr.ver_minor = 1;
                break;
+
+       case WTAP_FILE_NETMON_2_x:
+               magicp = netmon_2_x_magic;
+               magic_size = sizeof netmon_2_x_magic;
+               /*
+                * NetMon file version, for 2.x, is 2.0;
+                * for 3.0, it's 2.1.
+                */
+               file_hdr.ver_major = 2;
+               file_hdr.ver_minor = 0;
+               break;
+
+       default:
+               /* We should never get here - our open routine
+                  should only get called for the types above. */
+               if (err != NULL)
+                       *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
+               return FALSE;
+       }
+       nwritten = fwrite(magicp, 1, magic_size, wdh->fh);
+       if (nwritten != magic_size) {
+               if (err != NULL) {
+                       if (nwritten == 0 && ferror(wdh->fh))
+                               *err = errno;
+                       else
+                               *err = WTAP_ERR_SHORT_WRITE;
+               }
+               return FALSE;
+       }
+
+       file_hdr.network = htoles(wtap_encap[wdh->encap]);
+       tm = localtime(&netmon->first_record_time.secs);
+       if (tm != NULL) {
+               file_hdr.ts_year  = htoles(1900 + tm->tm_year);
+               file_hdr.ts_month = htoles(tm->tm_mon + 1);
+               file_hdr.ts_dow   = htoles(tm->tm_wday);
+               file_hdr.ts_day   = htoles(tm->tm_mday);
+               file_hdr.ts_hour  = htoles(tm->tm_hour);
+               file_hdr.ts_min   = htoles(tm->tm_min);
+               file_hdr.ts_sec   = htoles(tm->tm_sec);
+       } else {
+               file_hdr.ts_year  = htoles(1900 + 0);
+               file_hdr.ts_month = htoles(0 + 1);
+               file_hdr.ts_dow   = htoles(0);
+               file_hdr.ts_day   = htoles(0);
+               file_hdr.ts_hour  = htoles(0);
+               file_hdr.ts_min   = htoles(0);
+               file_hdr.ts_sec   = htoles(0);
+       }
+       file_hdr.ts_msec  = htoles(netmon->first_record_time.nsecs/1000000);
+               /* XXX - what about rounding? */
+       file_hdr.frametableoffset = htolel(netmon->frame_table_offset);
+       file_hdr.frametablelength =
+           htolel(netmon->frame_table_index * sizeof *netmon->frame_table);
+       nwritten = fwrite(&file_hdr, 1, sizeof file_hdr, wdh->fh);
+       if (nwritten != sizeof file_hdr) {
+               if (err != NULL) {
+                       if (nwritten == 0 && ferror(wdh->fh))
+                               *err = errno;
+                       else
+                               *err = WTAP_ERR_SHORT_WRITE;
+               }
+               return FALSE;
        }
-       wth->phdr.pkt_encap = wth->file_encap;
 
-       return data_offset;
+       return TRUE;
 }