Whitespace fixes:
[obnox/wireshark/wip.git] / wiretap / netmon.c
index 19eb125bcdc8b7a032f1ac2dc055faf61c26b1bc..cb3ed981f656943bb3741fdfc117155e05c4a571 100644 (file)
@@ -29,6 +29,7 @@
 #include "file_wrappers.h"
 #include "buffer.h"
 #include "atm.h"
+#include "pcap-encap.h"
 #include "netmon.h"
 
 /* The file at
@@ -94,6 +95,30 @@ struct netmonrec_2_x_hdr {
        guint32 incl_len;       /* number of octets captured in file */
 };
 
+/*
+ * Network Monitor 2.1 and later record trailers; documented in the Network
+ * Monitor 3.x help files, for 3.3 and later, although they don't clearly
+ * state how the trailer format changes from version to version.
+ *
+ * Some fields are multi-byte integers, but they're not aligned on their
+ * natural boundaries.
+ */
+struct netmonrec_2_1_trlr {
+       guint8 network[2];              /* network type for this packet */
+};
+
+struct netmonrec_2_2_trlr {
+       guint8 network[2];              /* network type for this packet */
+       guint8 process_info_index[4];   /* index into the process info table */
+};
+
+struct netmonrec_2_3_trlr {
+       guint8 network[2];              /* network type for this packet */
+       guint8 process_info_index[4];   /* index into the process info table */
+       guint8 utc_timestamp[8];        /* packet time stamp, as .1 us units since January 1, 1601, 00:00:00 UTC */
+       guint8 timezone_index;          /* index of time zone information */
+};
+
 /*
  * The link-layer header on ATM packets.
  */
@@ -104,17 +129,62 @@ struct netmon_atm_hdr {
        guint16 vci;            /* VCI */
 };
 
+typedef struct {
+       time_t  start_secs;
+       guint32 start_usecs;
+       guint8  version_major;
+       guint8  version_minor;
+       guint32 *frame_table;
+       guint32 frame_table_size;
+       guint   current_frame;
+} netmon_t;
+
+/*
+ * XXX - at least in some NetMon 3.4 VPN captures, the per-packet
+ * link-layer type is 0, but the packets have Ethernet headers.
+ * We handle this by mapping 0 to WTAP_ENCAP_ETHERNET; should we,
+ * instead, use the per-file link-layer type?
+ */
+static const int netmon_encap[] = {
+       WTAP_ENCAP_ETHERNET,
+       WTAP_ENCAP_ETHERNET,
+       WTAP_ENCAP_TOKEN_RING,
+       WTAP_ENCAP_FDDI_BITSWAPPED,
+       WTAP_ENCAP_ATM_PDUS,    /* NDIS WAN - this is what's used for ATM */
+       WTAP_ENCAP_UNKNOWN,     /* NDIS LocalTalk, but format 2.x uses it for IP-over-IEEE 1394 */
+       WTAP_ENCAP_IEEE802_11_NETMON_RADIO,
+                               /* NDIS "DIX", but format 2.x uses it for 802.11 */
+       WTAP_ENCAP_RAW_IP,      /* NDIS ARCNET raw, but format 2.x uses it for "Tunneling interfaces" */
+       WTAP_ENCAP_RAW_IP,      /* NDIS ARCNET 878.2, but format 2.x uses it for "Wireless WAN" */
+       WTAP_ENCAP_RAW_IP,      /* NDIS ATM (no, this is NOT used for ATM); format 2.x uses it for "Raw IP Frames" */
+       WTAP_ENCAP_UNKNOWN,     /* NDIS Wireless WAN */
+       WTAP_ENCAP_UNKNOWN      /* NDIS IrDA */
+};
+#define NUM_NETMON_ENCAPS (sizeof netmon_encap / sizeof netmon_encap[0])
+
+/*
+ * Special link-layer types.
+ */
+#define NETMON_NET_PCAP_BASE           0xE000
+#define NETMON_NET_NETEVENT            0xFFE0
+#define NETMON_NET_NETWORK_INFO_EX     0xFFFB
+#define NETMON_NET_PAYLOAD_HEADER      0xFFFC
+#define NETMON_NET_NETWORK_INFO                0xFFFD
+#define NETMON_NET_DNS_CACHE           0xFFFE
+#define NETMON_NET_NETMON_FILTER       0xFFFF
+
 static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
-    long *data_offset);
-static gboolean netmon_seek_read(wtap *wth, long seek_off,
+    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);
+    union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info);
 static gboolean netmon_read_rec_data(FILE_T fh, guchar *pd, int length,
-    int *err);
+    int *err, gchar **err_info);
+static int netmon_read_rec_trailer(FILE_T fh, int trlr_size, int *err,
+    gchar **err_info);
 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);
@@ -125,21 +195,6 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
        char magic[sizeof netmon_1_x_magic];
        struct netmon_hdr hdr;
        int file_type;
-       static const int netmon_encap[] = {
-               WTAP_ENCAP_UNKNOWN,
-               WTAP_ENCAP_ETHERNET,
-               WTAP_ENCAP_TOKEN_RING,
-               WTAP_ENCAP_FDDI_BITSWAPPED,
-               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;
@@ -148,13 +203,14 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
 #ifdef WORDS_BIGENDIAN
        unsigned int i;
 #endif
+       netmon_t *netmon;
 
        /* Read in the string that should be at the start of a Network
         * Monitor file */
        errno = WTAP_ERR_CANT_READ;
-       bytes_read = file_read(magic, 1, sizeof magic, wth->fh);
+       bytes_read = file_read(magic, sizeof magic, wth->fh);
        if (bytes_read != sizeof magic) {
-               *err = file_error(wth->fh);
+               *err = file_error(wth->fh, err_info);
                if (*err != 0)
                        return -1;
                return 0;
@@ -167,9 +223,9 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
 
        /* Read the rest of the header. */
        errno = WTAP_ERR_CANT_READ;
-       bytes_read = file_read(&hdr, 1, sizeof hdr, wth->fh);
+       bytes_read = file_read(&hdr, sizeof hdr, wth->fh);
        if (bytes_read != sizeof hdr) {
-               *err = file_error(wth->fh);
+               *err = file_error(wth->fh, err_info);
                if (*err != 0)
                        return -1;
                return 0;
@@ -202,12 +258,19 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
 
        /* This is a netmon file */
        wth->file_type = file_type;
-       wth->capture.netmon = g_malloc(sizeof(netmon_t));
+       netmon = (netmon_t *)g_malloc(sizeof(netmon_t));
+       wth->priv = (void *)netmon;
        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];
+
+       /* NetMon capture file formats v2.1+ use per-packet encapsulation types.  NetMon 3 sets the value in
+        * the header to 1 (Ethernet) for backwards compability. */
+       if((hdr.ver_major == 2 && hdr.ver_minor >= 1) || hdr.ver_major > 2)
+               wth->file_encap = WTAP_ENCAP_PER_PACKET;
+       else
+               wth->file_encap = netmon_encap[hdr.network];
+
        wth->snapshot_length = 0;       /* not available in header */
        /*
         * Convert the time stamp to a "time_t" and a number of
@@ -220,7 +283,7 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
        tm.tm_min = pletohs(&hdr.ts_min);
        tm.tm_sec = pletohs(&hdr.ts_sec);
        tm.tm_isdst = -1;
-       wth->capture.netmon->start_secs = mktime(&tm);
+       netmon->start_secs = mktime(&tm);
        /*
         * XXX - what if "secs" is -1?  Unlikely, but if the capture was
         * done in a time zone that switches between standard and summer
@@ -235,9 +298,10 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
         * intervals since 1601-01-01 00:00:00 "UTC", there, instead
         * of stuffing a SYSTEMTIME, which is time-zone-dependent, there?).
         */
-       wth->capture.netmon->start_usecs = pletohs(&hdr.ts_msec)*1000;
+       netmon->start_usecs = pletohs(&hdr.ts_msec)*1000;
 
-       wth->capture.netmon->version_major = hdr.ver_major;
+       netmon->version_major = hdr.ver_major;
+       netmon->version_minor = hdr.ver_minor;
 
        /*
         * Get the offset of the frame index table.
@@ -256,38 +320,38 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
         * in it as the offsets of the frames.
         */
        frame_table_length = pletohl(&hdr.frametablelength);
-       frame_table_size = frame_table_length / sizeof (guint32);
+       frame_table_size = frame_table_length / (guint32)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);
+               g_free(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);
+               g_free(netmon);
                return -1;
        }
        if (file_seek(wth->fh, frame_table_offset, SEEK_SET, err) == -1) {
-               g_free(wth->capture.netmon);
+               g_free(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);
+       bytes_read = file_read(frame_table, frame_table_length, wth->fh);
        if ((guint32)bytes_read != frame_table_length) {
-               *err = file_error(wth->fh);
+               *err = file_error(wth->fh, err_info);
                if (*err == 0)
                        *err = WTAP_ERR_SHORT_READ;
                g_free(frame_table);
-               g_free(wth->capture.netmon);
+               g_free(netmon);
                return -1;
        }
-       wth->capture.netmon->frame_table_size = frame_table_size;
-       wth->capture.netmon->frame_table = frame_table;
+       netmon->frame_table_size = frame_table_size;
+       netmon->frame_table = frame_table;
 
 #ifdef WORDS_BIGENDIAN
        /*
@@ -298,17 +362,77 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
 #endif
 
        /* Set up to start reading at the first frame. */
-       wth->capture.netmon->current_frame = 0;
+       netmon->current_frame = 0;
        wth->tsprecision = WTAP_FILE_TSPREC_USEC;
 
        return 1;
 }
 
+static size_t
+netmon_trailer_size(netmon_t *netmon)
+{
+       if ((netmon->version_major == 2 && netmon->version_minor >= 1) ||
+           netmon->version_major > 2) {
+               if (netmon->version_major > 2) {
+                       /*
+                        * Asssume 2.3 format, for now.
+                        */
+                       return sizeof (struct netmonrec_2_3_trlr);
+               } else {
+                       switch (netmon->version_minor) {
+
+                       case 1:
+                               return sizeof (struct netmonrec_2_1_trlr);
+
+                       case 2:
+                               return sizeof (struct netmonrec_2_2_trlr);
+
+                       default:
+                               return sizeof (struct netmonrec_2_3_trlr);
+                       }
+               }
+       }
+       return 0;       /* no trailer */
+}
+
+static void
+netmon_set_pseudo_header_info(int pkt_encap,
+    union wtap_pseudo_header *pseudo_header, guchar *pd, int length)
+{
+       switch (pkt_encap) {
+
+       case WTAP_ENCAP_ATM_PDUS:
+               /*
+                * Attempt to guess from the packet data, the VPI, and
+                * the VCIinformation about the type of traffic.
+                */
+               atm_guess_traffic_type(pd, length, pseudo_header);
+               break;
+
+       case WTAP_ENCAP_ETHERNET:
+               /*
+                * We assume there's no FCS in this frame.
+                */
+               pseudo_header->eth.fcs_len = 0;
+               break;
+
+       case WTAP_ENCAP_IEEE802_11_NETMON_RADIO:
+               /*
+                * It appears to be the case that management
+                * frames have an FCS and data frames don't;
+                * I'm not sure about control frames.  An
+                * "FCS length" of -2 means "NetMon weirdness".
+                */
+               pseudo_header->ieee_802_11.fcs_len = -2;
+               break;
+       }
+}
+
 /* Read the next packet */
 static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
-    long *data_offset)
+    gint64 *data_offset)
 {
-       netmon_t *netmon = wth->capture.netmon;
+       netmon_t *netmon = (netmon_t *)wth->priv;
        guint32 packet_size = 0;
        guint32 orig_size = 0;
        int     bytes_read;
@@ -317,18 +441,21 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
                struct netmonrec_2_x_hdr hdr_2_x;
        }       hdr;
        int     hdr_size = 0;
+       int     trlr_size;
        int     rec_offset;
        guint8  *data_ptr;
+       gint64  delta = 0;      /* signed - frame times can be before the nominal start */
        time_t  secs;
        guint32 usecs;
        double  t;
 
+again:
        /* Have we reached the end of the packet data? */
        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;
+               g_free(netmon->frame_table);
+               netmon->frame_table = NULL;
                *err = 0;       /* it's just an EOF, not an error */
                return FALSE;
        }
@@ -336,7 +463,13 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
        /* 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). */
+          so we don't want to do it unconditionally).
+
+          Yes, the current record could be before the previous
+          record.  At least some captures put the trailer record
+          with statistics as the first physical record in the
+          file, but set the frame table up so it's the last
+          record in sequence. */
        rec_offset = netmon->frame_table[netmon->current_frame];
        if (wth->data_offset != rec_offset) {
                wth->data_offset = rec_offset;
@@ -358,9 +491,9 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
        }
        errno = WTAP_ERR_CANT_READ;
 
-       bytes_read = file_read(&hdr, 1, hdr_size, wth->fh);
+       bytes_read = file_read(&hdr, hdr_size, wth->fh);
        if (bytes_read != hdr_size) {
-               *err = file_error(wth->fh);
+               *err = file_error(wth->fh, err_info);
                if (*err == 0 && bytes_read != 0) {
                        *err = WTAP_ERR_SHORT_READ;
                }
@@ -409,33 +542,30 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
                         * 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",
+                       *err_info = g_strdup_printf("netmon: ATM file has a %u-byte packet, too small to have even an ATM pseudo-header",
                            packet_size);
                        return FALSE;
                }
                if (!netmon_read_atm_pseudoheader(wth->fh, &wth->pseudo_header,
-                   err))
+                   err, err_info))
                        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);
+               orig_size -= (guint)sizeof (struct netmon_atm_hdr);
+               packet_size -= (guint)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;
+       default:
                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))
+       if (!netmon_read_rec_data(wth->fh, data_ptr, packet_size, err,
+           err_info))
                return FALSE;   /* Read error */
        wth->data_offset += packet_size;
 
@@ -443,14 +573,25 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
        switch (netmon->version_major) {
 
        case 1:
-               t += ((double)pletohl(&hdr.hdr_1_x.ts_delta))*1000;
+               /*
+                * According to Paul Long, this offset is unsigned.
+                * It's 32 bits, so the maximum value will fit in
+                * a gint64 such as delta, even after multiplying
+                * it by 1000.
+                *
+                * pletohl() returns a guint32; we cast it to gint64
+                * before multiplying, so that the product doesn't
+                * overflow a guint32.
+                */
+               delta = ((gint64)pletohl(&hdr.hdr_1_x.ts_delta))*1000;
                break;
 
        case 2:
-               t += (double)pletohl(&hdr.hdr_2_x.ts_delta_lo)
-                   + (double)pletohl(&hdr.hdr_2_x.ts_delta_hi)*4294967296.0;
+               delta = pletohl(&hdr.hdr_2_x.ts_delta_lo)
+                   | (((guint64)pletohl(&hdr.hdr_2_x.ts_delta_hi)) << 32);
                break;
        }
+       t += (double)delta;
        secs = (time_t)(t/1000000);
        usecs = (guint32)(t - (double)secs*1000000);
        wth->phdr.ts.secs = netmon->start_secs + secs;
@@ -459,22 +600,40 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
        wth->phdr.len = orig_size;
 
        /*
-        * Attempt to guess from the packet data, the VPI, and the VCI
-        * information about the type of traffic.
+        * For version 2.1 and later, there's additional information
+        * after the frame data.
         */
-       if (wth->file_encap == WTAP_ENCAP_ATM_PDUS) {
-               atm_guess_traffic_type(data_ptr, packet_size,
-                   &wth->pseudo_header);
+       trlr_size = (int)netmon_trailer_size(netmon);
+       if (trlr_size != 0) {
+               /*
+                * I haz a trailer.
+                */
+               wth->phdr.pkt_encap = netmon_read_rec_trailer(wth->fh,
+                   trlr_size, err, err_info);
+               if (wth->phdr.pkt_encap == -1)
+                       return FALSE;   /* error */
+               wth->data_offset += trlr_size;
+               if (wth->phdr.pkt_encap == 0)
+                       goto again;
+               netmon_set_pseudo_header_info(wth->phdr.pkt_encap,
+                   &wth->pseudo_header, data_ptr, packet_size);
+       } else {
+               netmon_set_pseudo_header_info(wth->file_encap,
+                   &wth->pseudo_header, data_ptr, packet_size);
        }
 
        return TRUE;
 }
 
 static gboolean
-netmon_seek_read(wtap *wth, long seek_off,
+netmon_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)
 {
+       netmon_t *netmon = (netmon_t *)wth->priv;
+       int     trlr_size;
+       int     pkt_encap;
+
        if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
                return FALSE;
 
@@ -482,48 +641,62 @@ netmon_seek_read(wtap *wth, long seek_off,
 
        case WTAP_ENCAP_ATM_PDUS:
                if (!netmon_read_atm_pseudoheader(wth->random_fh, pseudo_header,
-                   err)) {
+                   err, err_info)) {
                        /* Read error */
                        return FALSE;
                }
                break;
-
-       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))
+       if (!netmon_read_rec_data(wth->random_fh, pd, length, err, err_info))
                return FALSE;
 
        /*
-        * Attempt to guess from the packet data, the VPI, and the VCI
-        * information about the type of traffic.
+        * For version 2.1 and later, there's additional information
+        * after the frame data.
         */
-       if (wth->file_encap == WTAP_ENCAP_ATM_PDUS)
-               atm_guess_traffic_type(pd, length, pseudo_header);
+       trlr_size = (int)netmon_trailer_size(netmon);
+       if (trlr_size != 0) {
+               /*
+                * I haz a trailer.
+                */
+               pkt_encap = netmon_read_rec_trailer(wth->random_fh,
+                   trlr_size, err, err_info);
+               if (pkt_encap == -1)
+                       return FALSE;   /* error */
+               if (pkt_encap == 0) {
+                       /*
+                        * This should not happen.
+                        */
+                       *err = WTAP_ERR_BAD_RECORD;
+                       *err_info = g_strdup("netmon: saw metadata in netmon_seek_read");
+                       return FALSE;
+               }
+               netmon_set_pseudo_header_info(pkt_encap, pseudo_header,
+                   pd, length);
+       } else {
+               netmon_set_pseudo_header_info(wth->file_encap, pseudo_header,
+                   pd, length);
+       }
 
        return TRUE;
 }
 
 static gboolean
 netmon_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
-    int *err)
+    int *err, gchar **err_info)
 {
        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);
+       bytes_read = file_read(&atm_phdr, sizeof (struct netmon_atm_hdr), fh);
        if (bytes_read != sizeof (struct netmon_atm_hdr)) {
-               *err = file_error(fh);
+               *err = file_error(fh, err_info);
                if (*err == 0)
                        *err = WTAP_ERR_SHORT_READ;
                return FALSE;
@@ -547,15 +720,16 @@ netmon_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
 }
 
 static gboolean
-netmon_read_rec_data(FILE_T fh, guchar *pd, int length, int *err)
+netmon_read_rec_data(FILE_T fh, guchar *pd, int length, int *err,
+    gchar **err_info)
 {
        int     bytes_read;
 
        errno = WTAP_ERR_CANT_READ;
-       bytes_read = file_read(pd, 1, length, fh);
+       bytes_read = file_read(pd, length, fh);
 
        if (bytes_read != length) {
-               *err = file_error(fh);
+               *err = file_error(fh, err_info);
                if (*err == 0)
                        *err = WTAP_ERR_SHORT_READ;
                return FALSE;
@@ -563,26 +737,112 @@ netmon_read_rec_data(FILE_T fh, guchar *pd, int length, int *err)
        return TRUE;
 }
 
-/* Throw away the frame table used by the sequential I/O stream. */
-static void
-netmon_sequential_close(wtap *wth)
+/*
+ * Read a record trailer.
+ * On success, returns the packet encapsulation type.
+ * On error, returns -1 (which is WTAP_ENCAP_PER_PACKET, but we'd
+ * never return that on success).
+ * For metadata packets, returns 0 (which is WTAP_ENCAP_UNKNOWN, but
+ * we'd never return that on success).
+ */
+static int
+netmon_read_rec_trailer(FILE_T fh, int trlr_size, int *err, gchar **err_info)
 {
-       if (wth->capture.netmon->frame_table != NULL) {
-               g_free(wth->capture.netmon->frame_table);
-               wth->capture.netmon->frame_table = NULL;
+       int     bytes_read;
+       union {
+               struct netmonrec_2_1_trlr trlr_2_1;
+               struct netmonrec_2_2_trlr trlr_2_2;
+               struct netmonrec_2_3_trlr trlr_2_3;
+       }       trlr;
+       guint16 network;
+       int     pkt_encap;
+
+       errno = WTAP_ERR_CANT_READ;
+       bytes_read = file_read(&trlr, trlr_size, fh);
+       if (bytes_read != trlr_size) {
+               *err = file_error(fh, err_info);
+               if (*err == 0 && bytes_read != 0) {
+                       *err = WTAP_ERR_SHORT_READ;
+               }
+               return -1;      /* error */
+       }
+
+       network = pletohs(trlr.trlr_2_1.network);
+       if ((network & 0xF000) == NETMON_NET_PCAP_BASE) {
+               /*
+                * Converted pcap file - the LINKTYPE_ value
+                * is the network value with 0xF000 masked off.
+                */
+               network &= 0x0FFF;
+               pkt_encap = wtap_pcap_encap_to_wtap_encap(network);
+               if (pkt_encap == WTAP_ENCAP_UNKNOWN) {
+                       *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+                       *err_info = g_strdup_printf("netmon: converted pcap network type %u unknown or unsupported",
+                           network);
+                       return -1;      /* error */
+               }
+       } else if (network < NUM_NETMON_ENCAPS) {
+               /*
+                * Regular NetMon encapsulation.
+                */
+               pkt_encap = netmon_encap[network];
+               if (pkt_encap == WTAP_ENCAP_UNKNOWN) {
+                       *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+                       *err_info = g_strdup_printf("netmon: network type %u unknown or unsupported",
+                           network);
+                       return -1;      /* error */
+               }
+       } else {
+               /*
+                * Special packet type for metadata.
+                */
+               switch (network) {
+
+               case NETMON_NET_NETEVENT:
+               case NETMON_NET_NETWORK_INFO_EX:
+               case NETMON_NET_PAYLOAD_HEADER:
+               case NETMON_NET_NETWORK_INFO:
+               case NETMON_NET_DNS_CACHE:
+               case NETMON_NET_NETMON_FILTER:
+                       /*
+                        * Just ignore those record types, for
+                        * now.  Tell our caller to read the next
+                        * record.
+                        */
+                       return 0;
+
+               default:
+                       *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+                       *err_info = g_strdup_printf("netmon: network type %u unknown or unsupported",
+                           network);
+                       return -1;      /* error */
+               }
        }
+
+       return pkt_encap;       /* success */
 }
 
-/* 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.) */
+/* Throw away the frame table used by the sequential I/O stream. */
 static void
-netmon_close(wtap *wth)
+netmon_sequential_close(wtap *wth)
 {
-       g_free(wth->capture.netmon);
+       netmon_t *netmon = (netmon_t *)wth->priv;
+
+       if (netmon->frame_table != NULL) {
+               g_free(netmon->frame_table);
+               netmon->frame_table = NULL;
+       }
 }
 
+typedef struct {
+       gboolean got_first_record_time;
+       struct wtap_nstime first_record_time;
+       guint32 frame_table_offset;
+       guint32 *frame_table;
+       guint   frame_table_index;
+       guint   frame_table_size;
+} netmon_dump_t;
+
 static const int wtap_encap[] = {
        -1,             /* WTAP_ENCAP_UNKNOWN -> unsupported */
        1,              /* WTAP_ENCAP_ETHERNET -> NDIS Ethernet */
@@ -617,15 +877,9 @@ int netmon_dump_can_write_encap(int encap)
 
 /* 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)
+gboolean netmon_dump_open(wtap_dumper *wdh, 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;
-       }
+       netmon_dump_t *netmon;
 
        /* 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
@@ -639,12 +893,13 @@ gboolean netmon_dump_open(wtap_dumper *wdh, gboolean cant_seek, int *err)
        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;
+       netmon = (netmon_dump_t *)g_malloc(sizeof(netmon_dump_t));
+       wdh->priv = (void *)netmon;
+       netmon->frame_table_offset = CAPTUREFILE_HEADER_SIZE;
+       netmon->got_first_record_time = FALSE;
+       netmon->frame_table = NULL;
+       netmon->frame_table_index = 0;
+       netmon->frame_table_size = 0;
 
        return TRUE;
 }
@@ -654,12 +909,11 @@ gboolean netmon_dump_open(wtap_dumper *wdh, gboolean cant_seek, int *err)
 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;
+       netmon_dump_t *netmon = (netmon_dump_t *)wdh->priv;
        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;
@@ -716,14 +970,8 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
                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;
+       if (!wtap_dump_file_write(wdh, hdrp, hdr_size, err))
                return FALSE;
-       }
 
        if (wdh->encap == WTAP_ENCAP_ATM_PDUS) {
                /*
@@ -734,24 +982,12 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
                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;
+               if (!wtap_dump_file_write(wdh, &atm_hdr, sizeof atm_hdr, err))
                        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;
+       if (!wtap_dump_file_write(wdh, pd, phdr->caplen, err))
                return FALSE;
-       }
 
        /*
         * Stash the file offset of this frame.
@@ -778,7 +1014,7 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
        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;
+       netmon->frame_table_offset += (int) hdr_size + phdr->caplen + atm_hdrsize;
 
        return TRUE;
 }
@@ -787,9 +1023,8 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
    Returns TRUE on success, FALSE on failure. */
 static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
 {
-       netmon_dump_t *netmon = wdh->dump.netmon;
+       netmon_dump_t *netmon = (netmon_dump_t *)wdh->priv;
        size_t n_to_write;
-       size_t nwritten;
        struct netmon_hdr file_hdr;
        const char *magicp;
        size_t magic_size;
@@ -798,16 +1033,8 @@ static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
        /* 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;
-               }
+       if (!wtap_dump_file_write(wdh, netmon->frame_table, n_to_write, err))
                return FALSE;
-       }
 
        /* Now go fix up the file header. */
        fseek(wdh->fh, 0, SEEK_SET);
@@ -817,7 +1044,7 @@ static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
        case WTAP_FILE_NETMON_1_x:
                magicp = netmon_1_x_magic;
                magic_size = sizeof netmon_1_x_magic;
-               /* current NetMon version, for 1.x, is 1.1 */
+               /* NetMon file version, for 1.x, is 1.1 */
                file_hdr.ver_major = 1;
                file_hdr.ver_minor = 1;
                break;
@@ -825,7 +1052,10 @@ static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
        case WTAP_FILE_NETMON_2_x:
                magicp = netmon_2_x_magic;
                magic_size = sizeof netmon_2_x_magic;
-               /* current NetMon version, for 2.x, is 2.0 */
+               /*
+                * 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;
@@ -837,16 +1067,8 @@ static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
                        *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;
-               }
+       if (!wtap_dump_file_write(wdh, magicp, magic_size, err))
                return FALSE;
-       }
 
        file_hdr.network = htoles(wtap_encap[wdh->encap]);
        tm = localtime(&netmon->first_record_time.secs);
@@ -872,16 +1094,8 @@ static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
        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;
-               }
+       if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
                return FALSE;
-       }
 
        return TRUE;
 }