summaryrefslogtreecommitdiffstats
path: root/wiretap/netmon.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /wiretap/netmon.c
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'wiretap/netmon.c')
-rw-r--r--wiretap/netmon.c2038
1 files changed, 2038 insertions, 0 deletions
diff --git a/wiretap/netmon.c b/wiretap/netmon.c
new file mode 100644
index 0000000..861b1f9
--- /dev/null
+++ b/wiretap/netmon.c
@@ -0,0 +1,2038 @@
+/* netmon.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * 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 "atm.h"
+#include "pcap-encap.h"
+#include "netmon.h"
+
+/* The file at
+ *
+ * ftp://ftp.microsoft.com/developr/drg/cifs/cifs/Bhfile.zip
+ *
+ * contains "STRUCT.H", which declares the typedef CAPTUREFILE_HEADER
+ * for the header of a Microsoft Network Monitor 1.x capture file.
+ *
+ * The help files for Network Monitor 3.x document the 2.x file format.
+ */
+
+/* Capture file header, *including* magic number, is padded to 128 bytes. */
+#define CAPTUREFILE_HEADER_SIZE 128
+
+/* Magic number size, for both 1.x and 2.x. */
+#define MAGIC_SIZE 4
+
+/* Magic number in Network Monitor 1.x files. */
+static const char netmon_1_x_magic[MAGIC_SIZE] = {
+ 'R', 'T', 'S', 'S'
+};
+
+/* Magic number in Network Monitor 2.x files. */
+static const char netmon_2_x_magic[MAGIC_SIZE] = {
+ 'G', 'M', 'B', 'U'
+};
+
+/* Network Monitor file header (minus magic number). */
+struct netmon_hdr {
+ guint8 ver_minor; /* minor version number */
+ guint8 ver_major; /* major version number */
+ guint16 network; /* network type */
+ guint16 ts_year; /* year of capture start */
+ guint16 ts_month; /* month of capture start (January = 1) */
+ guint16 ts_dow; /* day of week of capture start (Sun = 0) */
+ guint16 ts_day; /* day of month of capture start */
+ guint16 ts_hour; /* hour of capture start */
+ guint16 ts_min; /* minute of capture start */
+ guint16 ts_sec; /* second of capture start */
+ guint16 ts_msec; /* millisecond of capture start */
+ guint32 frametableoffset; /* frame index table offset */
+ guint32 frametablelength; /* frame index table size */
+ guint32 userdataoffset; /* user data offset */
+ guint32 userdatalength; /* user data size */
+ guint32 commentdataoffset; /* comment data offset */
+ guint32 commentdatalength; /* comment data size */
+ 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 */
+};
+
+/* Network Monitor 1.x record header; not defined in STRUCT.H, but deduced by
+ * looking at capture files. */
+struct netmonrec_1_x_hdr {
+ guint32 ts_delta; /* time stamp - msecs since start of capture */
+ guint16 orig_len; /* actual length of packet */
+ guint16 incl_len; /* number of octets captured in file */
+};
+
+/*
+ * Network Monitor 2.x record header, as documented in NetMon 3.x's
+ * help files.
+ */
+struct netmonrec_2_x_hdr {
+ guint64 ts_delta; /* time stamp - usecs since start of capture */
+ guint32 orig_len; /* actual length of packet */
+ 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 */
+};
+
+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.
+ */
+struct netmon_atm_hdr {
+ guint8 dest[6]; /* "Destination address" - what is it? */
+ guint8 src[6]; /* "Source address" - what is it? */
+ guint16 vpi; /* VPI */
+ guint16 vci; /* VCI */
+};
+
+typedef struct {
+ time_t start_secs;
+ guint32 start_nsecs;
+ guint8 version_major;
+ guint8 version_minor;
+ guint32 *frame_table;
+ 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://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation
+ *
+ * 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.
+ * 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_IEEE_802_11_NETMON,
+ /* 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, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+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 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,
+ gchar **err_info);
+
+static int netmon_1_x_file_type_subtype = -1;
+static int netmon_2_x_file_type_subtype = -1;
+
+void register_netmon(void);
+
+/*
+ * 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)
+{
+ char magic[MAGIC_SIZE];
+ struct netmon_hdr hdr;
+ int file_type;
+ struct tm tm;
+ guint32 frame_table_offset;
+ guint32 frame_table_length;
+ guint32 frame_table_size;
+ guint32 *frame_table;
+ 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 */
+ 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 WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Read the rest of the header. */
+ if (!wtap_read_bytes(wth->fh, &hdr, sizeof hdr, err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ switch (hdr.ver_major) {
+
+ case 1:
+ file_type = netmon_1_x_file_type_subtype;
+ break;
+
+ case 2:
+ file_type = netmon_2_x_file_type_subtype;
+ break;
+
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("netmon: major version %u unsupported", hdr.ver_major);
+ 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;
+ *err_info = ws_strdup_printf("netmon: network type %u unknown or unsupported",
+ hdr.network);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* This is a netmon file */
+ wth->file_type_subtype = file_type;
+ netmon = g_new0(netmon_t, 1);
+ wth->priv = (void *)netmon;
+ wth->subtype_read = netmon_read;
+ wth->subtype_seek_read = netmon_seek_read;
+ wth->subtype_close = netmon_close;
+
+ /* NetMon capture file formats v2.1+ use per-packet encapsulation types. NetMon 3 sets the value in
+ * the header to 1 (Ethernet) for backwards compatibility. */
+ 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
+ * milliseconds.
+ */
+ tm.tm_year = pletoh16(&hdr.ts_year) - 1900;
+ tm.tm_mon = pletoh16(&hdr.ts_month) - 1;
+ tm.tm_mday = pletoh16(&hdr.ts_day);
+ tm.tm_hour = pletoh16(&hdr.ts_hour);
+ tm.tm_min = pletoh16(&hdr.ts_min);
+ tm.tm_sec = pletoh16(&hdr.ts_sec);
+ tm.tm_isdst = -1;
+ 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
+ * time sometime other than when we do, and thus the time was one
+ * that doesn't exist here because a switch from standard to summer
+ * time zips over it, it could happen.
+ *
+ * On the other hand, if the capture was done in a different time
+ * zone, this won't work right anyway; unfortunately, the time
+ * zone isn't stored in the capture file (why the hell didn't
+ * they stuff a FILETIME, which is the number of 100-nanosecond
+ * intervals since 1601-01-01 00:00:00 "UTC", there, instead
+ * of stuffing a SYSTEMTIME, which is time-zone-dependent, there?).
+ *
+ * Eventually they went with per-packet FILETIMEs in a later
+ * version.
+ */
+ netmon->start_nsecs = pletoh16(&hdr.ts_msec)*1000000;
+
+ netmon->version_major = hdr.ver_major;
+ netmon->version_minor = hdr.ver_minor;
+
+ /*
+ * Get the offset of the frame index table.
+ */
+ frame_table_offset = pletoh32(&hdr.frametableoffset);
+
+ /*
+ * 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.
+ */
+ 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
+ * first packet starting exactly 128 bytes into the file.
+ *
+ * Furthermore, it also appears that there are "holes" in
+ * the file, i.e. frame N+1 doesn't always follow immediately
+ * after frame N.
+ *
+ * Therefore, we must read the frame table, and use the offsets
+ * in it as the offsets of the frames.
+ */
+ frame_table_length = pletoh32(&hdr.frametablelength);
+ frame_table_size = frame_table_length / (guint32)sizeof (guint32);
+ if ((frame_table_size * sizeof (guint32)) != frame_table_length) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: frame table length is %u, which is not a multiple of the size of an entry",
+ frame_table_length);
+ return WTAP_OPEN_ERROR;
+ }
+ if (frame_table_size == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: frame table length is %u, which means it's less than one entry in size",
+ frame_table_length);
+ return WTAP_OPEN_ERROR;
+ }
+ /*
+ * XXX - clamp the size of the frame table, so that we don't
+ * attempt to allocate a huge frame table and fail.
+ *
+ * Given that file offsets in the frame table are 32-bit,
+ * a NetMon file cannot be bigger than 2^32 bytes.
+ * Given that a NetMon 1.x-format packet header is 8 bytes,
+ * that means a NetMon file cannot have more than
+ * 512*2^20 packets. We'll pick that as the limit for
+ * now; it's 1/8th of a 32-bit address space, which is
+ * probably not going to exhaust the address space all by
+ * itself, and probably won't exhaust the backing store.
+ */
+ if (frame_table_size > 512*1024*1024) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: frame table length is %u, which is larger than we support",
+ frame_table_length);
+ return WTAP_OPEN_ERROR;
+ }
+ if (file_seek(wth->fh, frame_table_offset, SEEK_SET, err) == -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 = ws_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 = ws_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 = ws_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 = ws_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 = ws_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 WTAP_OPEN_ERROR;
+ }
+ if (!wtap_read_bytes(wth->fh, frame_table, frame_table_length,
+ err, err_info)) {
+ g_free(frame_table);
+ return WTAP_OPEN_ERROR;
+ }
+ netmon->frame_table_size = frame_table_size;
+ netmon->frame_table = frame_table;
+
+ 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 = ws_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 = ws_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 = ws_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.
+ */
+ for (i = 0; i < frame_table_size; i++)
+ frame_table[i] = pletoh32(&frame_table[i]);
+#endif
+
+ /* Set up to start reading at the first frame. */
+ netmon->current_frame = 0;
+ switch (netmon->version_major) {
+
+ case 1:
+ /*
+ * Version 1.x of the file format supports
+ * millisecond precision.
+ */
+ wth->file_tsprec = WTAP_TSPREC_MSEC;
+ break;
+
+ case 2:
+ /*
+ * Versions 2.0 through 2.2 support microsecond
+ * precision; version 2.3 supports 100-nanosecond
+ * precision (2.3 was the last version).
+ */
+ if (netmon->version_minor >= 3)
+ wth->file_tsprec = WTAP_TSPREC_100_NSEC;
+ else
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ break;
+ }
+ return WTAP_OPEN_MINE;
+}
+
+static void
+netmon_set_pseudo_header_info(wtap_rec *rec, Buffer *buf)
+{
+ 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(rec, ws_buffer_start_ptr(buf));
+ break;
+
+ case WTAP_ENCAP_ETHERNET:
+ /*
+ * We assume there's no FCS in this frame.
+ */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
+ break;
+
+ case WTAP_ENCAP_IEEE_802_11_NETMON:
+ /*
+ * The 802.11 metadata at the beginnning of the frame data
+ * is processed by a dissector, which fills in a pseudo-
+ * header and passes it to the 802.11 radio dissector,
+ * just as is done with other 802.11 radio metadata headers
+ * that are part of the packet data, such as radiotap.
+ */
+ break;
+ }
+}
+
+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;
+ union {
+ struct netmonrec_1_x_hdr hdr_1_x;
+ struct netmonrec_2_x_hdr hdr_2_x;
+ } hdr;
+ gint64 delta = 0; /* signed - frame times can be before the nominal start */
+ gint64 t;
+ time_t secs;
+ 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) {
+
+ case 1:
+ hdr_size = sizeof (struct netmonrec_1_x_hdr);
+ break;
+
+ case 2:
+ hdr_size = sizeof (struct netmonrec_2_x_hdr);
+ break;
+ }
+ if (!wtap_read_bytes_or_eof(fh, &hdr, hdr_size, err, err_info))
+ return FAILURE;
+
+ switch (netmon->version_major) {
+
+ case 1:
+ orig_size = pletoh16(&hdr.hdr_1_x.orig_len);
+ packet_size = pletoh16(&hdr.hdr_1_x.incl_len);
+ break;
+
+ case 2:
+ orig_size = pletoh32(&hdr.hdr_2_x.orig_len);
+ packet_size = pletoh32(&hdr.hdr_2_x.incl_len);
+ break;
+ }
+ 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 = ws_strdup_printf("netmon: File has %u-byte packet, bigger than maximum of %u",
+ packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FAILURE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+
+ /*
+ * If this is an ATM packet, the first
+ * "sizeof (struct netmon_atm_hdr)" bytes have destination and
+ * source addresses (6 bytes - MAC addresses of some sort?)
+ * and the VPI and VCI; read them and generate the pseudo-header
+ * from them.
+ */
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_ATM_PDUS:
+ if (packet_size < sizeof (struct netmon_atm_hdr)) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: ATM file has a %u-byte packet, too small to have even an ATM pseudo-header",
+ packet_size);
+ return FAILURE;
+ }
+ if (!netmon_read_atm_pseudoheader(fh, &rec->rec_header.packet_header.pseudo_header,
+ err, err_info))
+ return FAILURE; /* Read error */
+
+ /*
+ * Don't count the pseudo-header as part of the packet.
+ */
+ orig_size -= (guint)sizeof (struct netmon_atm_hdr);
+ packet_size -= (guint)sizeof (struct netmon_atm_hdr);
+ break;
+
+ default:
+ break;
+ }
+
+ switch (netmon->version_major) {
+
+ case 1:
+ /*
+ * 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 1000000.
+ *
+ * pletoh32() returns a guint32; we cast it to gint64
+ * before multiplying, so that the product doesn't
+ * overflow a guint32.
+ */
+ delta = ((gint64)pletoh32(&hdr.hdr_1_x.ts_delta))*1000000;
+ break;
+
+ case 2:
+ /*
+ * OK, this is weird. Microsoft's documentation
+ * says this is in microseconds and is a 64-bit
+ * unsigned number, but it can be negative; they
+ * say what appears to amount to "treat it as an
+ * unsigned number, multiply it by 10, and then
+ * interpret the resulting 64-bit quantity as a
+ * signed number". That operation can turn a
+ * value with the uppermost bit 0 to a value with
+ * the uppermost bit 1, hence turning a large
+ * positive number-of-microseconds into a small
+ * negative number-of-100-nanosecond-increments.
+ */
+ delta = pletoh64(&hdr.hdr_2_x.ts_delta)*10;
+
+ /*
+ * OK, it's now a signed value in 100-nanosecond
+ * units. Now convert it to nanosecond units.
+ */
+ delta *= 100;
+ break;
+ }
+ secs = 0;
+ t = netmon->start_nsecs + delta;
+ while (t < 0) {
+ /*
+ * Propagate a borrow into the seconds.
+ * The seconds is a time_t, and can be < 0
+ * (unlikely, as Windows didn't exist before
+ * January 1, 1970, 00:00:00 UTC), while the
+ * nanoseconds should be positive, as in
+ * "nanoseconds since the instant of time
+ * represented by the seconds".
+ *
+ * We do not want t to be negative, as, according
+ * to the C90 standard, "if either operand [of /
+ * or %] is negative, whether the result of the
+ * / operator is the largest integer less than or
+ * equal to the algebraic quotient or the smallest
+ * greater than or equal to the algebraic quotient
+ * is implementation-defined, as is the sign of
+ * the result of the % operator", and we want
+ * the result of the division and remainder
+ * operations to be the same on all platforms.
+ */
+ t += 1000000000;
+ secs--;
+ }
+ secs += (time_t)(t/1000000000);
+ 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;
+
+ /*
+ * Read the packet data.
+ */
+ if (!wtap_read_packet_bytes(fh, buf, rec->rec_header.packet_header.caplen, err, err_info))
+ return FAILURE;
+
+ /*
+ * For version 2.1 and later, there's additional information
+ * after the frame data.
+ */
+ if (netmon->version_major == 2 && netmon->version_minor >= 1) {
+ switch (netmon->version_minor) {
+
+ case 1:
+ trlr_size = (int)sizeof (struct netmonrec_2_1_trlr);
+ break;
+
+ 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 = ws_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 = ws_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 = ws_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.
+ *
+ * https://docs.microsoft.com/en-us/windows/win32/api/evntcons/ns-evntcons-event_header
+ */
+ 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 = ws_strdup_printf("netmon: network type %u unknown or unsupported",
+ network);
+ return FAILURE;
+ }
+ }
+
+ rec->rec_header.packet_header.pkt_encap = pkt_encap;
+ if (netmon->version_minor >= 3) {
+ /*
+ * This is a 2.3 or later file. That format
+ * contains a UTC per-packet time stamp; use
+ * that instead of the start time and offset.
+ */
+ 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
+ */
+ 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 gboolean netmon_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ netmon_t *netmon = (netmon_t *)wth->priv;
+ gint64 rec_offset;
+
+ 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 FALSE;
+ }
+ netmon->current_frame++;
+
+ *data_offset = file_tell(wth->fh);
+
+ switch (netmon_process_record(wth, wth->fh, rec, buf, err,
+ err_info)) {
+
+ case RETRY:
+ continue;
+
+ case SUCCESS:
+ return TRUE;
+
+ case FAILURE:
+ return FALSE;
+ }
+ }
+}
+
+static gboolean
+netmon_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ switch (netmon_process_record(wth, wth->random_fh, rec, buf, err,
+ err_info)) {
+
+ default:
+ /*
+ * This should not happen.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("netmon: saw metadata in netmon_seek_read");
+ return FALSE;
+
+ case SUCCESS:
+ return TRUE;
+
+ case FAILURE:
+ return FALSE;
+ }
+}
+
+static gboolean
+netmon_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
+ int *err, gchar **err_info)
+{
+ struct netmon_atm_hdr atm_phdr;
+ guint16 vpi, vci;
+
+ 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);
+
+ pseudo_header->atm.vpi = vpi;
+ pseudo_header->atm.vci = vci;
+
+ /* We don't have this information */
+ pseudo_header->atm.flags = 0;
+ pseudo_header->atm.channel = 0;
+ pseudo_header->atm.cells = 0;
+ pseudo_header->atm.aal5t_u2u = 0;
+ pseudo_header->atm.aal5t_len = 0;
+ pseudo_header->atm.aal5t_chksum = 0;
+
+ return TRUE;
+}
+
+/* Throw away the frame table used by the sequential I/O stream. */
+static void
+netmon_close(wtap *wth)
+{
+ netmon_t *netmon = (netmon_t *)wth->priv;
+
+ if (netmon->frame_table != NULL) {
+ 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 {
+ gboolean is_v2;
+ gboolean got_first_record_time;
+ nstime_t first_record_time;
+ guint32 frame_table_offset;
+ guint32 *frame_table;
+ guint frame_table_index;
+ guint frame_table_size;
+ gboolean no_more_room; /* TRUE if no more records can be written */
+} netmon_dump_t;
+
+static const int wtap_encap[] = {
+ -1, /* WTAP_ENCAP_UNKNOWN -> unsupported */
+ 1, /* WTAP_ENCAP_ETHERNET -> NDIS Ethernet */
+ 2, /* WTAP_ENCAP_TOKEN_RING -> NDIS Token Ring */
+ -1, /* WTAP_ENCAP_SLIP -> unsupported */
+ -1, /* WTAP_ENCAP_PPP -> unsupported */
+ 3, /* WTAP_ENCAP_FDDI -> NDIS FDDI */
+ 3, /* WTAP_ENCAP_FDDI_BITSWAPPED -> NDIS FDDI */
+ -1, /* WTAP_ENCAP_RAW_IP -> unsupported */
+ -1, /* WTAP_ENCAP_ARCNET -> unsupported */
+ -1, /* WTAP_ENCAP_ARCNET_LINUX -> unsupported */
+ -1, /* WTAP_ENCAP_ATM_RFC1483 -> unsupported */
+ -1, /* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */
+ -1, /* WTAP_ENCAP_LAPB -> unsupported*/
+ 4, /* WTAP_ENCAP_ATM_PDUS -> NDIS WAN (*NOT* ATM!) */
+};
+#define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
+
+/* Returns 0 if we could write the specified encapsulation type,
+ an error indication otherwise. */
+static int netmon_dump_can_write_encap_1_x(int encap)
+{
+ /*
+ * Per-packet encapsulations are *not* supported in NetMon 1.x
+ * format.
+ */
+ if (encap < 0 || (unsigned) encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+static int netmon_dump_can_write_encap_2_x(int encap)
+{
+ /*
+ * Per-packet encapsulations are supported in NetMon 2.1
+ * format.
+ */
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return 0;
+
+ if (encap < 0 || (unsigned) encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+/* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean netmon_dump_open(wtap_dumper *wdh, gboolean is_v2,
+ int *err, gchar **err_info _U_)
+{
+ 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
+ the header when we've written out all the packets, we just
+ skip over the header for now. */
+ if (wtap_dump_file_seek(wdh, CAPTUREFILE_HEADER_SIZE, SEEK_SET, err) == -1)
+ return FALSE;
+
+ wdh->bytes_dumped = CAPTUREFILE_HEADER_SIZE;
+ wdh->subtype_write = netmon_dump;
+ wdh->subtype_finish = netmon_dump_finish;
+
+ netmon = g_new(netmon_dump_t, 1);
+ wdh->priv = (void *)netmon;
+ netmon->is_v2 = is_v2;
+ 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;
+ netmon->no_more_room = FALSE;
+
+ return TRUE;
+}
+
+static gboolean netmon_dump_open_1_x(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ return netmon_dump_open(wdh, FALSE, err, err_info);
+}
+
+static gboolean netmon_dump_open_2_x(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ return netmon_dump_open(wdh, TRUE, err, err_info);
+}
+
+/* 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 wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ 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;
+ void *hdrp;
+ size_t rec_size;
+ struct netmonrec_2_1_trlr rec_2_x_trlr;
+ size_t hdr_size;
+ struct netmon_atm_hdr atm_hdr;
+ int atm_hdrsize;
+ 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;
+ }
+
+ if (netmon->is_v2) {
+ /* Don't write anything we're not willing to read. */
+ if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+ } else {
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ /*
+ * The length fields are 16-bit, so there's a hard limit
+ * of 65535.
+ */
+ if (rec->rec_header.packet_header.caplen > 65535) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+ }
+
+ if (wdh->file_encap == WTAP_ENCAP_PER_PACKET) {
+ /*
+ * Is this network type supported?
+ */
+ 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_UNWRITABLE_ENCAP;
+ return FALSE;
+ }
+
+ /*
+ * Fill in the trailer with the network type.
+ */
+ phtoles(rec_2_x_trlr.network, wtap_encap[rec->rec_header.packet_header.pkt_encap]);
+ }
+
+ /*
+ * Will the file offset of this frame fit in a 32-bit unsigned
+ * integer?
+ */
+ if (netmon->no_more_room) {
+ /*
+ * No, so the file is too big for NetMon format to
+ * handle.
+ */
+ *err = EFBIG;
+ return FALSE;
+ }
+
+ /*
+ * NetMon files have a capture start time in the file header,
+ * and have times relative to that in the packet headers;
+ * pick the time of the first packet as the capture start
+ * time.
+ *
+ * That time has millisecond resolution, so chop any
+ * sub-millisecond part of the time stamp off.
+ */
+ if (!netmon->got_first_record_time) {
+ netmon->first_record_time.secs = rec->ts.secs;
+ netmon->first_record_time.nsecs =
+ (rec->ts.nsecs/1000000)*1000000;
+ netmon->got_first_record_time = TRUE;
+ }
+
+ if (wdh->file_encap == WTAP_ENCAP_ATM_PDUS)
+ atm_hdrsize = sizeof (struct netmon_atm_hdr);
+ else
+ atm_hdrsize = 0;
+ 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.
+ * The seconds is a time_t, and can be < 0
+ * (unlikely, as neither UN*X nor DOS
+ * nor the original Mac System existed
+ * before January 1, 1970, 00:00:00 UTC),
+ * while the nanoseconds should be positive,
+ * as in "nanoseconds since the instant of time
+ * represented by the seconds".
+ *
+ * We do not want t to be negative, as, according
+ * to the C90 standard, "if either operand [of /
+ * or %] is negative, whether the result of the
+ * / operator is the largest integer less than or
+ * equal to the algebraic quotient or the smallest
+ * greater than or equal to the algebraic quotient
+ * is implementation-defined, as is the sign of
+ * the result of the % operator", and we want
+ * the result of the division and remainder
+ * operations to be the same on all platforms.
+ */
+ nsecs += 1000000000;
+ secs--;
+ }
+ if (netmon->is_v2) {
+ rec_2_x_hdr.ts_delta = GUINT64_TO_LE(secs*1000000 + (nsecs + 500)/1000);
+ 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;
+ } else {
+ rec_1_x_hdr.ts_delta = GUINT32_TO_LE(secs*1000 + (nsecs + 500000)/1000000);
+ 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;
+ }
+
+ /*
+ * Keep track of the record size, as we need to update
+ * the current file offset.
+ */
+ rec_size = 0;
+
+ if (!wtap_dump_file_write(wdh, hdrp, hdr_size, err))
+ return FALSE;
+ rec_size += hdr_size;
+
+ if (wdh->file_encap == WTAP_ENCAP_ATM_PDUS) {
+ /*
+ * Write the ATM header.
+ * We supply all-zero destination and source addresses.
+ */
+ memset(&atm_hdr.dest, 0, sizeof atm_hdr.dest);
+ memset(&atm_hdr.src, 0, sizeof atm_hdr.src);
+ atm_hdr.vpi = g_htons(pseudo_header->atm.vpi);
+ atm_hdr.vci = g_htons(pseudo_header->atm.vci);
+ if (!wtap_dump_file_write(wdh, &atm_hdr, sizeof atm_hdr, err))
+ return FALSE;
+ rec_size += sizeof atm_hdr;
+ }
+
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+ rec_size += rec->rec_header.packet_header.caplen;
+
+ if (wdh->file_encap == WTAP_ENCAP_PER_PACKET) {
+ /*
+ * Write out the trailer.
+ */
+ if (!wtap_dump_file_write(wdh, &rec_2_x_trlr,
+ sizeof rec_2_x_trlr, err))
+ return FALSE;
+ rec_size += sizeof rec_2_x_trlr;
+ }
+
+ /*
+ * Stash the file offset of this frame.
+ */
+ if (netmon->frame_table_size == 0) {
+ /*
+ * Haven't yet allocated the buffer for the frame table.
+ */
+ netmon->frame_table = (guint32 *)g_malloc(1024 * sizeof *netmon->frame_table);
+ netmon->frame_table_size = 1024;
+ } else {
+ /*
+ * We've allocated it; are we at the end?
+ */
+ if (netmon->frame_table_index >= netmon->frame_table_size) {
+ /*
+ * Yes - double the size of the frame table.
+ */
+ netmon->frame_table_size *= 2;
+ netmon->frame_table = (guint32 *)g_realloc(netmon->frame_table,
+ netmon->frame_table_size * sizeof *netmon->frame_table);
+ }
+ }
+
+ netmon->frame_table[netmon->frame_table_index] =
+ GUINT32_TO_LE(netmon->frame_table_offset);
+
+ /*
+ * Is this the last record we can write?
+ * I.e., will the frame table offset of the next record not fit
+ * in a 32-bit frame table offset entry?
+ *
+ * (We don't bother checking whether the number of frames
+ * will fit in a 32-bit value, as, even if each record were
+ * 1 byte, if there were more than 2^32-1 packets, the frame
+ * table offset of at least one of those packets will be >
+ * 2^32 - 1.)
+ *
+ * Note: this also catches the unlikely possibility that
+ * the record itself is > 2^32 - 1 bytes long.
+ */
+ if ((guint64)netmon->frame_table_offset + rec_size > G_MAXUINT32) {
+ /*
+ * Yup, too big.
+ */
+ netmon->no_more_room = TRUE;
+ }
+ netmon->frame_table_index++;
+ netmon->frame_table_offset += (guint32) rec_size;
+
+ return TRUE;
+}
+
+/* Finish writing to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean netmon_dump_finish(wtap_dumper *wdh, int *err,
+ gchar **err_info _U_)
+{
+ netmon_dump_t *netmon = (netmon_dump_t *)wdh->priv;
+ size_t n_to_write;
+ struct netmon_hdr file_hdr;
+ const char *magicp;
+ size_t magic_size;
+ struct tm *tm;
+ gint64 saved_bytes_dumped;
+
+ /* 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;
+ if (!wtap_dump_file_write(wdh, netmon->frame_table, n_to_write, err))
+ return FALSE;
+
+ /* Now go fix up the file header. */
+ if (wtap_dump_file_seek(wdh, 0, SEEK_SET, err) == -1)
+ return FALSE;
+ /* Save bytes_dumped since following calls to wtap_dump_file_write()
+ * will still (mistakenly) increase it.
+ */
+ saved_bytes_dumped = wdh->bytes_dumped;
+ memset(&file_hdr, '\0', sizeof file_hdr);
+ if (netmon->is_v2) {
+ magicp = netmon_2_x_magic;
+ magic_size = sizeof netmon_2_x_magic;
+ /*
+ * NetMon file version, for 2.x, is 2.0;
+ * for 3.0, it's 2.1.
+ *
+ * If the file encapsulation is WTAP_ENCAP_PER_PACKET,
+ * we need version 2.1.
+ *
+ * XXX - version 2.3 supports UTC time stamps; when
+ * should we use it? According to the file format
+ * documentation, NetMon 3.3 "cannot properly
+ * interpret" the UTC timestamp information; does
+ * that mean it ignores it and uses the local-time
+ * start time and time deltas, or mishandles them?
+ * Also, NetMon 3.1 and earlier can't read version
+ * 2.2, much less version 2.3.
+ */
+ file_hdr.ver_major = 2;
+ file_hdr.ver_minor =
+ (wdh->file_encap == WTAP_ENCAP_PER_PACKET) ? 1 : 0;
+ } else {
+ magicp = netmon_1_x_magic;
+ magic_size = sizeof netmon_1_x_magic;
+ /* NetMon file version, for 1.x, is 1.1 */
+ file_hdr.ver_major = 1;
+ file_hdr.ver_minor = 1;
+ }
+ if (!wtap_dump_file_write(wdh, magicp, magic_size, err))
+ return FALSE;
+
+ if (wdh->file_encap == WTAP_ENCAP_PER_PACKET) {
+ /*
+ * We're writing NetMon 2.1 format, so the media
+ * type in the file header is irrelevant. Set it
+ * to 1, just as Network Monitor does.
+ */
+ file_hdr.network = GUINT16_TO_LE(1);
+ } else
+ file_hdr.network = GUINT16_TO_LE(wtap_encap[wdh->file_encap]);
+ tm = localtime(&netmon->first_record_time.secs);
+ if (tm != NULL) {
+ file_hdr.ts_year = GUINT16_TO_LE(1900 + tm->tm_year);
+ file_hdr.ts_month = GUINT16_TO_LE(tm->tm_mon + 1);
+ file_hdr.ts_dow = GUINT16_TO_LE(tm->tm_wday);
+ file_hdr.ts_day = GUINT16_TO_LE(tm->tm_mday);
+ file_hdr.ts_hour = GUINT16_TO_LE(tm->tm_hour);
+ file_hdr.ts_min = GUINT16_TO_LE(tm->tm_min);
+ file_hdr.ts_sec = GUINT16_TO_LE(tm->tm_sec);
+ } else {
+ file_hdr.ts_year = GUINT16_TO_LE(1900 + 0);
+ file_hdr.ts_month = GUINT16_TO_LE(0 + 1);
+ file_hdr.ts_dow = GUINT16_TO_LE(0);
+ file_hdr.ts_day = GUINT16_TO_LE(0);
+ file_hdr.ts_hour = GUINT16_TO_LE(0);
+ file_hdr.ts_min = GUINT16_TO_LE(0);
+ file_hdr.ts_sec = GUINT16_TO_LE(0);
+ }
+ file_hdr.ts_msec = GUINT16_TO_LE(netmon->first_record_time.nsecs/1000000);
+ file_hdr.frametableoffset = GUINT32_TO_LE(netmon->frame_table_offset);
+ file_hdr.frametablelength =
+ GUINT32_TO_LE(netmon->frame_table_index * sizeof *netmon->frame_table);
+ if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
+ return FALSE;
+
+ wdh->bytes_dumped = saved_bytes_dumped;
+ return TRUE;
+}
+
+static const struct supported_block_type netmon_1_x_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info netmon_1_x_info = {
+ "Microsoft NetMon 1.x", "netmon1", "cap", NULL,
+ TRUE, BLOCKS_SUPPORTED(netmon_1_x_blocks_supported),
+ netmon_dump_can_write_encap_1_x, netmon_dump_open_1_x, NULL
+};
+
+static const struct supported_block_type netmon_2_x_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info netmon_2_x_info = {
+ "Microsoft NetMon 2.x", "netmon2", "cap", NULL,
+ TRUE, BLOCKS_SUPPORTED(netmon_2_x_blocks_supported),
+ netmon_dump_can_write_encap_2_x, netmon_dump_open_2_x, NULL
+};
+
+void register_netmon(void)
+{
+ netmon_1_x_file_type_subtype = wtap_register_file_type_subtype(&netmon_1_x_info);
+ netmon_2_x_file_type_subtype = wtap_register_file_type_subtype(&netmon_2_x_info);
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("NETMON_1_x",
+ netmon_1_x_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("NETMON_2_x",
+ netmon_2_x_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://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:
+ */