Whitespace fixes:
[obnox/wireshark/wip.git] / wiretap / netmon.c
index e56dc822ad0f20729d98eab2ebe4e7c2352bb95a..cb3ed981f656943bb3741fdfc117155e05c4a571 100644 (file)
@@ -29,6 +29,7 @@
 #include "file_wrappers.h"
 #include "buffer.h"
 #include "atm.h"
+#include "pcap-encap.h"
 #include "netmon.h"
 
 /* The file at
@@ -94,6 +95,30 @@ struct netmonrec_2_x_hdr {
        guint32 incl_len;       /* number of octets captured in file */
 };
 
+/*
+ * Network Monitor 2.1 and later record trailers; documented in the Network
+ * Monitor 3.x help files, for 3.3 and later, although they don't clearly
+ * state how the trailer format changes from version to version.
+ *
+ * Some fields are multi-byte integers, but they're not aligned on their
+ * natural boundaries.
+ */
+struct netmonrec_2_1_trlr {
+       guint8 network[2];              /* network type for this packet */
+};
+
+struct netmonrec_2_2_trlr {
+       guint8 network[2];              /* network type for this packet */
+       guint8 process_info_index[4];   /* index into the process info table */
+};
+
+struct netmonrec_2_3_trlr {
+       guint8 network[2];              /* network type for this packet */
+       guint8 process_info_index[4];   /* index into the process info table */
+       guint8 utc_timestamp[8];        /* packet time stamp, as .1 us units since January 1, 1601, 00:00:00 UTC */
+       guint8 timezone_index;          /* index of time zone information */
+};
+
 /*
  * The link-layer header on ATM packets.
  */
@@ -108,20 +133,57 @@ typedef struct {
        time_t  start_secs;
        guint32 start_usecs;
        guint8  version_major;
+       guint8  version_minor;
        guint32 *frame_table;
        guint32 frame_table_size;
        guint   current_frame;
 } netmon_t;
 
+/*
+ * XXX - at least in some NetMon 3.4 VPN captures, the per-packet
+ * link-layer type is 0, but the packets have Ethernet headers.
+ * We handle this by mapping 0 to WTAP_ENCAP_ETHERNET; should we,
+ * instead, use the per-file link-layer type?
+ */
+static const int netmon_encap[] = {
+       WTAP_ENCAP_ETHERNET,
+       WTAP_ENCAP_ETHERNET,
+       WTAP_ENCAP_TOKEN_RING,
+       WTAP_ENCAP_FDDI_BITSWAPPED,
+       WTAP_ENCAP_ATM_PDUS,    /* NDIS WAN - this is what's used for ATM */
+       WTAP_ENCAP_UNKNOWN,     /* NDIS LocalTalk, but format 2.x uses it for IP-over-IEEE 1394 */
+       WTAP_ENCAP_IEEE802_11_NETMON_RADIO,
+                               /* NDIS "DIX", but format 2.x uses it for 802.11 */
+       WTAP_ENCAP_RAW_IP,      /* NDIS ARCNET raw, but format 2.x uses it for "Tunneling interfaces" */
+       WTAP_ENCAP_RAW_IP,      /* NDIS ARCNET 878.2, but format 2.x uses it for "Wireless WAN" */
+       WTAP_ENCAP_RAW_IP,      /* NDIS ATM (no, this is NOT used for ATM); format 2.x uses it for "Raw IP Frames" */
+       WTAP_ENCAP_UNKNOWN,     /* NDIS Wireless WAN */
+       WTAP_ENCAP_UNKNOWN      /* NDIS IrDA */
+};
+#define NUM_NETMON_ENCAPS (sizeof netmon_encap / sizeof netmon_encap[0])
+
+/*
+ * Special link-layer types.
+ */
+#define NETMON_NET_PCAP_BASE           0xE000
+#define NETMON_NET_NETEVENT            0xFFE0
+#define NETMON_NET_NETWORK_INFO_EX     0xFFFB
+#define NETMON_NET_PAYLOAD_HEADER      0xFFFC
+#define NETMON_NET_NETWORK_INFO                0xFFFD
+#define NETMON_NET_DNS_CACHE           0xFFFE
+#define NETMON_NET_NETMON_FILTER       0xFFFF
+
 static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
     gint64 *data_offset);
 static gboolean netmon_seek_read(wtap *wth, gint64 seek_off,
     union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
     int *err, gchar **err_info);
 static gboolean netmon_read_atm_pseudoheader(FILE_T fh,
-    union wtap_pseudo_header *pseudo_header, int *err);
+    union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info);
 static gboolean netmon_read_rec_data(FILE_T fh, guchar *pd, int length,
-    int *err);
+    int *err, gchar **err_info);
+static int netmon_read_rec_trailer(FILE_T fh, int trlr_size, int *err,
+    gchar **err_info);
 static void netmon_sequential_close(wtap *wth);
 static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
     const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err);
@@ -133,21 +195,6 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
        char magic[sizeof netmon_1_x_magic];
        struct netmon_hdr hdr;
        int file_type;
-       static const int netmon_encap[] = {
-               WTAP_ENCAP_UNKNOWN,
-               WTAP_ENCAP_ETHERNET,
-               WTAP_ENCAP_TOKEN_RING,
-               WTAP_ENCAP_FDDI_BITSWAPPED,
-               WTAP_ENCAP_ATM_PDUS,    /* NDIS WAN - this is what's used for ATM */
-               WTAP_ENCAP_UNKNOWN,     /* NDIS LocalTalk */
-               WTAP_ENCAP_UNKNOWN,     /* NDIS "DIX" - should not occur */
-               WTAP_ENCAP_UNKNOWN,     /* NDIS ARCNET raw */
-               WTAP_ENCAP_UNKNOWN,     /* NDIS ARCNET 878.2 */
-               WTAP_ENCAP_UNKNOWN,     /* NDIS ATM (no, this is NOT used for ATM) */
-               WTAP_ENCAP_UNKNOWN,     /* NDIS Wireless WAN */
-               WTAP_ENCAP_UNKNOWN      /* NDIS IrDA */
-       };
-       #define NUM_NETMON_ENCAPS (sizeof netmon_encap / sizeof netmon_encap[0])
        struct tm tm;
        int frame_table_offset;
        guint32 frame_table_length;
@@ -161,9 +208,9 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
        /* Read in the string that should be at the start of a Network
         * Monitor file */
        errno = WTAP_ERR_CANT_READ;
-       bytes_read = file_read(magic, 1, sizeof magic, wth->fh);
+       bytes_read = file_read(magic, sizeof magic, wth->fh);
        if (bytes_read != sizeof magic) {
-               *err = file_error(wth->fh);
+               *err = file_error(wth->fh, err_info);
                if (*err != 0)
                        return -1;
                return 0;
@@ -176,9 +223,9 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
 
        /* Read the rest of the header. */
        errno = WTAP_ERR_CANT_READ;
-       bytes_read = file_read(&hdr, 1, sizeof hdr, wth->fh);
+       bytes_read = file_read(&hdr, sizeof hdr, wth->fh);
        if (bytes_read != sizeof hdr) {
-               *err = file_error(wth->fh);
+               *err = file_error(wth->fh, err_info);
                if (*err != 0)
                        return -1;
                return 0;
@@ -219,10 +266,8 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
 
        /* 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. */
-       /* XXX - It would be better if we could set this to WTAP_ENCAP_PER_PACKET and show a message for
-        * that, but the wtap_read() routine asserts on that value to catch errors. */
        if((hdr.ver_major == 2 && hdr.ver_minor >= 1) || hdr.ver_major > 2)
-               wth->file_encap = WTAP_ENCAP_UNKNOWN;
+               wth->file_encap = WTAP_ENCAP_PER_PACKET;
        else
                wth->file_encap = netmon_encap[hdr.network];
 
@@ -256,6 +301,7 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
        netmon->start_usecs = pletohs(&hdr.ts_msec)*1000;
 
        netmon->version_major = hdr.ver_major;
+       netmon->version_minor = hdr.ver_minor;
 
        /*
         * Get the offset of the frame index table.
@@ -295,9 +341,9 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
        }
        frame_table = g_malloc(frame_table_length);
        errno = WTAP_ERR_CANT_READ;
-       bytes_read = file_read(frame_table, 1, frame_table_length, wth->fh);
+       bytes_read = file_read(frame_table, frame_table_length, wth->fh);
        if ((guint32)bytes_read != frame_table_length) {
-               *err = file_error(wth->fh);
+               *err = file_error(wth->fh, err_info);
                if (*err == 0)
                        *err = WTAP_ERR_SHORT_READ;
                g_free(frame_table);
@@ -322,6 +368,66 @@ int netmon_open(wtap *wth, int *err, gchar **err_info)
        return 1;
 }
 
+static size_t
+netmon_trailer_size(netmon_t *netmon)
+{
+       if ((netmon->version_major == 2 && netmon->version_minor >= 1) ||
+           netmon->version_major > 2) {
+               if (netmon->version_major > 2) {
+                       /*
+                        * Asssume 2.3 format, for now.
+                        */
+                       return sizeof (struct netmonrec_2_3_trlr);
+               } else {
+                       switch (netmon->version_minor) {
+
+                       case 1:
+                               return sizeof (struct netmonrec_2_1_trlr);
+
+                       case 2:
+                               return sizeof (struct netmonrec_2_2_trlr);
+
+                       default:
+                               return sizeof (struct netmonrec_2_3_trlr);
+                       }
+               }
+       }
+       return 0;       /* no trailer */
+}
+
+static void
+netmon_set_pseudo_header_info(int pkt_encap,
+    union wtap_pseudo_header *pseudo_header, guchar *pd, int length)
+{
+       switch (pkt_encap) {
+
+       case WTAP_ENCAP_ATM_PDUS:
+               /*
+                * Attempt to guess from the packet data, the VPI, and
+                * the VCIinformation about the type of traffic.
+                */
+               atm_guess_traffic_type(pd, length, pseudo_header);
+               break;
+
+       case WTAP_ENCAP_ETHERNET:
+               /*
+                * We assume there's no FCS in this frame.
+                */
+               pseudo_header->eth.fcs_len = 0;
+               break;
+
+       case WTAP_ENCAP_IEEE802_11_NETMON_RADIO:
+               /*
+                * It appears to be the case that management
+                * frames have an FCS and data frames don't;
+                * I'm not sure about control frames.  An
+                * "FCS length" of -2 means "NetMon weirdness".
+                */
+               pseudo_header->ieee_802_11.fcs_len = -2;
+               break;
+       }
+}
+
 /* Read the next packet */
 static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
     gint64 *data_offset)
@@ -335,12 +441,15 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
                struct netmonrec_2_x_hdr hdr_2_x;
        }       hdr;
        int     hdr_size = 0;
+       int     trlr_size;
        int     rec_offset;
        guint8  *data_ptr;
+       gint64  delta = 0;      /* signed - frame times can be before the nominal start */
        time_t  secs;
        guint32 usecs;
        double  t;
 
+again:
        /* Have we reached the end of the packet data? */
        if (netmon->current_frame >= netmon->frame_table_size) {
                /* Yes.  We won't need the frame table any more;
@@ -354,7 +463,13 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
        /* Seek to the beginning of the current record, if we're
           not there already (seeking to the current position
           may still cause a seek and a read of the underlying file,
-          so we don't want to do it unconditionally). */
+          so we don't want to do it unconditionally).
+
+          Yes, the current record could be before the previous
+          record.  At least some captures put the trailer record
+          with statistics as the first physical record in the
+          file, but set the frame table up so it's the last
+          record in sequence. */
        rec_offset = netmon->frame_table[netmon->current_frame];
        if (wth->data_offset != rec_offset) {
                wth->data_offset = rec_offset;
@@ -376,9 +491,9 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
        }
        errno = WTAP_ERR_CANT_READ;
 
-       bytes_read = file_read(&hdr, 1, hdr_size, wth->fh);
+       bytes_read = file_read(&hdr, hdr_size, wth->fh);
        if (bytes_read != hdr_size) {
-               *err = file_error(wth->fh);
+               *err = file_error(wth->fh, err_info);
                if (*err == 0 && bytes_read != 0) {
                        *err = WTAP_ERR_SHORT_READ;
                }
@@ -432,7 +547,7 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
                        return FALSE;
                }
                if (!netmon_read_atm_pseudoheader(wth->fh, &wth->pseudo_header,
-                   err))
+                   err, err_info))
                        return FALSE;   /* Read error */
 
                /*
@@ -443,17 +558,14 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
                wth->data_offset += sizeof (struct netmon_atm_hdr);
                break;
 
-       case WTAP_ENCAP_ETHERNET:
-               /*
-                * We assume there's no FCS in this frame.
-                */
-               wth->pseudo_header.eth.fcs_len = 0;
+       default:
                break;
        }
 
        buffer_assure_space(wth->frame_buffer, packet_size);
        data_ptr = buffer_start_ptr(wth->frame_buffer);
-       if (!netmon_read_rec_data(wth->fh, data_ptr, packet_size, err))
+       if (!netmon_read_rec_data(wth->fh, data_ptr, packet_size, err,
+           err_info))
                return FALSE;   /* Read error */
        wth->data_offset += packet_size;
 
@@ -461,14 +573,25 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
        switch (netmon->version_major) {
 
        case 1:
-               t += ((double)pletohl(&hdr.hdr_1_x.ts_delta))*1000;
+               /*
+                * According to Paul Long, this offset is unsigned.
+                * It's 32 bits, so the maximum value will fit in
+                * a gint64 such as delta, even after multiplying
+                * it by 1000.
+                *
+                * pletohl() returns a guint32; we cast it to gint64
+                * before multiplying, so that the product doesn't
+                * overflow a guint32.
+                */
+               delta = ((gint64)pletohl(&hdr.hdr_1_x.ts_delta))*1000;
                break;
 
        case 2:
-               t += (double)pletohl(&hdr.hdr_2_x.ts_delta_lo)
-                   + (double)pletohl(&hdr.hdr_2_x.ts_delta_hi)*4294967296.0;
+               delta = pletohl(&hdr.hdr_2_x.ts_delta_lo)
+                   | (((guint64)pletohl(&hdr.hdr_2_x.ts_delta_hi)) << 32);
                break;
        }
+       t += (double)delta;
        secs = (time_t)(t/1000000);
        usecs = (guint32)(t - (double)secs*1000000);
        wth->phdr.ts.secs = netmon->start_secs + secs;
@@ -477,12 +600,26 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
        wth->phdr.len = orig_size;
 
        /*
-        * Attempt to guess from the packet data, the VPI, and the VCI
-        * information about the type of traffic.
+        * For version 2.1 and later, there's additional information
+        * after the frame data.
         */
-       if (wth->file_encap == WTAP_ENCAP_ATM_PDUS) {
-               atm_guess_traffic_type(data_ptr, packet_size,
-                   &wth->pseudo_header);
+       trlr_size = (int)netmon_trailer_size(netmon);
+       if (trlr_size != 0) {
+               /*
+                * I haz a trailer.
+                */
+               wth->phdr.pkt_encap = netmon_read_rec_trailer(wth->fh,
+                   trlr_size, err, err_info);
+               if (wth->phdr.pkt_encap == -1)
+                       return FALSE;   /* error */
+               wth->data_offset += trlr_size;
+               if (wth->phdr.pkt_encap == 0)
+                       goto again;
+               netmon_set_pseudo_header_info(wth->phdr.pkt_encap,
+                   &wth->pseudo_header, data_ptr, packet_size);
+       } else {
+               netmon_set_pseudo_header_info(wth->file_encap,
+                   &wth->pseudo_header, data_ptr, packet_size);
        }
 
        return TRUE;
@@ -491,8 +628,12 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info,
 static gboolean
 netmon_seek_read(wtap *wth, gint64 seek_off,
     union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
-    int *err, gchar **err_info _U_)
+    int *err, gchar **err_info)
 {
+       netmon_t *netmon = (netmon_t *)wth->priv;
+       int     trlr_size;
+       int     pkt_encap;
+
        if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
                return FALSE;
 
@@ -500,48 +641,62 @@ netmon_seek_read(wtap *wth, gint64 seek_off,
 
        case WTAP_ENCAP_ATM_PDUS:
                if (!netmon_read_atm_pseudoheader(wth->random_fh, pseudo_header,
-                   err)) {
+                   err, err_info)) {
                        /* Read error */
                        return FALSE;
                }
                break;
-
-       case WTAP_ENCAP_ETHERNET:
-               /*
-                * We assume there's no FCS in this frame.
-                */
-               pseudo_header->eth.fcs_len = 0;
-               break;
        }
 
        /*
         * Read the packet data.
         */
-       if (!netmon_read_rec_data(wth->random_fh, pd, length, err))
+       if (!netmon_read_rec_data(wth->random_fh, pd, length, err, err_info))
                return FALSE;
 
        /*
-        * Attempt to guess from the packet data, the VPI, and the VCI
-        * information about the type of traffic.
+        * For version 2.1 and later, there's additional information
+        * after the frame data.
         */
-       if (wth->file_encap == WTAP_ENCAP_ATM_PDUS)
-               atm_guess_traffic_type(pd, length, pseudo_header);
+       trlr_size = (int)netmon_trailer_size(netmon);
+       if (trlr_size != 0) {
+               /*
+                * I haz a trailer.
+                */
+               pkt_encap = netmon_read_rec_trailer(wth->random_fh,
+                   trlr_size, err, err_info);
+               if (pkt_encap == -1)
+                       return FALSE;   /* error */
+               if (pkt_encap == 0) {
+                       /*
+                        * This should not happen.
+                        */
+                       *err = WTAP_ERR_BAD_RECORD;
+                       *err_info = g_strdup("netmon: saw metadata in netmon_seek_read");
+                       return FALSE;
+               }
+               netmon_set_pseudo_header_info(pkt_encap, pseudo_header,
+                   pd, length);
+       } else {
+               netmon_set_pseudo_header_info(wth->file_encap, pseudo_header,
+                   pd, length);
+       }
 
        return TRUE;
 }
 
 static gboolean
 netmon_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
-    int *err)
+    int *err, gchar **err_info)
 {
        struct netmon_atm_hdr atm_phdr;
        int     bytes_read;
        guint16 vpi, vci;
 
        errno = WTAP_ERR_CANT_READ;
-       bytes_read = file_read(&atm_phdr, 1, sizeof (struct netmon_atm_hdr), fh);
+       bytes_read = file_read(&atm_phdr, sizeof (struct netmon_atm_hdr), fh);
        if (bytes_read != sizeof (struct netmon_atm_hdr)) {
-               *err = file_error(fh);
+               *err = file_error(fh, err_info);
                if (*err == 0)
                        *err = WTAP_ERR_SHORT_READ;
                return FALSE;
@@ -565,15 +720,16 @@ netmon_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
 }
 
 static gboolean
-netmon_read_rec_data(FILE_T fh, guchar *pd, int length, int *err)
+netmon_read_rec_data(FILE_T fh, guchar *pd, int length, int *err,
+    gchar **err_info)
 {
        int     bytes_read;
 
        errno = WTAP_ERR_CANT_READ;
-       bytes_read = file_read(pd, 1, length, fh);
+       bytes_read = file_read(pd, length, fh);
 
        if (bytes_read != length) {
-               *err = file_error(fh);
+               *err = file_error(fh, err_info);
                if (*err == 0)
                        *err = WTAP_ERR_SHORT_READ;
                return FALSE;
@@ -581,6 +737,91 @@ netmon_read_rec_data(FILE_T fh, guchar *pd, int length, int *err)
        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 = pletohs(trlr.trlr_2_1.network);
+       if ((network & 0xF000) == NETMON_NET_PCAP_BASE) {
+               /*
+                * Converted pcap file - the LINKTYPE_ value
+                * is the network value with 0xF000 masked off.
+                */
+               network &= 0x0FFF;
+               pkt_encap = wtap_pcap_encap_to_wtap_encap(network);
+               if (pkt_encap == WTAP_ENCAP_UNKNOWN) {
+                       *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+                       *err_info = g_strdup_printf("netmon: converted pcap network type %u unknown or unsupported",
+                           network);
+                       return -1;      /* error */
+               }
+       } else if (network < NUM_NETMON_ENCAPS) {
+               /*
+                * Regular NetMon encapsulation.
+                */
+               pkt_encap = netmon_encap[network];
+               if (pkt_encap == WTAP_ENCAP_UNKNOWN) {
+                       *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+                       *err_info = g_strdup_printf("netmon: network type %u unknown or unsupported",
+                           network);
+                       return -1;      /* error */
+               }
+       } else {
+               /*
+                * Special packet type for metadata.
+                */
+               switch (network) {
+
+               case NETMON_NET_NETEVENT:
+               case NETMON_NET_NETWORK_INFO_EX:
+               case NETMON_NET_PAYLOAD_HEADER:
+               case NETMON_NET_NETWORK_INFO:
+               case NETMON_NET_DNS_CACHE:
+               case NETMON_NET_NETMON_FILTER:
+                       /*
+                        * Just ignore those record types, for
+                        * now.  Tell our caller to read the next
+                        * record.
+                        */
+                       return 0;
+
+               default:
+                       *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+                       *err_info = g_strdup_printf("netmon: network type %u unknown or unsupported",
+                           network);
+                       return -1;      /* error */
+               }
+       }
+
+       return pkt_encap;       /* success */
+}
+
 /* Throw away the frame table used by the sequential I/O stream. */
 static void
 netmon_sequential_close(wtap *wth)
@@ -636,18 +877,10 @@ int netmon_dump_can_write_encap(int encap)
 
 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
    failure */
-gboolean netmon_dump_open(wtap_dumper *wdh, gboolean cant_seek, int *err)
+gboolean netmon_dump_open(wtap_dumper *wdh, int *err)
 {
        netmon_dump_t *netmon;
 
-       /* This is a NetMon file.  We can't fill in some fields in the
-          header until all the packets have been written, so we can't
-          write to a pipe. */
-       if (cant_seek) {
-               *err = WTAP_ERR_CANT_WRITE_TO_PIPE;
-               return FALSE;
-       }
-
        /* We can't fill in all the fields in the file header, as we
           haven't yet written any packets.  As we'll have to rewrite
           the header when we've written out all the packets, we just
@@ -681,7 +914,6 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
        struct netmonrec_2_x_hdr rec_2_x_hdr;
        char *hdrp;
        size_t hdr_size;
-       size_t nwritten;
        double t;
        guint32 time_low, time_high;
        struct netmon_atm_hdr atm_hdr;
@@ -738,14 +970,8 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
                return FALSE;
        }
 
-       nwritten = fwrite(hdrp, 1, hdr_size, wdh->fh);
-       if (nwritten != hdr_size) {
-               if (nwritten == 0 && ferror(wdh->fh))
-                       *err = errno;
-               else
-                       *err = WTAP_ERR_SHORT_WRITE;
+       if (!wtap_dump_file_write(wdh, hdrp, hdr_size, err))
                return FALSE;
-       }
 
        if (wdh->encap == WTAP_ENCAP_ATM_PDUS) {
                /*
@@ -756,24 +982,12 @@ static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
                memset(&atm_hdr.src, 0, sizeof atm_hdr.src);
                atm_hdr.vpi = g_htons(pseudo_header->atm.vpi);
                atm_hdr.vci = g_htons(pseudo_header->atm.vci);
-               nwritten = fwrite(&atm_hdr, 1, sizeof atm_hdr, wdh->fh);
-               if (nwritten != sizeof atm_hdr) {
-                       if (nwritten == 0 && ferror(wdh->fh))
-                               *err = errno;
-                       else
-                               *err = WTAP_ERR_SHORT_WRITE;
+               if (!wtap_dump_file_write(wdh, &atm_hdr, sizeof atm_hdr, err))
                        return FALSE;
-               }
        }
 
-       nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
-       if (nwritten != phdr->caplen) {
-               if (nwritten == 0 && ferror(wdh->fh))
-                       *err = errno;
-               else
-                       *err = WTAP_ERR_SHORT_WRITE;
+       if (!wtap_dump_file_write(wdh, pd, phdr->caplen, err))
                return FALSE;
-       }
 
        /*
         * Stash the file offset of this frame.
@@ -811,7 +1025,6 @@ static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
 {
        netmon_dump_t *netmon = (netmon_dump_t *)wdh->priv;
        size_t n_to_write;
-       size_t nwritten;
        struct netmon_hdr file_hdr;
        const char *magicp;
        size_t magic_size;
@@ -820,16 +1033,8 @@ static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
        /* Write out the frame table.  "netmon->frame_table_index" is
           the number of entries we've put into it. */
        n_to_write = netmon->frame_table_index * sizeof *netmon->frame_table;
-       nwritten = fwrite(netmon->frame_table, 1, n_to_write, wdh->fh);
-       if (nwritten != n_to_write) {
-               if (err != NULL) {
-                       if (nwritten == 0 && ferror(wdh->fh))
-                               *err = errno;
-                       else
-                               *err = WTAP_ERR_SHORT_WRITE;
-               }
+       if (!wtap_dump_file_write(wdh, netmon->frame_table, n_to_write, err))
                return FALSE;
-       }
 
        /* Now go fix up the file header. */
        fseek(wdh->fh, 0, SEEK_SET);
@@ -862,16 +1067,8 @@ static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
                        *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
                return FALSE;
        }
-       nwritten = fwrite(magicp, 1, magic_size, wdh->fh);
-       if (nwritten != magic_size) {
-               if (err != NULL) {
-                       if (nwritten == 0 && ferror(wdh->fh))
-                               *err = errno;
-                       else
-                               *err = WTAP_ERR_SHORT_WRITE;
-               }
+       if (!wtap_dump_file_write(wdh, magicp, magic_size, err))
                return FALSE;
-       }
 
        file_hdr.network = htoles(wtap_encap[wdh->encap]);
        tm = localtime(&netmon->first_record_time.secs);
@@ -897,16 +1094,8 @@ static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
        file_hdr.frametableoffset = htolel(netmon->frame_table_offset);
        file_hdr.frametablelength =
            htolel(netmon->frame_table_index * sizeof *netmon->frame_table);
-       nwritten = fwrite(&file_hdr, 1, sizeof file_hdr, wdh->fh);
-       if (nwritten != sizeof file_hdr) {
-               if (err != NULL) {
-                       if (nwritten == 0 && ferror(wdh->fh))
-                               *err = errno;
-                       else
-                               *err = WTAP_ERR_SHORT_WRITE;
-               }
+       if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
                return FALSE;
-       }
 
        return TRUE;
 }