fix for netmon 3.x captures
[metze/wireshark/wip.git] / wiretap / netmon.c
index 85f3234121b977919135711c1e7e31df0f340b59..7fe128ea10a57f96f9e25fc49b38a0bd30e5832f 100644 (file)
@@ -3,27 +3,15 @@
  * Wiretap Library
  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
 #include <errno.h>
 #include <string.h>
+#include <wsutil/unicode-utils.h>
 #include "wtap-int.h"
 #include "file_wrappers.h"
-#include "buffer.h"
 #include "atm.h"
 #include "pcap-encap.h"
 #include "netmon.h"
@@ -73,8 +61,8 @@ struct netmon_hdr {
        guint32 userdatalength;         /* user data size */
        guint32 commentdataoffset;      /* comment data offset */
        guint32 commentdatalength;      /* comment data size */
-       guint32 statisticsoffset;       /* offset to statistics structure */
-       guint32 statisticslength;       /* length of statistics structure */
+       guint32 processinfooffset;      /* offset to process info structure */
+       guint32 processinfocount;       /* number of process info structures */
        guint32 networkinfooffset;      /* offset to network info structure */
        guint32 networkinfolength;      /* length of network info structure */
 };
@@ -121,6 +109,38 @@ struct netmonrec_2_3_trlr {
        guint8 timezone_index;          /* index of time zone information */
 };
 
+struct netmonrec_comment {
+       guint32 numFramePerComment;     /* Currently, this is always set to 1. Each comment is attached to only one frame. */
+       guint32 frameOffset;            /* Offset in the capture file table that indicates the beginning of the frame.  Key used to match comment with frame */
+       guint8* title;                  /* Comment title */
+       guint32 descLength;             /* Number of bytes in the comment description. Must be at least zero. */
+       guint8* description;            /* Comment description */
+};
+
+/* Just the first few fields of netmonrec_comment so it can be read sequentially from file */
+struct netmonrec_comment_header {
+       guint32 numFramePerComment;
+       guint32 frameOffset;
+       guint32 titleLength;
+};
+
+union ip_address {
+       guint32 ipv4;
+       ws_in6_addr ipv6;
+};
+
+struct netmonrec_process_info {
+       guint8* path;                           /* A Unicode string of length PathSize */
+       guint32 iconSize;
+       guint8* iconData;
+       guint32 pid;
+       guint16 localPort;
+       guint16 remotePort;
+       gboolean isIPv6;
+       union ip_address localAddr;
+       union ip_address remoteAddr;
+};
+
 /*
  * The link-layer header on ATM packets.
  */
@@ -132,15 +152,35 @@ struct netmon_atm_hdr {
 };
 
 typedef struct {
-       time_t  start_secs;
-       guint32 start_nsecs;
-       guint8  version_major;
-       guint8  version_minor;
+       time_t  start_secs;
+       guint32 start_nsecs;
+       guint8  version_major;
+       guint8  version_minor;
        guint32 *frame_table;
-       guint32 frame_table_size;
-       guint   current_frame;
+       guint32 frame_table_size;
+       GHashTable* comment_table;
+       GHashTable* process_info_table;
+       guint current_frame;
 } netmon_t;
 
+/*
+ * Maximum pathname length supported in the process table; the length
+ * is in a 32-bit field, so we impose a limit to prevent attempts to
+ * allocate too much memory.
+ *
+ * See
+ *
+ *    https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396#maxpath
+ *
+ * The NetMon 3.4 "Capture File Format" documentation says "PathSize must be
+ * greater than 0, and less than MAX_PATH (260 characters)", but, as per that
+ * link above, that limit has been raised in more recent systems.
+ *
+ * We pick a limit of 65536, as that should handle a path length of 32767
+ * UTF-16 octet pairs plus a trailing NUL octet pair.
+ */
+#define MATH_PROCINFO_PATH_SIZE                65536
+
 /*
  * XXX - at least in some NetMon 3.4 VPN captures, the per-packet
  * link-layer type is 0, but the packets have Ethernet headers.
@@ -175,22 +215,192 @@ static const int netmon_encap[] = {
 #define NETMON_NET_DNS_CACHE           0xFFFE
 #define NETMON_NET_NETMON_FILTER       0xFFFF
 
-static int netmon_read(wtap *wth, int *err, gchar **err_info,
+static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
     gint64 *data_offset);
-static int netmon_seek_read(wtap *wth, gint64 seek_off,
-    struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info);
+static gboolean netmon_seek_read(wtap *wth, gint64 seek_off,
+    wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
 static gboolean netmon_read_atm_pseudoheader(FILE_T fh,
     union wtap_pseudo_header *pseudo_header, 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 gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
-    const guint8 *pd, int *err);
-static gboolean netmon_dump_close(wtap_dumper *wdh, int *err);
-
-int netmon_open(wtap *wth, int *err, gchar **err_info)
+static void netmon_close(wtap *wth);
+static gboolean netmon_dump(wtap_dumper *wdh, const wtap_rec *rec,
+    const guint8 *pd, int *err, gchar **err_info);
+static gboolean netmon_dump_finish(wtap_dumper *wdh, int *err);
+
+/*
+ * Convert a counted UTF-16 string, which is probably also null-terminated
+ * but is not guaranteed to be null-terminated (as it came from a file),
+ * to a null-terminated UTF-8 string.
+ */
+static guint8 *
+utf_16_to_utf_8(const guint8 *in, guint32 length)
+{
+       guint8 *result, *out;
+       gunichar2 uchar2;
+       gunichar uchar;
+       size_t n_bytes;
+       guint32 i;
+
+       /*
+        * Get the length of the resulting UTF-8 string, and validate
+        * the input string in the process.
+        */
+       n_bytes = 0;
+       for (i = 0; i + 1 < length && (uchar2 = pletoh16(in + i)) != '\0';
+           i += 2) {
+               if (IS_LEAD_SURROGATE(uchar2)) {
+                       /*
+                        * Lead surrogate.  Must be followed by a trail
+                        * surrogate.
+                        */
+                       gunichar2 lead_surrogate;
+
+                       i += 2;
+                       if (i + 1 >= length) {
+                               /*
+                                * Oops, string ends with a lead surrogate.
+                                * Ignore this for now.
+                                * XXX - insert "substitute" character?
+                                * Report the error in some other fashion?
+                                */
+                               break;
+                       }
+                       lead_surrogate = uchar2;
+                       uchar2 = pletoh16(in + i);
+                       if (uchar2 == '\0') {
+                               /*
+                                * Oops, string ends with a lead surrogate.
+                                * Ignore this for now.
+                                * XXX - insert "substitute" character?
+                                * Report the error in some other fashion?
+                                */
+                               break;
+                       }
+                       if (IS_TRAIL_SURROGATE(uchar2)) {
+                               /* Trail surrogate. */
+                               uchar = SURROGATE_VALUE(lead_surrogate, uchar2);
+                               n_bytes += g_unichar_to_utf8(uchar, NULL);
+                       } else {
+                               /*
+                                * Not a trail surrogate.
+                                * Ignore the entire pair.
+                                * XXX - insert "substitute" character?
+                                * Report the error in some other fashion?
+                                */
+                               ;
+                       }
+               } else {
+                       if (IS_TRAIL_SURROGATE(uchar2)) {
+                               /*
+                                * Trail surrogate without a preceding
+                                * lead surrogate.  Ignore it.
+                                * XXX - insert "substitute" character?
+                                * Report the error in some other fashion?
+                                */
+                               ;
+                       } else {
+                               /*
+                                * Non-surrogate; just count it.
+                                */
+                               n_bytes += g_unichar_to_utf8(uchar2, NULL);
+                       }
+               }
+       }
+
+       /*
+        * Now allocate a buffer big enough for the UTF-8 string plus a
+        * trailing NUL, and generate the string.
+        */
+       result = (guint8 *)g_malloc(n_bytes + 1);
+
+       out = result;
+       for (i = 0; i + 1 < length && (uchar2 = pletoh16(in + i)) != '\0';
+           i += 2) {
+               if (IS_LEAD_SURROGATE(uchar2)) {
+                       /*
+                        * Lead surrogate.  Must be followed by a trail
+                        * surrogate.
+                        */
+                       gunichar2 lead_surrogate;
+
+                       i += 2;
+                       if (i + 1 >= length) {
+                               /*
+                                * Oops, string ends with a lead surrogate.
+                                * Ignore this for now.
+                                * XXX - insert "substitute" character?
+                                * Report the error in some other fashion?
+                                */
+                               break;
+                       }
+                       lead_surrogate = uchar2;
+                       uchar2 = pletoh16(in + i);
+                       if (uchar2 == '\0') {
+                               /*
+                                * Oops, string ends with a lead surrogate.
+                                * Ignore this for now.
+                                * XXX - insert "substitute" character?
+                                * Report the error in some other fashion?
+                                */
+                               break;
+                       }
+                       if (IS_TRAIL_SURROGATE(uchar2)) {
+                               /* Trail surrogate. */
+                               uchar = SURROGATE_VALUE(lead_surrogate, uchar2);
+                               out += g_unichar_to_utf8(uchar, out);
+                       } else {
+                               /*
+                                * Not a trail surrogate.
+                                * Ignore the entire pair.
+                                * XXX - insert "substitute" character?
+                                * Report the error in some other fashion?
+                                */
+                               ;
+                       }
+               } else {
+                       if (IS_TRAIL_SURROGATE(uchar2)) {
+                               /*
+                                * Trail surrogate without a preceding
+                                * lead surrogate.  Ignore it.
+                                * XXX - insert "substitute" character?
+                                * Report the error in some other fashion?
+                                */
+                               ;
+                       } else {
+                               /*
+                                * Non-surrogate; just count it.
+                                */
+                               out += g_unichar_to_utf8(uchar2, out);
+                       }
+               }
+       }
+       *out = '\0';
+
+       /*
+        * XXX - if i < length, this means we were handed an odd
+        * number of bytes, so it was not a valid UTF-16 string.
+        */
+       return result;
+}
+
+
+static void netmonrec_comment_destroy(gpointer key) {
+       struct netmonrec_comment *comment = (struct netmonrec_comment*) key;
+
+       g_free(comment->title);
+       g_free(comment->description);
+       g_free(comment);
+}
+
+static void netmonrec_process_info_destroy(gpointer key) {
+       struct netmonrec_process_info *process_info = (struct netmonrec_process_info*) key;
+
+       g_free(process_info->path);
+       g_free(process_info->iconData);
+       g_free(process_info);
+}
+
+wtap_open_return_val netmon_open(wtap *wth, int *err, gchar **err_info)
 {
-       int bytes_read;
        char magic[MAGIC_SIZE];
        struct netmon_hdr hdr;
        int file_type;
@@ -199,36 +409,32 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
        guint32 frame_table_length;
        guint32 frame_table_size;
        guint32 *frame_table;
-#ifdef WORDS_BIGENDIAN
+       guint32 comment_table_offset, process_info_table_offset;
+       guint32 comment_table_size, process_info_table_count;
+       GHashTable *comment_table, *process_info_table;
+       struct netmonrec_comment* comment_rec;
+       gint64 file_size = wtap_file_size(wth, err);
+#if G_BYTE_ORDER == G_BIG_ENDIAN
        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, MAGIC_SIZE, wth->fh);
-       if (bytes_read != MAGIC_SIZE) {
-               *err = file_error(wth->fh, err_info);
-               if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
-                       return -1;
-               return 0;
+       if (!wtap_read_bytes(wth->fh, magic, MAGIC_SIZE, err, err_info)) {
+               if (*err != WTAP_ERR_SHORT_READ)
+                       return WTAP_OPEN_ERROR;
+               return WTAP_OPEN_NOT_MINE;
        }
 
        if (memcmp(magic, netmon_1_x_magic, MAGIC_SIZE) != 0 &&
            memcmp(magic, netmon_2_x_magic, MAGIC_SIZE) != 0) {
-               return 0;
+               return WTAP_OPEN_NOT_MINE;
        }
 
        /* Read the rest of the header. */
-       errno = WTAP_ERR_CANT_READ;
-       bytes_read = file_read(&hdr, sizeof hdr, wth->fh);
-       if (bytes_read != sizeof hdr) {
-               *err = file_error(wth->fh, err_info);
-               if (*err == 0)
-                       *err = WTAP_ERR_SHORT_READ;
-               return -1;
-       }
+       if (!wtap_read_bytes(wth->fh, &hdr, sizeof hdr, err, err_info))
+               return WTAP_OPEN_ERROR;
 
        switch (hdr.ver_major) {
 
@@ -243,32 +449,34 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
        default:
                *err = WTAP_ERR_UNSUPPORTED;
                *err_info = g_strdup_printf("netmon: major version %u unsupported", hdr.ver_major);
-               return -1;
+               return WTAP_OPEN_ERROR;
        }
 
        hdr.network = pletoh16(&hdr.network);
        if (hdr.network >= NUM_NETMON_ENCAPS
            || netmon_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
-               *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+               *err = WTAP_ERR_UNSUPPORTED;
                *err_info = g_strdup_printf("netmon: network type %u unknown or unsupported",
                    hdr.network);
-               return -1;
+               return WTAP_OPEN_ERROR;
        }
 
        /* This is a netmon file */
        wth->file_type_subtype = file_type;
-       netmon = (netmon_t *)g_malloc(sizeof(netmon_t));
+       netmon = (netmon_t *)g_malloc0(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;
 
+#if 0
        /* 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];
+#endif
+       wth->file_encap = netmon_encap[hdr.network];
 
        wth->snapshot_length = 0;       /* not available in header */
        /*
@@ -303,18 +511,29 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
        netmon->version_minor = hdr.ver_minor;
 
        /*
-        * No frame table allocated yet; initialize these in case we
-        * get an error before allocating it or when trying to allocate
-        * it, so that the attempt to release the private data on failure
-        * doesn't crash.
+        * Get the offset of the frame index table.
         */
-       netmon->frame_table_size = 0;
-       netmon->frame_table = NULL;
+       frame_table_offset = pletoh32(&hdr.frametableoffset);
 
        /*
-        * Get the offset of the frame index table.
+        * For NetMon 2.2 format and later, get the offset and length of
+        * the comment index table and process info table.
+        *
+        * For earlier versions, set them to zero; they appear to be
+        * uninitialized, so they're not necessarily zero.
         */
-       frame_table_offset = pletoh32(&hdr.frametableoffset);
+       if ((netmon->version_major == 2 && netmon->version_minor >= 2) ||
+           netmon->version_major > 2) {
+               comment_table_offset = pletoh32(&hdr.commentdataoffset);
+               comment_table_size = pletoh32(&hdr.commentdatalength);
+               process_info_table_offset = pletoh32(&hdr.processinfooffset);
+               process_info_table_count = pletoh32(&hdr.processinfocount);
+       } else {
+               comment_table_offset = 0;
+               comment_table_size = 0;
+               process_info_table_offset = 0;
+               process_info_table_count = 0;
+       }
 
        /*
         * It appears that some NetMon 2.x files don't have the
@@ -333,13 +552,13 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
                *err = WTAP_ERR_BAD_FILE;
                *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);
-               return -1;
+               return WTAP_OPEN_ERROR;
        }
        if (frame_table_size == 0) {
                *err = WTAP_ERR_BAD_FILE;
                *err_info = g_strdup_printf("netmon: frame table length is %u, which means it's less than one entry in size",
                    frame_table_length);
-               return -1;
+               return WTAP_OPEN_ERROR;
        }
        /*
         * XXX - clamp the size of the frame table, so that we don't
@@ -358,29 +577,368 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
                *err = WTAP_ERR_BAD_FILE;
                *err_info = g_strdup_printf("netmon: frame table length is %u, which is larger than we support",
                    frame_table_length);
-               return -1;
+               return WTAP_OPEN_ERROR;
        }
        if (file_seek(wth->fh, frame_table_offset, SEEK_SET, err) == -1) {
-               return -1;
+               return WTAP_OPEN_ERROR;
+       }
+
+       /*
+        * Sanity check the comment table information before we bother to allocate
+        * large chunks of memory for the frame table
+        */
+       if (comment_table_size > 0) {
+               /*
+                * XXX - clamp the size of the comment table, so that we don't
+                * attempt to allocate a huge comment table and fail.
+                *
+                * Just use same size requires as frame table
+                */
+               if (comment_table_size > 512*1024*1024) {
+                       *err = WTAP_ERR_BAD_FILE;
+                       *err_info = g_strdup_printf("netmon: comment table size is %u, which is larger than we support",
+                               comment_table_size);
+                       return WTAP_OPEN_ERROR;
+               }
+
+               if (comment_table_size < 17) {
+                       *err = WTAP_ERR_BAD_FILE;
+                       *err_info = g_strdup_printf("netmon: comment table size is %u, which is too small to use",
+                               comment_table_size);
+                       return WTAP_OPEN_ERROR;
+               }
+
+               if (comment_table_offset > file_size) {
+                       *err = WTAP_ERR_BAD_FILE;
+                       *err_info = g_strdup_printf("netmon: comment table offset (%u) is larger than file",
+                               comment_table_offset);
+                       return WTAP_OPEN_ERROR;
+               }
        }
+
+       /*
+        * Sanity check the process info table information before we bother to allocate
+        * large chunks of memory for the frame table
+        */
+       if ((process_info_table_offset > 0) && (process_info_table_count > 0)) {
+               /*
+                * XXX - clamp the size of the process info table, so that we don't
+                * attempt to allocate a huge process info table and fail.
+                */
+               if (process_info_table_count > 512*1024) {
+                       *err = WTAP_ERR_BAD_FILE;
+                       *err_info = g_strdup_printf("netmon: process info table size is %u, which is larger than we support",
+                               process_info_table_count);
+                       return WTAP_OPEN_ERROR;
+               }
+
+               if (process_info_table_offset > file_size) {
+                       *err = WTAP_ERR_BAD_FILE;
+                       *err_info = g_strdup_printf("netmon: process info table offset (%u) is larger than file",
+                               process_info_table_offset);
+                       return WTAP_OPEN_ERROR;
+               }
+       }
+
+       /*
+        * Return back to the frame table offset
+        */
+       if (file_seek(wth->fh, frame_table_offset, SEEK_SET, err) == -1) {
+               return WTAP_OPEN_ERROR;
+       }
+
+       /*
+        * Sanity check the process info table information before we bother to allocate
+        * large chunks of memory for the frame table
+        */
+
        frame_table = (guint32 *)g_try_malloc(frame_table_length);
        if (frame_table_length != 0 && frame_table == NULL) {
                *err = ENOMEM;  /* we assume we're out of memory */
-               return -1;
+               return WTAP_OPEN_ERROR;
        }
-       errno = WTAP_ERR_CANT_READ;
-       bytes_read = file_read(frame_table, frame_table_length, wth->fh);
-       if ((guint32)bytes_read != frame_table_length) {
-               *err = file_error(wth->fh, err_info);
-               if (*err == 0)
-                       *err = WTAP_ERR_SHORT_READ;
+       if (!wtap_read_bytes(wth->fh, frame_table, frame_table_length,
+           err, err_info)) {
                g_free(frame_table);
-               return -1;
+               return WTAP_OPEN_ERROR;
        }
        netmon->frame_table_size = frame_table_size;
        netmon->frame_table = frame_table;
 
-#ifdef WORDS_BIGENDIAN
+       if (comment_table_size > 0) {
+               comment_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, netmonrec_comment_destroy);
+               if (comment_table == NULL) {
+                       *err = ENOMEM;  /* we assume we're out of memory */
+                       return WTAP_OPEN_ERROR;
+               }
+
+               /* Make sure the file contains the full comment section */
+               if (file_seek(wth->fh, comment_table_offset+comment_table_size, SEEK_SET, err) == -1) {
+                       g_hash_table_destroy(comment_table);
+                       return WTAP_OPEN_ERROR;
+               }
+
+               if (file_seek(wth->fh, comment_table_offset, SEEK_SET, err) == -1) {
+                       /* Shouldn't fail... */
+                       g_hash_table_destroy(comment_table);
+                       return WTAP_OPEN_ERROR;
+               }
+
+               while (comment_table_size > 16) {
+                       struct netmonrec_comment_header comment_header;
+                       guint32 title_length;
+                       guint32 desc_length;
+                       guint8 *utf16_str;
+
+                       /* Read the first 12 bytes of the structure */
+                       if (!wtap_read_bytes(wth->fh, &comment_header, 12, err, err_info)) {
+                               g_hash_table_destroy(comment_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+                       comment_table_size -= 12;
+
+                       /* Make sure comment size is sane */
+                       title_length = pletoh32(&comment_header.titleLength);
+                       if (title_length == 0) {
+                               *err = WTAP_ERR_BAD_FILE;
+                               *err_info = g_strdup("netmon: comment title size can't be 0");
+                               g_hash_table_destroy(comment_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+                       if (title_length > comment_table_size) {
+                               *err = WTAP_ERR_BAD_FILE;
+                               *err_info = g_strdup_printf("netmon: comment title size is %u, which is larger than the amount remaining in the comment section (%u)",
+                                               title_length, comment_table_size);
+                               g_hash_table_destroy(comment_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+
+                       comment_rec = g_new0(struct netmonrec_comment, 1);
+                       comment_rec->numFramePerComment = pletoh32(&comment_header.numFramePerComment);
+                       comment_rec->frameOffset = pletoh32(&comment_header.frameOffset);
+
+                       g_hash_table_insert(comment_table, GUINT_TO_POINTER(comment_rec->frameOffset), comment_rec);
+
+                       /*
+                        * Read in the comment title.
+                        *
+                        * It is in UTF-16-encoded Unicode, and the title
+                        * size is a count of octets, not octet pairs or
+                        * Unicode characters.
+                        */
+                       utf16_str = (guint8*)g_malloc(title_length);
+                       if (!wtap_read_bytes(wth->fh, utf16_str, title_length,
+                           err, err_info)) {
+                               g_hash_table_destroy(comment_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+                       comment_table_size -= title_length;
+
+                       /*
+                        * Now convert it to UTF-8 for internal use.
+                        */
+                       comment_rec->title = utf_16_to_utf_8(utf16_str,
+                           title_length);
+                       g_free(utf16_str);
+
+                       if (comment_table_size < 4) {
+                               *err = WTAP_ERR_BAD_FILE;
+                               *err_info = g_strdup("netmon: corrupt comment section");
+                               g_hash_table_destroy(comment_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+
+                       if (!wtap_read_bytes(wth->fh, &desc_length, 4, err, err_info)) {
+                               g_hash_table_destroy(comment_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+                       comment_table_size -= 4;
+
+                       comment_rec->descLength = pletoh32(&desc_length);
+                       if (comment_rec->descLength > 0) {
+                               /* Make sure comment size is sane */
+                               if (comment_rec->descLength > comment_table_size) {
+                                       *err = WTAP_ERR_BAD_FILE;
+                                       *err_info = g_strdup_printf("netmon: comment description size is %u, which is larger than the amount remaining in the comment section (%u)",
+                                                               comment_rec->descLength, comment_table_size);
+                                       g_hash_table_destroy(comment_table);
+                                       return WTAP_OPEN_ERROR;
+                               }
+
+                               comment_rec->description = (guint8*)g_malloc(comment_rec->descLength);
+
+                               /* Read the comment description */
+                               if (!wtap_read_bytes(wth->fh, comment_rec->description, comment_rec->descLength, err, err_info)) {
+                                       g_hash_table_destroy(comment_table);
+                                       return WTAP_OPEN_ERROR;
+                               }
+
+                               comment_table_size -= comment_rec->descLength;
+                       }
+               }
+               netmon->comment_table = comment_table;
+       }
+
+       if ((process_info_table_offset > 0) && (process_info_table_count > 0)) {
+               guint16 version;
+
+               /* Go to the process table offset */
+               if (file_seek(wth->fh, process_info_table_offset, SEEK_SET, err) == -1) {
+                       return WTAP_OPEN_ERROR;
+               }
+
+               process_info_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, netmonrec_process_info_destroy);
+               if (process_info_table == NULL) {
+                       *err = ENOMEM;  /* we assume we're out of memory */
+                       return WTAP_OPEN_ERROR;
+               }
+
+               /* Read the version (ignored for now) */
+               if (!wtap_read_bytes(wth->fh, &version, 2, err, err_info)) {
+                       g_hash_table_destroy(process_info_table);
+                       return WTAP_OPEN_ERROR;
+               }
+
+               while (process_info_table_count > 0)
+               {
+                       struct netmonrec_process_info* process_info;
+                       guint32 tmp32;
+                       guint16 tmp16;
+                       guint32 path_size;
+                       guint8 *utf16_str;
+
+                       process_info = g_new0(struct netmonrec_process_info, 1);
+
+                       /* Read path */
+                       if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) {
+                               g_free(process_info);
+                               g_hash_table_destroy(process_info_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+
+                       path_size = pletoh32(&tmp32);
+                       if (path_size > MATH_PROCINFO_PATH_SIZE) {
+                               *err = WTAP_ERR_BAD_FILE;
+                               *err_info = g_strdup_printf("netmon: Path size for process info record is %u, which is larger than allowed max value (%u)",
+                                   path_size, MATH_PROCINFO_PATH_SIZE);
+                               g_free(process_info);
+                               g_hash_table_destroy(process_info_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+
+                       /*
+                        * Read in the path string.
+                        *
+                        * It is in UTF-16-encoded Unicode, and the path
+                        * size is a count of octets, not octet pairs or
+                        * Unicode characters.
+                        */
+                       utf16_str = (guint8*)g_malloc(path_size);
+                       if (!wtap_read_bytes(wth->fh, utf16_str, path_size,
+                           err, err_info)) {
+                               g_free(process_info);
+                               g_hash_table_destroy(process_info_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+
+                       /*
+                        * Now convert it to UTF-8 for internal use.
+                        */
+                       process_info->path = utf_16_to_utf_8(utf16_str,
+                           path_size);
+                       g_free(utf16_str);
+
+                       /* Read icon (currently not saved) */
+                       if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) {
+                               g_free(process_info);
+                               g_hash_table_destroy(process_info_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+
+                       process_info->iconSize = pletoh32(&tmp32);
+
+                       /* XXX - skip the icon for now */
+                       if (file_seek(wth->fh, process_info->iconSize, SEEK_CUR, err) == -1) {
+                               g_free(process_info);
+                               g_hash_table_destroy(process_info_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+                       process_info->iconSize = 0;
+
+                       if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) {
+                               g_free(process_info);
+                               g_hash_table_destroy(process_info_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+                       process_info->pid = pletoh32(&tmp32);
+
+                       /* XXX - Currently index process information by PID */
+                       g_hash_table_insert(process_info_table, GUINT_TO_POINTER(process_info->pid), process_info);
+
+                       /* Read local port */
+                       if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) {
+                               g_hash_table_destroy(process_info_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+                       process_info->localPort = pletoh16(&tmp16);
+
+                       /* Skip padding */
+                       if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) {
+                               g_hash_table_destroy(process_info_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+
+                       /* Read remote port */
+                       if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) {
+                               g_hash_table_destroy(process_info_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+                       process_info->remotePort = pletoh16(&tmp16);
+
+                       /* Skip padding */
+                       if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) {
+                               g_hash_table_destroy(process_info_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+
+                       /* Determine IP version */
+                       if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) {
+                               g_hash_table_destroy(process_info_table);
+                               return WTAP_OPEN_ERROR;
+                       }
+                       process_info->isIPv6 = ((pletoh32(&tmp32) == 0) ? FALSE : TRUE);
+
+                       if (process_info->isIPv6) {
+                               if (!wtap_read_bytes(wth->fh, &process_info->localAddr.ipv6, 16, err, err_info)) {
+                                       g_hash_table_destroy(process_info_table);
+                                       return WTAP_OPEN_ERROR;
+                               }
+                               if (!wtap_read_bytes(wth->fh, &process_info->remoteAddr.ipv6, 16, err, err_info)) {
+                                       g_hash_table_destroy(process_info_table);
+                                       return WTAP_OPEN_ERROR;
+                               }
+                       } else {
+                               guint8 ipbuffer[16];
+                               if (!wtap_read_bytes(wth->fh, ipbuffer, 16, err, err_info)) {
+                                       g_hash_table_destroy(process_info_table);
+                                       return WTAP_OPEN_ERROR;
+                               }
+                               process_info->localAddr.ipv4 = pletoh32(ipbuffer);
+
+                               if (!wtap_read_bytes(wth->fh, ipbuffer, 16, err, err_info)) {
+                                       g_hash_table_destroy(process_info_table);
+                                       return WTAP_OPEN_ERROR;
+                               }
+                               process_info->remoteAddr.ipv4 = pletoh32(ipbuffer);
+                       }
+
+                       process_info_table_count--;
+               }
+
+               netmon->process_info_table = process_info_table;
+       }
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
        /*
         * OK, now byte-swap the frame table.
         */
@@ -397,7 +955,7 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
                 * Version 1.x of the file format supports
                 * millisecond precision.
                 */
-               wth->tsprecision = WTAP_FILE_TSPREC_MSEC;
+               wth->file_tsprec = WTAP_TSPREC_MSEC;
                break;
 
        case 2:
@@ -407,77 +965,62 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
                 * currently support that, so say
                 * "nanosecond precision" for now.
                 */
-               wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
+               wth->file_tsprec = WTAP_TSPREC_NSEC;
                break;
        }
-       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 */
+       return WTAP_OPEN_MINE;
 }
 
 static void
-netmon_set_pseudo_header_info(int pkt_encap, struct wtap_pkthdr *phdr,
-    Buffer *buf)
+netmon_set_pseudo_header_info(wtap_rec *rec, Buffer *buf)
 {
-       guint8 *pd = buffer_start_ptr(buf);
-
-       switch (pkt_encap) {
+       switch (rec->rec_header.packet_header.pkt_encap) {
 
        case WTAP_ENCAP_ATM_PDUS:
                /*
                 * Attempt to guess from the packet data, the VPI, and
                 * the VCI information about the type of traffic.
                 */
-               atm_guess_traffic_type(phdr, pd);
+               atm_guess_traffic_type(rec, ws_buffer_start_ptr(buf));
                break;
 
        case WTAP_ENCAP_ETHERNET:
                /*
                 * We assume there's no FCS in this frame.
                 */
-               phdr->pseudo_header.eth.fcs_len = 0;
+               rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
                break;
 
        case WTAP_ENCAP_IEEE_802_11_NETMON:
                /*
                 * 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".
+                * frames (and control and extension frames ?) may
+                * or may not have an FCS and data frames don't.
+                * (Netmon capture files have been seen for this
+                *  encapsulation having management frames either
+                *  completely with or without an FCS. Also: instances have been
+                *  seen where both Management and Control frames
+                *  do not have an FCS).
+                * An "FCS length" of -2 means "NetMon weirdness".
                 */
-               phdr->pseudo_header.ieee_802_11.fcs_len = -2;
-               phdr->pseudo_header.ieee_802_11.decrypted = FALSE;
+               memset(&rec->rec_header.packet_header.pseudo_header.ieee_802_11, 0, sizeof(rec->rec_header.packet_header.pseudo_header.ieee_802_11));
+               rec->rec_header.packet_header.pseudo_header.ieee_802_11.fcs_len = -2;
+               rec->rec_header.packet_header.pseudo_header.ieee_802_11.decrypted = FALSE;
+               rec->rec_header.packet_header.pseudo_header.ieee_802_11.datapad = FALSE;
+               rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_UNKNOWN;
                break;
        }
 }
 
-static gboolean netmon_process_rec_header(wtap *wth, FILE_T fh,
-    struct wtap_pkthdr *phdr, int *err, gchar **err_info)
+typedef enum {
+       SUCCESS,
+       FAILURE,
+       RETRY
+} process_record_retval;
+
+static process_record_retval
+netmon_process_record(wtap *wth, FILE_T fh, wtap_rec *rec,
+    Buffer *buf, int *err, gchar **err_info)
 {
        netmon_t *netmon = (netmon_t *)wth->priv;
        int     hdr_size = 0;
@@ -485,13 +1028,21 @@ static gboolean netmon_process_rec_header(wtap *wth, FILE_T fh,
                struct netmonrec_1_x_hdr hdr_1_x;
                struct netmonrec_2_x_hdr hdr_2_x;
        }       hdr;
-       int     bytes_read;
        gint64  delta = 0;      /* signed - frame times can be before the nominal start */
        gint64  t;
        time_t  secs;
-       guint32 nsecs;
+       int     nsecs;
        guint32 packet_size = 0;
        guint32 orig_size = 0;
+       int     trlr_size;
+       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;
+       struct netmonrec_comment* comment_rec = NULL;
 
        /* Read record header. */
        switch (netmon->version_major) {
@@ -504,16 +1055,8 @@ static gboolean netmon_process_rec_header(wtap *wth, FILE_T fh,
                hdr_size = sizeof (struct netmonrec_2_x_hdr);
                break;
        }
-       errno = WTAP_ERR_CANT_READ;
-
-       bytes_read = file_read(&hdr, hdr_size, fh);
-       if (bytes_read != hdr_size) {
-               *err = file_error(fh, err_info);
-               if (*err == 0 && bytes_read != 0) {
-                       *err = WTAP_ERR_SHORT_READ;
-               }
-               return FALSE;
-       }
+       if (!wtap_read_bytes_or_eof(fh, &hdr, hdr_size, err, err_info))
+               return FAILURE;
 
        switch (netmon->version_major) {
 
@@ -527,17 +1070,19 @@ static gboolean netmon_process_rec_header(wtap *wth, FILE_T fh,
                packet_size = pletoh32(&hdr.hdr_2_x.incl_len);
                break;
        }
-       if (packet_size > WTAP_MAX_PACKET_SIZE) {
+       if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
                /*
                 * Probably a corrupt capture file; don't blow up trying
                 * to allocate space for an immensely-large packet.
                 */
                *err = WTAP_ERR_BAD_FILE;
                *err_info = g_strdup_printf("netmon: File has %u-byte packet, bigger than maximum of %u",
-                   packet_size, WTAP_MAX_PACKET_SIZE);
-               return FALSE;
+                   packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+               return FAILURE;
        }
 
+       rec->rec_type = REC_TYPE_PACKET;
+
        /*
         * If this is an ATM packet, the first
         * "sizeof (struct netmon_atm_hdr)" bytes have destination and
@@ -556,11 +1101,11 @@ static gboolean netmon_process_rec_header(wtap *wth, FILE_T fh,
                        *err = WTAP_ERR_BAD_FILE;
                        *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;
+                       return FAILURE;
                }
-               if (!netmon_read_atm_pseudoheader(fh, &phdr->pseudo_header,
+               if (!netmon_read_atm_pseudoheader(fh, &rec->rec_header.packet_header.pseudo_header,
                    err, err_info))
-                       return FALSE;   /* Read error */
+                       return FAILURE; /* Read error */
 
                /*
                 * Don't count the pseudo-header as part of the packet.
@@ -639,155 +1184,325 @@ static gboolean netmon_process_rec_header(wtap *wth, FILE_T fh,
                secs--;
        }
        secs += (time_t)(t/1000000000);
-       nsecs = (guint32)(t%1000000000);
-       phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
-       phdr->ts.secs = netmon->start_secs + secs;
-       phdr->ts.nsecs = nsecs;
-       phdr->caplen = packet_size;
-       phdr->len = orig_size;
+       nsecs = (int)(t%1000000000);
+       rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+       rec->ts.secs = netmon->start_secs + secs;
+       rec->ts.nsecs = nsecs;
+       rec->rec_header.packet_header.caplen = packet_size;
+       rec->rec_header.packet_header.len = orig_size;
 
-       return TRUE;
-}
+       /*
+        * Read the packet data.
+        */
+       if (!wtap_read_packet_bytes(fh, buf, rec->rec_header.packet_header.caplen, err, err_info))
+               return FAILURE;
 
-typedef enum {
-       SUCCESS,
-       FAILURE,
-       RETRY
-} process_trailer_retval;
+       /*
+        * For version 2.1 and later, there's additional information
+        * after the frame data.
+        */
+       if ((netmon->version_major == 2 && netmon->version_minor >= 1) ||
+           netmon->version_major > 2) {
+               if (netmon->version_major > 2) {
+                       /*
+                        * Asssume 2.3 format, for now.
+                        */
+                       trlr_size = (int)sizeof (struct netmonrec_2_3_trlr);
+               } else {
+                       switch (netmon->version_minor) {
 
-static process_trailer_retval netmon_process_rec_trailer(netmon_t *netmon,
-    FILE_T fh, struct wtap_pkthdr *phdr, int *err, gchar **err_info)
-{
-       int     trlr_size;
+                       case 1:
+                               trlr_size = (int)sizeof (struct netmonrec_2_1_trlr);
+                               break;
 
-       trlr_size = (int)netmon_trailer_size(netmon);
-       if (trlr_size != 0) {
-               /*
-                * I haz a trailer.
+                       case 2:
+                               trlr_size = (int)sizeof (struct netmonrec_2_2_trlr);
+                               break;
+
+                       default:
+                               trlr_size = (int)sizeof (struct netmonrec_2_3_trlr);
+                               break;
+                       }
+               }
+
+               if (!wtap_read_bytes(fh, &trlr, trlr_size, err, err_info))
+                       return FAILURE;
+
+               network = pletoh16(trlr.trlr_2_1.network);
+               if ((network >= 0xE080) && (network <= 0xE08A)) {
+                       /* These values "violate" the LINKTYPE_ media type values
+                        * in Microsoft Analyzer and are considered a MAExportedMediaType,
+                        * so they need their own WTAP_ types
+                        */
+                       switch (network)
+                       {
+                       case 0xE080:    // "WiFi Message"
+                               pkt_encap = WTAP_ENCAP_IEEE_802_11;
+                               break;
+                       case 0xE081:    // "Ndis Etw WiFi Channel Message"
+                       case 0xE082:    // "Fiddler Netmon Message"
+                       case 0xE089:    // "Pef Ndis Msg";
+                       case 0xE08A:    // "Pef Ndis Wifi Meta Msg";
+                               *err = WTAP_ERR_UNSUPPORTED;
+                               *err_info = g_strdup_printf("netmon: network type %u unknown or unsupported", network);
+                               return FAILURE;
+                       case 0xE083:
+                               pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_V4;
+                               break;
+                       case 0xE084:
+                               pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_V6;
+                               break;
+                       case 0xE085:
+                               pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_2V4;
+                               break;
+                       case 0xE086:
+                               pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_2V6;
+                               break;
+                       case 0xE087:
+                               pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_AUTH_V4;
+                               break;
+                       case 0xE088:
+                               pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_AUTH_V6;
+                               break;
+                       default:
+                               pkt_encap = WTAP_ENCAP_UNKNOWN;
+                               break;
+                       }
+               } else 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;
+                               *err_info = g_strdup_printf("netmon: converted pcap network type %u unknown or unsupported",
+                                   network);
+                               return FAILURE;
+                       }
+               } else if (network < NUM_NETMON_ENCAPS) {
+                       /*
+                        * Regular NetMon encapsulation.
+                        */
+                       pkt_encap = netmon_encap[network];
+                       if (pkt_encap == WTAP_ENCAP_UNKNOWN) {
+                               *err = WTAP_ERR_UNSUPPORTED;
+                               *err_info = g_strdup_printf("netmon: network type %u unknown or unsupported",
+                                   network);
+                               return FAILURE;
+                       }
+               } else {
+                       /*
+                        * Special packet type for metadata.
+                        */
+                       switch (network) {
+
+                       case NETMON_NET_NETEVENT:
+                               /*
+                                * Event Tracing event.
+                                *
+                                * http://msdn.microsoft.com/en-us/library/aa363759(VS.85).aspx
+                                */
+                               pkt_encap = WTAP_ENCAP_NETMON_NET_NETEVENT;
+                               break;
+
+                       case NETMON_NET_NETWORK_INFO_EX:
+                               /*
+                                * List of adapters on which the capture
+                                * was done.
+                                * XXX - this could be translated into pcapng
+                                * blocks but for now, just treat as a frame.
+                                */
+                               pkt_encap = WTAP_ENCAP_NETMON_NETWORK_INFO_EX;
+                               break;
+
+                       case NETMON_NET_PAYLOAD_HEADER:
+                               /*
+                                * Header for a fake frame constructed
+                                * by reassembly.
+                                */
+                               return RETRY;
+
+                       case NETMON_NET_NETWORK_INFO:
+                               /*
+                                * List of adapters on which the capture
+                                * was done.
+                                */
+                               return RETRY;
+
+                       case NETMON_NET_DNS_CACHE:
+                               /*
+                                * List of resolved IP addresses.
+                                */
+                               return RETRY;
+
+                       case NETMON_NET_NETMON_FILTER:
+                               /*
+                                * NetMon capture or display filter
+                                * string.
+                                */
+                               pkt_encap = WTAP_ENCAP_NETMON_NET_FILTER;
+                               break;
+
+                       default:
+                               *err = WTAP_ERR_UNSUPPORTED;
+                               *err_info = g_strdup_printf("netmon: network type %u unknown or unsupported",
+                                   network);
+                               return FAILURE;
+                       }
+               }
+
+               rec->rec_header.packet_header.pkt_encap = pkt_encap;
+               if (netmon->version_major > 2 || netmon->version_minor > 2) {
+                       guint64 d;
+
+                       d = pletoh64(trlr.trlr_2_3.utc_timestamp);
+
+                       /*
+                        * Get the time as seconds and nanoseconds.
+                        * and overwrite the time stamp obtained
+                        * from the record header.
+                        */
+                       if (!filetime_to_nstime(&rec->ts, d)) {
+                               *err = WTAP_ERR_BAD_FILE;
+                               *err_info = g_strdup("netmon: time stamp outside supported range");
+                               return FAILURE;
+                       }
+               }
+       }
+
+       netmon_set_pseudo_header_info(rec, buf);
+
+       /* If any header specific information is present, set it as pseudo header data
+        * and set the encapsulation type, so it can be handled to the netmon_header
+        * dissector for further processing
+        */
+       if (netmon->comment_table != NULL) {
+               comment_rec = (struct netmonrec_comment*)g_hash_table_lookup(netmon->comment_table, GUINT_TO_POINTER(netmon->frame_table[netmon->current_frame-1]));
+       }
+
+       if (comment_rec != NULL) {
+               union wtap_pseudo_header temp_header;
+
+               /* These are the current encapsulation types that NetMon uses.
+                * Save them off so they can be copied to the NetMon pseudoheader
                 */
-               phdr->pkt_encap = netmon_read_rec_trailer(fh,
-                   trlr_size, err, err_info);
-               if (phdr->pkt_encap == -1)
-                       return FAILURE; /* error */
-               if (phdr->pkt_encap == 0)
-                       return RETRY;
+               switch (rec->rec_header.packet_header.pkt_encap)
+               {
+               case WTAP_ENCAP_ATM_PDUS:
+                       memcpy(&temp_header.atm, &rec->rec_header.packet_header.pseudo_header.atm, sizeof(temp_header.atm));
+                       break;
+               case WTAP_ENCAP_ETHERNET:
+                       memcpy(&temp_header.eth, &rec->rec_header.packet_header.pseudo_header.eth, sizeof(temp_header.eth));
+                       break;
+               case WTAP_ENCAP_IEEE_802_11_NETMON:
+                       memcpy(&temp_header.ieee_802_11, &rec->rec_header.packet_header.pseudo_header.ieee_802_11, sizeof(temp_header.ieee_802_11));
+                       break;
+               }
+               memset(&rec->rec_header.packet_header.pseudo_header.netmon, 0, sizeof(rec->rec_header.packet_header.pseudo_header.netmon));
+
+               /* Save the current encapsulation type to the NetMon pseudoheader */
+               rec->rec_header.packet_header.pseudo_header.netmon.sub_encap = rec->rec_header.packet_header.pkt_encap;
+
+               /* Copy the comment data */
+               rec->rec_header.packet_header.pseudo_header.netmon.title = comment_rec->title;
+               rec->rec_header.packet_header.pseudo_header.netmon.descLength = comment_rec->descLength;
+               rec->rec_header.packet_header.pseudo_header.netmon.description = comment_rec->description;
+
+               /* Copy the saved pseudoheaders to the netmon pseudoheader structure */
+               switch (rec->rec_header.packet_header.pkt_encap)
+               {
+               case WTAP_ENCAP_ATM_PDUS:
+                       memcpy(&rec->rec_header.packet_header.pseudo_header.netmon.subheader.atm, &temp_header.atm, sizeof(temp_header.atm));
+                       break;
+               case WTAP_ENCAP_ETHERNET:
+                       memcpy(&rec->rec_header.packet_header.pseudo_header.netmon.subheader.eth, &temp_header.eth, sizeof(temp_header.eth));
+                       break;
+               case WTAP_ENCAP_IEEE_802_11_NETMON:
+                       memcpy(&rec->rec_header.packet_header.pseudo_header.netmon.subheader.ieee_802_11, &temp_header.ieee_802_11, sizeof(temp_header.ieee_802_11));
+                       break;
+               }
+
+               /* Encapsulation type is now something that can be passed to netmon_header dissector */
+               rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETMON_HEADER;
        }
 
        return SUCCESS;
 }
 
 /* Read the next packet */
-static int netmon_read(wtap *wth, int *err, gchar **err_info,
+static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
     gint64 *data_offset)
 {
        netmon_t *netmon = (netmon_t *)wth->priv;
        gint64  rec_offset;
 
-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(netmon->frame_table);
-               netmon->frame_table = NULL;
-               *err = 0;       /* it's just an EOF, not an error */
-               return -1;
-       }
+       for (;;) {
+               /* Have we reached the end of the packet data? */
+               if (netmon->current_frame >= netmon->frame_table_size) {
+                       *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).
-
-          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 (file_tell(wth->fh) != rec_offset) {
-               if (file_seek(wth->fh, rec_offset, SEEK_SET, err) == -1)
-                       return -1;
-       }
-       netmon->current_frame++;
+               /* 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).
+
+                  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 (file_tell(wth->fh) != rec_offset) {
+                       if (file_seek(wth->fh, rec_offset, SEEK_SET, err) == -1)
+                               return FALSE;
+               }
+               netmon->current_frame++;
 
-       *data_offset = file_tell(wth->fh);
+               *data_offset = file_tell(wth->fh);
 
-       if (!netmon_process_rec_header(wth, wth->fh, &wth->phdr,
-           err, err_info))
-               return -1;
+               switch (netmon_process_record(wth, wth->fh, &wth->rec,
+                   wth->rec_data, err, err_info)) {
 
-       if (!wtap_read_packet_bytes(wth->fh, wth->frame_buffer,
-           wth->phdr.caplen, err, err_info))
-               return -1;      /* Read error */
+               case RETRY:
+                       continue;
 
-       /*
-        * For version 2.1 and later, there's additional information
-        * after the frame data.
-        */
-       switch (netmon_process_rec_trailer(netmon, wth->fh, &wth->phdr,
-           err, err_info)) {
+               case SUCCESS:
+                       return TRUE;
 
-       case RETRY:
-               goto again;
-
-       case SUCCESS:
-               break;
-
-       case FAILURE:
-               return -1;
+               case FAILURE:
+                       return FALSE;
+               }
        }
-
-       netmon_set_pseudo_header_info(wth->phdr.pkt_encap, &wth->phdr,
-           wth->frame_buffer);
-       return REC_TYPE_PACKET;
 }
 
-static int
+static gboolean
 netmon_seek_read(wtap *wth, gint64 seek_off,
-    struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
+    wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
 {
-       netmon_t *netmon = (netmon_t *)wth->priv;
-
        if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
-               return -1;
-
-       if (!netmon_process_rec_header(wth, wth->random_fh, phdr,
-           err, err_info))
-               return -1;
-
-       /*
-        * Read the packet data.
-        */
-       if (!wtap_read_packet_bytes(wth->random_fh, buf, phdr->caplen, err,
-           err_info))
-               return -1;
+               return FALSE;
 
-       /*
-        * For version 2.1 and later, there's additional information
-        * after the frame data.
-        */
-       switch (netmon_process_rec_trailer(netmon, wth->random_fh, phdr,
-           err, err_info)) {
+       switch (netmon_process_record(wth, wth->random_fh, rec, buf, err,
+           err_info)) {
 
-       case RETRY:
+       default:
                /*
                 * This should not happen.
                 */
                *err = WTAP_ERR_BAD_FILE;
                *err_info = g_strdup("netmon: saw metadata in netmon_seek_read");
-               return -1;
+               return FALSE;
 
        case SUCCESS:
-               break;
+               return TRUE;
 
        case FAILURE:
-               return -1;
+               return FALSE;
        }
-
-       netmon_set_pseudo_header_info(phdr->pkt_encap, phdr, buf);
-
-       return REC_TYPE_PACKET;
 }
 
 static gboolean
@@ -795,17 +1510,11 @@ netmon_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
     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, sizeof (struct netmon_atm_hdr), fh);
-       if (bytes_read != sizeof (struct netmon_atm_hdr)) {
-               *err = file_error(fh, err_info);
-               if (*err == 0)
-                       *err = WTAP_ERR_SHORT_READ;
+       if (!wtap_read_bytes(fh, &atm_phdr, sizeof (struct netmon_atm_hdr),
+           err, err_info))
                return FALSE;
-       }
 
        vpi = g_ntohs(atm_phdr.vpi);
        vci = g_ntohs(atm_phdr.vci);
@@ -824,94 +1533,9 @@ netmon_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
        return TRUE;
 }
 
-/*
- * 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)
-{
-       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 = pletoh16(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 */
-}
-
 /* Throw away the frame table used by the sequential I/O stream. */
 static void
-netmon_sequential_close(wtap *wth)
+netmon_close(wtap *wth)
 {
        netmon_t *netmon = (netmon_t *)wth->priv;
 
@@ -919,6 +1543,16 @@ netmon_sequential_close(wtap *wth)
                g_free(netmon->frame_table);
                netmon->frame_table = NULL;
        }
+
+       if (netmon->comment_table != NULL) {
+               g_hash_table_destroy(netmon->comment_table);
+               netmon->comment_table = NULL;
+       }
+
+       if (netmon->process_info_table != NULL) {
+               g_hash_table_destroy(netmon->process_info_table);
+               netmon->process_info_table = NULL;
+       }
 }
 
 typedef struct {
@@ -958,7 +1592,7 @@ int netmon_dump_can_write_encap_1_x(int encap)
         * format.
         */
        if (encap < 0 || (unsigned) encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
-               return WTAP_ERR_UNSUPPORTED_ENCAP;
+               return WTAP_ERR_UNWRITABLE_ENCAP;
 
        return 0;
 }
@@ -973,7 +1607,7 @@ int netmon_dump_can_write_encap_2_x(int encap)
                return 0;
 
        if (encap < 0 || (unsigned) encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
-               return WTAP_ERR_UNSUPPORTED_ENCAP;
+               return WTAP_ERR_UNWRITABLE_ENCAP;
 
        return 0;
 }
@@ -992,7 +1626,7 @@ gboolean netmon_dump_open(wtap_dumper *wdh, int *err)
                return FALSE;
 
        wdh->subtype_write = netmon_dump;
-       wdh->subtype_close = netmon_dump_close;
+       wdh->subtype_finish = netmon_dump_finish;
 
        netmon = (netmon_dump_t *)g_malloc(sizeof(netmon_dump_t));
        wdh->priv = (void *)netmon;
@@ -1008,10 +1642,10 @@ gboolean netmon_dump_open(wtap_dumper *wdh, int *err)
 
 /* 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 guint8 *pd, int *err)
+static gboolean netmon_dump(wtap_dumper *wdh, const wtap_rec *rec,
+    const guint8 *pd, int *err, gchar **err_info _U_)
 {
-       const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
+       const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
        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;
@@ -1024,6 +1658,12 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
        gint64  secs;
        gint32  nsecs;
 
+       /* We can only write packet records. */
+       if (rec->rec_type != REC_TYPE_PACKET) {
+               *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+               return FALSE;
+       }
+
        switch (wdh->file_type_subtype) {
 
        case WTAP_FILE_TYPE_SUBTYPE_NETMON_1_x:
@@ -1031,7 +1671,7 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
                 * The length fields are 16-bit, so there's a hard limit
                 * of 65535.
                 */
-               if (phdr->caplen > 65535) {
+               if (rec->rec_header.packet_header.caplen > 65535) {
                        *err = WTAP_ERR_PACKET_TOO_LARGE;
                        return FALSE;
                }
@@ -1039,7 +1679,7 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
 
        case WTAP_FILE_TYPE_SUBTYPE_NETMON_2_x:
                /* Don't write anything we're not willing to read. */
-               if (phdr->caplen > WTAP_MAX_PACKET_SIZE) {
+               if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
                        *err = WTAP_ERR_PACKET_TOO_LARGE;
                        return FALSE;
                }
@@ -1048,7 +1688,7 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
        default:
                /* We should never get here - our open routine
                   should only get called for the types above. */
-               *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
+               *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
                return FALSE;
        }
 
@@ -1056,20 +1696,20 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
                /*
                 * Is this network type supported?
                 */
-               if (phdr->pkt_encap < 0 ||
-                   (unsigned) phdr->pkt_encap >= NUM_WTAP_ENCAPS ||
-                   wtap_encap[phdr->pkt_encap] == -1) {
+               if (rec->rec_header.packet_header.pkt_encap < 0 ||
+                   (unsigned) rec->rec_header.packet_header.pkt_encap >= NUM_WTAP_ENCAPS ||
+                   wtap_encap[rec->rec_header.packet_header.pkt_encap] == -1) {
                        /*
                         * No.  Fail.
                         */
-                       *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+                       *err = WTAP_ERR_UNWRITABLE_ENCAP;
                        return FALSE;
                }
 
                /*
                 * Fill in the trailer with the network type.
                 */
-               phtoles(rec_2_x_trlr.network, wtap_encap[phdr->pkt_encap]);
+               phtoles(rec_2_x_trlr.network, wtap_encap[rec->rec_header.packet_header.pkt_encap]);
        }
 
        /*
@@ -1095,9 +1735,9 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
         * sub-millisecond part of the time stamp off.
         */
        if (!netmon->got_first_record_time) {
-               netmon->first_record_time.secs = phdr->ts.secs;
+               netmon->first_record_time.secs = rec->ts.secs;
                netmon->first_record_time.nsecs =
-                   (phdr->ts.nsecs/1000000)*1000000;
+                   (rec->ts.nsecs/1000000)*1000000;
                netmon->got_first_record_time = TRUE;
        }
 
@@ -1105,8 +1745,8 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
                atm_hdrsize = sizeof (struct netmon_atm_hdr);
        else
                atm_hdrsize = 0;
-       secs = (gint64)(phdr->ts.secs - netmon->first_record_time.secs);
-       nsecs = phdr->ts.nsecs - netmon->first_record_time.nsecs;
+       secs = (gint64)(rec->ts.secs - netmon->first_record_time.secs);
+       nsecs = rec->ts.nsecs - netmon->first_record_time.nsecs;
        while (nsecs < 0) {
                /*
                 * Propagate a borrow into the seconds.
@@ -1136,16 +1776,16 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
 
        case WTAP_FILE_TYPE_SUBTYPE_NETMON_1_x:
                rec_1_x_hdr.ts_delta = GUINT32_TO_LE(secs*1000 + (nsecs + 500000)/1000000);
-               rec_1_x_hdr.orig_len = GUINT16_TO_LE(phdr->len + atm_hdrsize);
-               rec_1_x_hdr.incl_len = GUINT16_TO_LE(phdr->caplen + atm_hdrsize);
+               rec_1_x_hdr.orig_len = GUINT16_TO_LE(rec->rec_header.packet_header.len + atm_hdrsize);
+               rec_1_x_hdr.incl_len = GUINT16_TO_LE(rec->rec_header.packet_header.caplen + atm_hdrsize);
                hdrp = &rec_1_x_hdr;
                hdr_size = sizeof rec_1_x_hdr;
                break;
 
        case WTAP_FILE_TYPE_SUBTYPE_NETMON_2_x:
                rec_2_x_hdr.ts_delta = GUINT64_TO_LE(secs*1000000 + (nsecs + 500)/1000);
-               rec_2_x_hdr.orig_len = GUINT32_TO_LE(phdr->len + atm_hdrsize);
-               rec_2_x_hdr.incl_len = GUINT32_TO_LE(phdr->caplen + atm_hdrsize);
+               rec_2_x_hdr.orig_len = GUINT32_TO_LE(rec->rec_header.packet_header.len + atm_hdrsize);
+               rec_2_x_hdr.incl_len = GUINT32_TO_LE(rec->rec_header.packet_header.caplen + atm_hdrsize);
                hdrp = &rec_2_x_hdr;
                hdr_size = sizeof rec_2_x_hdr;
                break;
@@ -1153,7 +1793,7 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
        default:
                /* We should never get here - our open routine
                   should only get called for the types above. */
-               *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
+               *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
                return FALSE;
        }
 
@@ -1181,9 +1821,9 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
                rec_size += sizeof atm_hdr;
        }
 
-       if (!wtap_dump_file_write(wdh, pd, phdr->caplen, err))
+       if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
                return FALSE;
-       rec_size += phdr->caplen;
+       rec_size += rec->rec_header.packet_header.caplen;
 
        if (wdh->encap == WTAP_ENCAP_PER_PACKET) {
                /*
@@ -1249,7 +1889,7 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
 
 /* Finish writing to a dump file.
    Returns TRUE on success, FALSE on failure. */
-static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
+static gboolean netmon_dump_finish(wtap_dumper *wdh, int *err)
 {
        netmon_dump_t *netmon = (netmon_dump_t *)wdh->priv;
        size_t n_to_write;
@@ -1306,7 +1946,7 @@ static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
                /* 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;
+                       *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
                return FALSE;
        }
        if (!wtap_dump_file_write(wdh, magicp, magic_size, err))
@@ -1348,3 +1988,16 @@ static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
 
        return TRUE;
 }
+
+/*
+ * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */