Revert "UNUSED enc_key_id_{equal,hash}"
[wireshark-sm.git] / wiretap / netmon.c
blob000014189aad78ba27145e58d3f901529f51983f
1 /* netmon.c
3 * Wiretap Library
4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
9 #include "config.h"
10 #include "netmon.h"
12 #include <errno.h>
13 #include <string.h>
14 #include <wsutil/unicode-utils.h>
15 #include "wtap-int.h"
16 #include "file_wrappers.h"
17 #include "atm.h"
18 #include "pcap-encap.h"
20 /* The file at
22 * ftp://ftp.microsoft.com/developr/drg/cifs/cifs/Bhfile.zip
24 * contains "STRUCT.H", which declares the typedef CAPTUREFILE_HEADER
25 * for the header of a Microsoft Network Monitor 1.x capture file.
27 * The help files for Network Monitor 3.x document the 2.x file format.
30 /* Capture file header, *including* magic number, is padded to 128 bytes. */
31 #define CAPTUREFILE_HEADER_SIZE 128
33 /* Magic number size, for both 1.x and 2.x. */
34 #define MAGIC_SIZE 4
36 /* Magic number in Network Monitor 1.x files. */
37 static const char netmon_1_x_magic[MAGIC_SIZE] = {
38 'R', 'T', 'S', 'S'
41 /* Magic number in Network Monitor 2.x files. */
42 static const char netmon_2_x_magic[MAGIC_SIZE] = {
43 'G', 'M', 'B', 'U'
46 /* Network Monitor file header (minus magic number). */
47 struct netmon_hdr {
48 uint8_t ver_minor; /* minor version number */
49 uint8_t ver_major; /* major version number */
50 uint16_t network; /* network type */
51 uint16_t ts_year; /* year of capture start */
52 uint16_t ts_month; /* month of capture start (January = 1) */
53 uint16_t ts_dow; /* day of week of capture start (Sun = 0) */
54 uint16_t ts_day; /* day of month of capture start */
55 uint16_t ts_hour; /* hour of capture start */
56 uint16_t ts_min; /* minute of capture start */
57 uint16_t ts_sec; /* second of capture start */
58 uint16_t ts_msec; /* millisecond of capture start */
59 uint32_t frametableoffset; /* frame index table offset */
60 uint32_t frametablelength; /* frame index table size */
61 uint32_t userdataoffset; /* user data offset */
62 uint32_t userdatalength; /* user data size */
63 uint32_t commentdataoffset; /* comment data offset */
64 uint32_t commentdatalength; /* comment data size */
65 uint32_t processinfooffset; /* offset to process info structure */
66 uint32_t processinfocount; /* number of process info structures */
67 uint32_t networkinfooffset; /* offset to network info structure */
68 uint32_t networkinfolength; /* length of network info structure */
71 /* Network Monitor 1.x record header; not defined in STRUCT.H, but deduced by
72 * looking at capture files. */
73 struct netmonrec_1_x_hdr {
74 uint32_t ts_delta; /* time stamp - msecs since start of capture */
75 uint16_t orig_len; /* actual length of packet */
76 uint16_t incl_len; /* number of octets captured in file */
80 * Network Monitor 2.x record header, as documented in NetMon 3.x's
81 * help files.
83 struct netmonrec_2_x_hdr {
84 uint64_t ts_delta; /* time stamp - usecs since start of capture */
85 uint32_t orig_len; /* actual length of packet */
86 uint32_t incl_len; /* number of octets captured in file */
90 * Network Monitor 2.1 and later record trailers; documented in the Network
91 * Monitor 3.x help files, for 3.3 and later, although they don't clearly
92 * state how the trailer format changes from version to version.
94 * Some fields are multi-byte integers, but they're not aligned on their
95 * natural boundaries.
97 struct netmonrec_2_1_trlr {
98 uint8_t network[2]; /* network type for this packet */
101 struct netmonrec_2_2_trlr {
102 uint8_t network[2]; /* network type for this packet */
103 uint8_t process_info_index[4]; /* index into the process info table */
106 struct netmonrec_2_3_trlr {
107 uint8_t network[2]; /* network type for this packet */
108 uint8_t process_info_index[4]; /* index into the process info table */
109 uint8_t utc_timestamp[8]; /* packet time stamp, as .1 us units since January 1, 1601, 00:00:00 UTC */
110 uint8_t timezone_index; /* index of time zone information */
113 struct netmonrec_comment {
114 uint32_t numFramePerComment; /* Currently, this is always set to 1. Each comment is attached to only one frame. */
115 uint32_t frameOffset; /* Offset in the capture file table that indicates the beginning of the frame. Key used to match comment with frame */
116 uint8_t* title; /* Comment title */
117 uint32_t descLength; /* Number of bytes in the comment description. Must be at least zero. */
118 uint8_t* description; /* Comment description */
121 /* Just the first few fields of netmonrec_comment so it can be read sequentially from file */
122 struct netmonrec_comment_header {
123 uint32_t numFramePerComment;
124 uint32_t frameOffset;
125 uint32_t titleLength;
128 union ip_address {
129 uint32_t ipv4;
130 ws_in6_addr ipv6;
133 struct netmonrec_process_info {
134 uint8_t* path; /* A Unicode string of length PathSize */
135 uint32_t iconSize;
136 uint8_t* iconData;
137 uint32_t pid;
138 uint16_t localPort;
139 uint16_t remotePort;
140 bool isIPv6;
141 union ip_address localAddr;
142 union ip_address remoteAddr;
146 * The link-layer header on ATM packets.
148 struct netmon_atm_hdr {
149 uint8_t dest[6]; /* "Destination address" - what is it? */
150 uint8_t src[6]; /* "Source address" - what is it? */
151 uint16_t vpi; /* VPI */
152 uint16_t vci; /* VCI */
155 typedef struct {
156 time_t start_secs;
157 uint32_t start_nsecs;
158 uint8_t version_major;
159 uint8_t version_minor;
160 uint32_t *frame_table;
161 uint32_t frame_table_size;
162 GHashTable* comment_table;
163 GHashTable* process_info_table;
164 unsigned current_frame;
165 } netmon_t;
168 * Maximum pathname length supported in the process table; the length
169 * is in a 32-bit field, so we impose a limit to prevent attempts to
170 * allocate too much memory.
172 * See
174 * https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation
176 * The NetMon 3.4 "Capture File Format" documentation says "PathSize must be
177 * greater than 0, and less than MAX_PATH (260 characters)", but, as per that
178 * link above, that limit has been raised in more recent systems.
180 * We pick a limit of 65536, as that should handle a path length of 32767
181 * UTF-16 octet pairs plus a trailing NUL octet pair.
183 #define MATH_PROCINFO_PATH_SIZE 65536
186 * XXX - at least in some NetMon 3.4 VPN captures, the per-packet
187 * link-layer type is 0, but the packets have Ethernet headers.
188 * We handle this by mapping 0 to WTAP_ENCAP_ETHERNET; should we,
189 * instead, use the per-file link-layer type?
191 static const int netmon_encap[] = {
192 WTAP_ENCAP_ETHERNET,
193 WTAP_ENCAP_ETHERNET,
194 WTAP_ENCAP_TOKEN_RING,
195 WTAP_ENCAP_FDDI_BITSWAPPED,
196 WTAP_ENCAP_ATM_PDUS, /* NDIS WAN - this is what's used for ATM */
197 WTAP_ENCAP_UNKNOWN, /* NDIS LocalTalk, but format 2.x uses it for IP-over-IEEE 1394 */
198 WTAP_ENCAP_IEEE_802_11_NETMON,
199 /* NDIS "DIX", but format 2.x uses it for 802.11 */
200 WTAP_ENCAP_RAW_IP, /* NDIS ARCNET raw, but format 2.x uses it for "Tunneling interfaces" */
201 WTAP_ENCAP_RAW_IP, /* NDIS ARCNET 878.2, but format 2.x uses it for "Wireless WAN" */
202 WTAP_ENCAP_RAW_IP, /* NDIS ATM (no, this is NOT used for ATM); format 2.x uses it for "Raw IP Frames" */
203 WTAP_ENCAP_UNKNOWN, /* NDIS Wireless WAN */
204 WTAP_ENCAP_UNKNOWN /* NDIS IrDA */
206 #define NUM_NETMON_ENCAPS array_length(netmon_encap)
209 * Special link-layer types.
211 #define NETMON_NET_PCAP_BASE 0xE000
212 #define NETMON_NET_NETEVENT 0xFFE0
213 #define NETMON_NET_NETWORK_INFO_EX 0xFFFB
214 #define NETMON_NET_PAYLOAD_HEADER 0xFFFC
215 #define NETMON_NET_NETWORK_INFO 0xFFFD
216 #define NETMON_NET_DNS_CACHE 0xFFFE
217 #define NETMON_NET_NETMON_FILTER 0xFFFF
219 static bool netmon_read(wtap *wth, wtap_rec *rec, Buffer *buf,
220 int *err, char **err_info, int64_t *data_offset);
221 static bool netmon_seek_read(wtap *wth, int64_t seek_off,
222 wtap_rec *rec, Buffer *buf, int *err, char **err_info);
223 static bool netmon_read_atm_pseudoheader(FILE_T fh,
224 union wtap_pseudo_header *pseudo_header, int *err, char **err_info);
225 static void netmon_close(wtap *wth);
226 static bool netmon_dump(wtap_dumper *wdh, const wtap_rec *rec,
227 const uint8_t *pd, int *err, char **err_info);
228 static bool netmon_dump_finish(wtap_dumper *wdh, int *err,
229 char **err_info);
231 static int netmon_1_x_file_type_subtype = -1;
232 static int netmon_2_x_file_type_subtype = -1;
234 void register_netmon(void);
237 * Convert a counted UTF-16 string, which is probably also null-terminated
238 * but is not guaranteed to be null-terminated (as it came from a file),
239 * to a null-terminated UTF-8 string.
241 static uint8_t *
242 utf_16_to_utf_8(const uint8_t *in, uint32_t length)
244 uint8_t *result, *out;
245 gunichar2 uchar2;
246 gunichar uchar;
247 size_t n_bytes;
248 uint32_t i;
251 * Get the length of the resulting UTF-8 string, and validate
252 * the input string in the process.
254 n_bytes = 0;
255 for (i = 0; i + 1 < length && (uchar2 = pletoh16(in + i)) != '\0';
256 i += 2) {
257 if (IS_LEAD_SURROGATE(uchar2)) {
259 * Lead surrogate. Must be followed by a trail
260 * surrogate.
262 gunichar2 lead_surrogate;
264 i += 2;
265 if (i + 1 >= length) {
267 * Oops, string ends with a lead surrogate.
268 * Ignore this for now.
269 * XXX - insert "substitute" character?
270 * Report the error in some other fashion?
272 break;
274 lead_surrogate = uchar2;
275 uchar2 = pletoh16(in + i);
276 if (uchar2 == '\0') {
278 * Oops, string ends with a lead surrogate.
279 * Ignore this for now.
280 * XXX - insert "substitute" character?
281 * Report the error in some other fashion?
283 break;
285 if (IS_TRAIL_SURROGATE(uchar2)) {
286 /* Trail surrogate. */
287 uchar = SURROGATE_VALUE(lead_surrogate, uchar2);
288 n_bytes += g_unichar_to_utf8(uchar, NULL);
289 } else {
291 * Not a trail surrogate.
292 * Ignore the entire pair.
293 * XXX - insert "substitute" character?
294 * Report the error in some other fashion?
298 } else {
299 if (IS_TRAIL_SURROGATE(uchar2)) {
301 * Trail surrogate without a preceding
302 * lead surrogate. Ignore it.
303 * XXX - insert "substitute" character?
304 * Report the error in some other fashion?
307 } else {
309 * Non-surrogate; just count it.
311 n_bytes += g_unichar_to_utf8(uchar2, NULL);
317 * Now allocate a buffer big enough for the UTF-8 string plus a
318 * trailing NUL, and generate the string.
320 result = (uint8_t *)g_malloc(n_bytes + 1);
322 out = result;
323 for (i = 0; i + 1 < length && (uchar2 = pletoh16(in + i)) != '\0';
324 i += 2) {
325 if (IS_LEAD_SURROGATE(uchar2)) {
327 * Lead surrogate. Must be followed by a trail
328 * surrogate.
330 gunichar2 lead_surrogate;
332 i += 2;
333 if (i + 1 >= length) {
335 * Oops, string ends with a lead surrogate.
336 * Ignore this for now.
337 * XXX - insert "substitute" character?
338 * Report the error in some other fashion?
340 break;
342 lead_surrogate = uchar2;
343 uchar2 = pletoh16(in + i);
344 if (uchar2 == '\0') {
346 * Oops, string ends with a lead surrogate.
347 * Ignore this for now.
348 * XXX - insert "substitute" character?
349 * Report the error in some other fashion?
351 break;
353 if (IS_TRAIL_SURROGATE(uchar2)) {
354 /* Trail surrogate. */
355 uchar = SURROGATE_VALUE(lead_surrogate, uchar2);
356 out += g_unichar_to_utf8(uchar, out);
357 } else {
359 * Not a trail surrogate.
360 * Ignore the entire pair.
361 * XXX - insert "substitute" character?
362 * Report the error in some other fashion?
366 } else {
367 if (IS_TRAIL_SURROGATE(uchar2)) {
369 * Trail surrogate without a preceding
370 * lead surrogate. Ignore it.
371 * XXX - insert "substitute" character?
372 * Report the error in some other fashion?
375 } else {
377 * Non-surrogate; just count it.
379 out += g_unichar_to_utf8(uchar2, out);
383 *out = '\0';
386 * XXX - if i < length, this means we were handed an odd
387 * number of bytes, so it was not a valid UTF-16 string.
389 return result;
393 static void netmonrec_comment_destroy(void *key) {
394 struct netmonrec_comment *comment = (struct netmonrec_comment*) key;
396 g_free(comment->title);
397 g_free(comment->description);
398 g_free(comment);
401 static void netmonrec_process_info_destroy(void *key) {
402 struct netmonrec_process_info *process_info = (struct netmonrec_process_info*) key;
404 g_free(process_info->path);
405 g_free(process_info->iconData);
406 g_free(process_info);
409 wtap_open_return_val netmon_open(wtap *wth, int *err, char **err_info)
411 char magic[MAGIC_SIZE];
412 struct netmon_hdr hdr;
413 int file_type;
414 struct tm tm;
415 uint32_t frame_table_offset;
416 uint32_t frame_table_length;
417 uint32_t frame_table_size;
418 uint32_t *frame_table;
419 uint32_t comment_table_offset, process_info_table_offset;
420 uint32_t comment_table_size, process_info_table_count;
421 GHashTable *comment_table, *process_info_table;
422 struct netmonrec_comment* comment_rec;
423 int64_t file_size = wtap_file_size(wth, err);
424 #if G_BYTE_ORDER == G_BIG_ENDIAN
425 unsigned int i;
426 #endif
427 netmon_t *netmon;
429 /* Read in the string that should be at the start of a Network
430 * Monitor file */
431 if (!wtap_read_bytes(wth->fh, magic, MAGIC_SIZE, err, err_info)) {
432 if (*err != WTAP_ERR_SHORT_READ)
433 return WTAP_OPEN_ERROR;
434 return WTAP_OPEN_NOT_MINE;
437 if (memcmp(magic, netmon_1_x_magic, MAGIC_SIZE) != 0 &&
438 memcmp(magic, netmon_2_x_magic, MAGIC_SIZE) != 0) {
439 return WTAP_OPEN_NOT_MINE;
442 /* Read the rest of the header. */
443 if (!wtap_read_bytes(wth->fh, &hdr, sizeof hdr, err, err_info))
444 return WTAP_OPEN_ERROR;
446 switch (hdr.ver_major) {
448 case 1:
449 file_type = netmon_1_x_file_type_subtype;
450 break;
452 case 2:
453 file_type = netmon_2_x_file_type_subtype;
454 break;
456 default:
457 *err = WTAP_ERR_UNSUPPORTED;
458 *err_info = ws_strdup_printf("netmon: major version %u unsupported", hdr.ver_major);
459 return WTAP_OPEN_ERROR;
462 hdr.network = pletoh16(&hdr.network);
463 if (hdr.network >= NUM_NETMON_ENCAPS
464 || netmon_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
465 *err = WTAP_ERR_UNSUPPORTED;
466 *err_info = ws_strdup_printf("netmon: network type %u unknown or unsupported",
467 hdr.network);
468 return WTAP_OPEN_ERROR;
471 /* This is a netmon file */
472 wth->file_type_subtype = file_type;
473 netmon = g_new0(netmon_t, 1);
474 wth->priv = (void *)netmon;
475 wth->subtype_read = netmon_read;
476 wth->subtype_seek_read = netmon_seek_read;
477 wth->subtype_close = netmon_close;
479 #if 0
480 /* NetMon capture file formats v2.1+ use per-packet encapsulation types. NetMon 3 sets the value in
481 * the header to 1 (Ethernet) for backwards compatibility. */
482 if((hdr.ver_major == 2 && hdr.ver_minor >= 1) || hdr.ver_major > 2)
483 wth->file_encap = WTAP_ENCAP_PER_PACKET;
484 else
485 #endif
486 wth->file_encap = netmon_encap[hdr.network];
488 wth->snapshot_length = 0; /* not available in header */
490 * Convert the time stamp to a "time_t" and a number of
491 * milliseconds.
493 tm.tm_year = pletoh16(&hdr.ts_year) - 1900;
494 tm.tm_mon = pletoh16(&hdr.ts_month) - 1;
495 tm.tm_mday = pletoh16(&hdr.ts_day);
496 tm.tm_hour = pletoh16(&hdr.ts_hour);
497 tm.tm_min = pletoh16(&hdr.ts_min);
498 tm.tm_sec = pletoh16(&hdr.ts_sec);
499 tm.tm_isdst = -1;
500 netmon->start_secs = mktime(&tm);
502 * XXX - what if "secs" is -1? Unlikely, but if the capture was
503 * done in a time zone that switches between standard and summer
504 * time sometime other than when we do, and thus the time was one
505 * that doesn't exist here because a switch from standard to summer
506 * time zips over it, it could happen.
508 * On the other hand, if the capture was done in a different time
509 * zone, this won't work right anyway; unfortunately, the time
510 * zone isn't stored in the capture file (why the hell didn't
511 * they stuff a FILETIME, which is the number of 100-nanosecond
512 * intervals since 1601-01-01 00:00:00 "UTC", there, instead
513 * of stuffing a SYSTEMTIME, which is time-zone-dependent, there?).
515 * Eventually they went with per-packet FILETIMEs in a later
516 * version.
518 netmon->start_nsecs = pletoh16(&hdr.ts_msec)*1000000;
520 netmon->version_major = hdr.ver_major;
521 netmon->version_minor = hdr.ver_minor;
524 * Get the offset of the frame index table.
526 frame_table_offset = pletoh32(&hdr.frametableoffset);
529 * For NetMon 2.2 format and later, get the offset and length of
530 * the comment index table and process info table.
532 * For earlier versions, set them to zero; they appear to be
533 * uninitialized, so they're not necessarily zero.
535 if ((netmon->version_major == 2 && netmon->version_minor >= 2) ||
536 netmon->version_major > 2) {
537 comment_table_offset = pletoh32(&hdr.commentdataoffset);
538 comment_table_size = pletoh32(&hdr.commentdatalength);
539 process_info_table_offset = pletoh32(&hdr.processinfooffset);
540 process_info_table_count = pletoh32(&hdr.processinfocount);
541 } else {
542 comment_table_offset = 0;
543 comment_table_size = 0;
544 process_info_table_offset = 0;
545 process_info_table_count = 0;
549 * It appears that some NetMon 2.x files don't have the
550 * first packet starting exactly 128 bytes into the file.
552 * Furthermore, it also appears that there are "holes" in
553 * the file, i.e. frame N+1 doesn't always follow immediately
554 * after frame N.
556 * Therefore, we must read the frame table, and use the offsets
557 * in it as the offsets of the frames.
559 frame_table_length = pletoh32(&hdr.frametablelength);
560 frame_table_size = frame_table_length / (uint32_t)sizeof (uint32_t);
561 if ((frame_table_size * sizeof (uint32_t)) != frame_table_length) {
562 *err = WTAP_ERR_BAD_FILE;
563 *err_info = ws_strdup_printf("netmon: frame table length is %u, which is not a multiple of the size of an entry",
564 frame_table_length);
565 return WTAP_OPEN_ERROR;
567 if (frame_table_size == 0) {
568 *err = WTAP_ERR_BAD_FILE;
569 *err_info = ws_strdup_printf("netmon: frame table length is %u, which means it's less than one entry in size",
570 frame_table_length);
571 return WTAP_OPEN_ERROR;
574 * XXX - clamp the size of the frame table, so that we don't
575 * attempt to allocate a huge frame table and fail.
577 * Given that file offsets in the frame table are 32-bit,
578 * a NetMon file cannot be bigger than 2^32 bytes.
579 * Given that a NetMon 1.x-format packet header is 8 bytes,
580 * that means a NetMon file cannot have more than
581 * 512*2^20 packets. We'll pick that as the limit for
582 * now; it's 1/8th of a 32-bit address space, which is
583 * probably not going to exhaust the address space all by
584 * itself, and probably won't exhaust the backing store.
586 if (frame_table_size > 512*1024*1024) {
587 *err = WTAP_ERR_BAD_FILE;
588 *err_info = ws_strdup_printf("netmon: frame table length is %u, which is larger than we support",
589 frame_table_length);
590 return WTAP_OPEN_ERROR;
592 if (file_seek(wth->fh, frame_table_offset, SEEK_SET, err) == -1) {
593 return WTAP_OPEN_ERROR;
597 * Sanity check the comment table information before we bother to allocate
598 * large chunks of memory for the frame table
600 if (comment_table_size > 0) {
602 * XXX - clamp the size of the comment table, so that we don't
603 * attempt to allocate a huge comment table and fail.
605 * Just use same size requires as frame table
607 if (comment_table_size > 512*1024*1024) {
608 *err = WTAP_ERR_BAD_FILE;
609 *err_info = ws_strdup_printf("netmon: comment table size is %u, which is larger than we support",
610 comment_table_size);
611 return WTAP_OPEN_ERROR;
614 if (comment_table_size < 17) {
615 *err = WTAP_ERR_BAD_FILE;
616 *err_info = ws_strdup_printf("netmon: comment table size is %u, which is too small to use",
617 comment_table_size);
618 return WTAP_OPEN_ERROR;
621 if (comment_table_offset > file_size) {
622 *err = WTAP_ERR_BAD_FILE;
623 *err_info = ws_strdup_printf("netmon: comment table offset (%u) is larger than file",
624 comment_table_offset);
625 return WTAP_OPEN_ERROR;
630 * Sanity check the process info table information before we bother to allocate
631 * large chunks of memory for the frame table
633 if ((process_info_table_offset > 0) && (process_info_table_count > 0)) {
635 * XXX - clamp the size of the process info table, so that we don't
636 * attempt to allocate a huge process info table and fail.
638 if (process_info_table_count > 512*1024) {
639 *err = WTAP_ERR_BAD_FILE;
640 *err_info = ws_strdup_printf("netmon: process info table size is %u, which is larger than we support",
641 process_info_table_count);
642 return WTAP_OPEN_ERROR;
645 if (process_info_table_offset > file_size) {
646 *err = WTAP_ERR_BAD_FILE;
647 *err_info = ws_strdup_printf("netmon: process info table offset (%u) is larger than file",
648 process_info_table_offset);
649 return WTAP_OPEN_ERROR;
654 * Return back to the frame table offset
656 if (file_seek(wth->fh, frame_table_offset, SEEK_SET, err) == -1) {
657 return WTAP_OPEN_ERROR;
661 * Sanity check the process info table information before we bother to allocate
662 * large chunks of memory for the frame table
665 frame_table = (uint32_t *)g_try_malloc(frame_table_length);
666 if (frame_table_length != 0 && frame_table == NULL) {
667 *err = ENOMEM; /* we assume we're out of memory */
668 return WTAP_OPEN_ERROR;
670 if (!wtap_read_bytes(wth->fh, frame_table, frame_table_length,
671 err, err_info)) {
672 g_free(frame_table);
673 return WTAP_OPEN_ERROR;
675 netmon->frame_table_size = frame_table_size;
676 netmon->frame_table = frame_table;
678 if (comment_table_size > 0) {
679 comment_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, netmonrec_comment_destroy);
680 if (comment_table == NULL) {
681 *err = ENOMEM; /* we assume we're out of memory */
682 return WTAP_OPEN_ERROR;
685 /* Make sure the file contains the full comment section */
686 if (file_seek(wth->fh, comment_table_offset+comment_table_size, SEEK_SET, err) == -1) {
687 g_hash_table_destroy(comment_table);
688 return WTAP_OPEN_ERROR;
691 if (file_seek(wth->fh, comment_table_offset, SEEK_SET, err) == -1) {
692 /* Shouldn't fail... */
693 g_hash_table_destroy(comment_table);
694 return WTAP_OPEN_ERROR;
697 while (comment_table_size > 16) {
698 struct netmonrec_comment_header comment_header;
699 uint32_t title_length;
700 uint32_t desc_length;
701 uint8_t *utf16_str;
703 /* Read the first 12 bytes of the structure */
704 if (!wtap_read_bytes(wth->fh, &comment_header, 12, err, err_info)) {
705 g_hash_table_destroy(comment_table);
706 return WTAP_OPEN_ERROR;
708 comment_table_size -= 12;
710 /* Make sure comment size is sane */
711 title_length = pletoh32(&comment_header.titleLength);
712 if (title_length == 0) {
713 *err = WTAP_ERR_BAD_FILE;
714 *err_info = g_strdup("netmon: comment title size can't be 0");
715 g_hash_table_destroy(comment_table);
716 return WTAP_OPEN_ERROR;
718 if (title_length > comment_table_size) {
719 *err = WTAP_ERR_BAD_FILE;
720 *err_info = ws_strdup_printf("netmon: comment title size is %u, which is larger than the amount remaining in the comment section (%u)",
721 title_length, comment_table_size);
722 g_hash_table_destroy(comment_table);
723 return WTAP_OPEN_ERROR;
726 comment_rec = g_new0(struct netmonrec_comment, 1);
727 comment_rec->numFramePerComment = pletoh32(&comment_header.numFramePerComment);
728 comment_rec->frameOffset = pletoh32(&comment_header.frameOffset);
730 g_hash_table_insert(comment_table, GUINT_TO_POINTER(comment_rec->frameOffset), comment_rec);
733 * Read in the comment title.
735 * It is in UTF-16-encoded Unicode, and the title
736 * size is a count of octets, not octet pairs or
737 * Unicode characters.
739 utf16_str = (uint8_t*)g_malloc(title_length);
740 if (!wtap_read_bytes(wth->fh, utf16_str, title_length,
741 err, err_info)) {
742 g_hash_table_destroy(comment_table);
743 return WTAP_OPEN_ERROR;
745 comment_table_size -= title_length;
748 * Now convert it to UTF-8 for internal use.
750 comment_rec->title = utf_16_to_utf_8(utf16_str,
751 title_length);
752 g_free(utf16_str);
754 if (comment_table_size < 4) {
755 *err = WTAP_ERR_BAD_FILE;
756 *err_info = g_strdup("netmon: corrupt comment section");
757 g_hash_table_destroy(comment_table);
758 return WTAP_OPEN_ERROR;
761 if (!wtap_read_bytes(wth->fh, &desc_length, 4, err, err_info)) {
762 g_hash_table_destroy(comment_table);
763 return WTAP_OPEN_ERROR;
765 comment_table_size -= 4;
767 comment_rec->descLength = pletoh32(&desc_length);
768 if (comment_rec->descLength > 0) {
769 /* Make sure comment size is sane */
770 if (comment_rec->descLength > comment_table_size) {
771 *err = WTAP_ERR_BAD_FILE;
772 *err_info = ws_strdup_printf("netmon: comment description size is %u, which is larger than the amount remaining in the comment section (%u)",
773 comment_rec->descLength, comment_table_size);
774 g_hash_table_destroy(comment_table);
775 return WTAP_OPEN_ERROR;
778 comment_rec->description = (uint8_t*)g_malloc(comment_rec->descLength);
780 /* Read the comment description */
781 if (!wtap_read_bytes(wth->fh, comment_rec->description, comment_rec->descLength, err, err_info)) {
782 g_hash_table_destroy(comment_table);
783 return WTAP_OPEN_ERROR;
786 comment_table_size -= comment_rec->descLength;
789 netmon->comment_table = comment_table;
792 if ((process_info_table_offset > 0) && (process_info_table_count > 0)) {
793 uint16_t version;
795 /* Go to the process table offset */
796 if (file_seek(wth->fh, process_info_table_offset, SEEK_SET, err) == -1) {
797 return WTAP_OPEN_ERROR;
800 process_info_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, netmonrec_process_info_destroy);
801 if (process_info_table == NULL) {
802 *err = ENOMEM; /* we assume we're out of memory */
803 return WTAP_OPEN_ERROR;
806 /* Read the version (ignored for now) */
807 if (!wtap_read_bytes(wth->fh, &version, 2, err, err_info)) {
808 g_hash_table_destroy(process_info_table);
809 return WTAP_OPEN_ERROR;
812 while (process_info_table_count > 0)
814 struct netmonrec_process_info* process_info;
815 uint32_t tmp32;
816 uint16_t tmp16;
817 uint32_t path_size;
818 uint8_t *utf16_str;
820 process_info = g_new0(struct netmonrec_process_info, 1);
822 /* Read path */
823 if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) {
824 g_free(process_info);
825 g_hash_table_destroy(process_info_table);
826 return WTAP_OPEN_ERROR;
829 path_size = pletoh32(&tmp32);
830 if (path_size > MATH_PROCINFO_PATH_SIZE) {
831 *err = WTAP_ERR_BAD_FILE;
832 *err_info = ws_strdup_printf("netmon: Path size for process info record is %u, which is larger than allowed max value (%u)",
833 path_size, MATH_PROCINFO_PATH_SIZE);
834 g_free(process_info);
835 g_hash_table_destroy(process_info_table);
836 return WTAP_OPEN_ERROR;
840 * Read in the path string.
842 * It is in UTF-16-encoded Unicode, and the path
843 * size is a count of octets, not octet pairs or
844 * Unicode characters.
846 utf16_str = (uint8_t*)g_malloc(path_size);
847 if (!wtap_read_bytes(wth->fh, utf16_str, path_size,
848 err, err_info)) {
849 g_free(process_info);
850 g_hash_table_destroy(process_info_table);
851 return WTAP_OPEN_ERROR;
855 * Now convert it to UTF-8 for internal use.
857 process_info->path = utf_16_to_utf_8(utf16_str,
858 path_size);
859 g_free(utf16_str);
861 /* Read icon (currently not saved) */
862 if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) {
863 g_free(process_info);
864 g_hash_table_destroy(process_info_table);
865 return WTAP_OPEN_ERROR;
868 process_info->iconSize = pletoh32(&tmp32);
870 /* XXX - skip the icon for now */
871 if (file_seek(wth->fh, process_info->iconSize, SEEK_CUR, err) == -1) {
872 g_free(process_info);
873 g_hash_table_destroy(process_info_table);
874 return WTAP_OPEN_ERROR;
876 process_info->iconSize = 0;
878 if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) {
879 g_free(process_info);
880 g_hash_table_destroy(process_info_table);
881 return WTAP_OPEN_ERROR;
883 process_info->pid = pletoh32(&tmp32);
885 /* XXX - Currently index process information by PID */
886 g_hash_table_insert(process_info_table, GUINT_TO_POINTER(process_info->pid), process_info);
888 /* Read local port */
889 if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) {
890 g_hash_table_destroy(process_info_table);
891 return WTAP_OPEN_ERROR;
893 process_info->localPort = pletoh16(&tmp16);
895 /* Skip padding */
896 if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) {
897 g_hash_table_destroy(process_info_table);
898 return WTAP_OPEN_ERROR;
901 /* Read remote port */
902 if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) {
903 g_hash_table_destroy(process_info_table);
904 return WTAP_OPEN_ERROR;
906 process_info->remotePort = pletoh16(&tmp16);
908 /* Skip padding */
909 if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) {
910 g_hash_table_destroy(process_info_table);
911 return WTAP_OPEN_ERROR;
914 /* Determine IP version */
915 if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) {
916 g_hash_table_destroy(process_info_table);
917 return WTAP_OPEN_ERROR;
919 process_info->isIPv6 = ((pletoh32(&tmp32) == 0) ? false : true);
921 if (process_info->isIPv6) {
922 if (!wtap_read_bytes(wth->fh, &process_info->localAddr.ipv6, 16, err, err_info)) {
923 g_hash_table_destroy(process_info_table);
924 return WTAP_OPEN_ERROR;
926 if (!wtap_read_bytes(wth->fh, &process_info->remoteAddr.ipv6, 16, err, err_info)) {
927 g_hash_table_destroy(process_info_table);
928 return WTAP_OPEN_ERROR;
930 } else {
931 uint8_t ipbuffer[16];
932 if (!wtap_read_bytes(wth->fh, ipbuffer, 16, err, err_info)) {
933 g_hash_table_destroy(process_info_table);
934 return WTAP_OPEN_ERROR;
936 process_info->localAddr.ipv4 = pletoh32(ipbuffer);
938 if (!wtap_read_bytes(wth->fh, ipbuffer, 16, err, err_info)) {
939 g_hash_table_destroy(process_info_table);
940 return WTAP_OPEN_ERROR;
942 process_info->remoteAddr.ipv4 = pletoh32(ipbuffer);
945 process_info_table_count--;
948 netmon->process_info_table = process_info_table;
951 #if G_BYTE_ORDER == G_BIG_ENDIAN
953 * OK, now byte-swap the frame table.
955 for (i = 0; i < frame_table_size; i++)
956 frame_table[i] = pletoh32(&frame_table[i]);
957 #endif
959 /* Set up to start reading at the first frame. */
960 netmon->current_frame = 0;
961 switch (netmon->version_major) {
963 case 1:
965 * Version 1.x of the file format supports
966 * millisecond precision.
968 wth->file_tsprec = WTAP_TSPREC_MSEC;
969 break;
971 case 2:
973 * Versions 2.0 through 2.2 support microsecond
974 * precision; version 2.3 supports 100-nanosecond
975 * precision (2.3 was the last version).
977 if (netmon->version_minor >= 3)
978 wth->file_tsprec = WTAP_TSPREC_100_NSEC;
979 else
980 wth->file_tsprec = WTAP_TSPREC_USEC;
981 break;
983 return WTAP_OPEN_MINE;
986 static void
987 netmon_set_pseudo_header_info(wtap_rec *rec, Buffer *buf)
989 switch (rec->rec_header.packet_header.pkt_encap) {
991 case WTAP_ENCAP_ATM_PDUS:
993 * Attempt to guess from the packet data, the VPI, and
994 * the VCI information about the type of traffic.
996 atm_guess_traffic_type(rec, ws_buffer_start_ptr(buf));
997 break;
999 case WTAP_ENCAP_ETHERNET:
1001 * We assume there's no FCS in this frame.
1003 rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
1004 break;
1006 case WTAP_ENCAP_IEEE_802_11_NETMON:
1008 * The 802.11 metadata at the beginnning of the frame data
1009 * is processed by a dissector, which fills in a pseudo-
1010 * header and passes it to the 802.11 radio dissector,
1011 * just as is done with other 802.11 radio metadata headers
1012 * that are part of the packet data, such as radiotap.
1014 break;
1018 typedef enum {
1019 SUCCESS,
1020 FAILURE,
1021 RETRY
1022 } process_record_retval;
1024 static process_record_retval
1025 netmon_process_record(wtap *wth, FILE_T fh, wtap_rec *rec,
1026 Buffer *buf, int *err, char **err_info)
1028 netmon_t *netmon = (netmon_t *)wth->priv;
1029 int hdr_size = 0;
1030 union {
1031 struct netmonrec_1_x_hdr hdr_1_x;
1032 struct netmonrec_2_x_hdr hdr_2_x;
1033 } hdr;
1034 int64_t delta = 0; /* signed - frame times can be before the nominal start */
1035 int64_t t;
1036 time_t secs;
1037 int nsecs;
1038 uint32_t packet_size = 0;
1039 uint32_t orig_size = 0;
1040 int trlr_size;
1041 union {
1042 struct netmonrec_2_1_trlr trlr_2_1;
1043 struct netmonrec_2_2_trlr trlr_2_2;
1044 struct netmonrec_2_3_trlr trlr_2_3;
1045 } trlr;
1046 uint16_t network;
1047 int pkt_encap;
1048 struct netmonrec_comment* comment_rec = NULL;
1050 /* Read record header. */
1051 switch (netmon->version_major) {
1053 case 1:
1054 hdr_size = sizeof (struct netmonrec_1_x_hdr);
1055 break;
1057 case 2:
1058 hdr_size = sizeof (struct netmonrec_2_x_hdr);
1059 break;
1061 if (!wtap_read_bytes_or_eof(fh, &hdr, hdr_size, err, err_info))
1062 return FAILURE;
1064 switch (netmon->version_major) {
1066 case 1:
1067 orig_size = pletoh16(&hdr.hdr_1_x.orig_len);
1068 packet_size = pletoh16(&hdr.hdr_1_x.incl_len);
1069 break;
1071 case 2:
1072 orig_size = pletoh32(&hdr.hdr_2_x.orig_len);
1073 packet_size = pletoh32(&hdr.hdr_2_x.incl_len);
1074 break;
1076 if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
1078 * Probably a corrupt capture file; don't blow up trying
1079 * to allocate space for an immensely-large packet.
1081 *err = WTAP_ERR_BAD_FILE;
1082 *err_info = ws_strdup_printf("netmon: File has %u-byte packet, bigger than maximum of %u",
1083 packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
1084 return FAILURE;
1087 rec->rec_type = REC_TYPE_PACKET;
1088 rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
1091 * If this is an ATM packet, the first
1092 * "sizeof (struct netmon_atm_hdr)" bytes have destination and
1093 * source addresses (6 bytes - MAC addresses of some sort?)
1094 * and the VPI and VCI; read them and generate the pseudo-header
1095 * from them.
1097 switch (wth->file_encap) {
1099 case WTAP_ENCAP_ATM_PDUS:
1100 if (packet_size < sizeof (struct netmon_atm_hdr)) {
1102 * Uh-oh, the packet isn't big enough to even
1103 * have a pseudo-header.
1105 *err = WTAP_ERR_BAD_FILE;
1106 *err_info = ws_strdup_printf("netmon: ATM file has a %u-byte packet, too small to have even an ATM pseudo-header",
1107 packet_size);
1108 return FAILURE;
1110 if (!netmon_read_atm_pseudoheader(fh, &rec->rec_header.packet_header.pseudo_header,
1111 err, err_info))
1112 return FAILURE; /* Read error */
1115 * Don't count the pseudo-header as part of the packet.
1117 orig_size -= (unsigned)sizeof (struct netmon_atm_hdr);
1118 packet_size -= (unsigned)sizeof (struct netmon_atm_hdr);
1119 break;
1121 default:
1122 break;
1125 switch (netmon->version_major) {
1127 case 1:
1129 * According to Paul Long, this offset is unsigned.
1130 * It's 32 bits, so the maximum value will fit in
1131 * a int64_t such as delta, even after multiplying
1132 * it by 1000000.
1134 * pletoh32() returns a uint32_t; we cast it to int64_t
1135 * before multiplying, so that the product doesn't
1136 * overflow a uint32_t.
1138 delta = ((int64_t)pletoh32(&hdr.hdr_1_x.ts_delta))*1000000;
1139 break;
1141 case 2:
1143 * OK, this is weird. Microsoft's documentation
1144 * says this is in microseconds and is a 64-bit
1145 * unsigned number, but it can be negative; they
1146 * say what appears to amount to "treat it as an
1147 * unsigned number, multiply it by 10, and then
1148 * interpret the resulting 64-bit quantity as a
1149 * signed number". That operation can turn a
1150 * value with the uppermost bit 0 to a value with
1151 * the uppermost bit 1, hence turning a large
1152 * positive number-of-microseconds into a small
1153 * negative number-of-100-nanosecond-increments.
1155 delta = pletoh64(&hdr.hdr_2_x.ts_delta)*10;
1158 * OK, it's now a signed value in 100-nanosecond
1159 * units. Now convert it to nanosecond units.
1161 delta *= 100;
1162 break;
1164 secs = 0;
1165 t = netmon->start_nsecs + delta;
1166 while (t < 0) {
1168 * Propagate a borrow into the seconds.
1169 * The seconds is a time_t, and can be < 0
1170 * (unlikely, as Windows didn't exist before
1171 * January 1, 1970, 00:00:00 UTC), while the
1172 * nanoseconds should be positive, as in
1173 * "nanoseconds since the instant of time
1174 * represented by the seconds".
1176 * We do not want t to be negative, as, according
1177 * to the C90 standard, "if either operand [of /
1178 * or %] is negative, whether the result of the
1179 * / operator is the largest integer less than or
1180 * equal to the algebraic quotient or the smallest
1181 * greater than or equal to the algebraic quotient
1182 * is implementation-defined, as is the sign of
1183 * the result of the % operator", and we want
1184 * the result of the division and remainder
1185 * operations to be the same on all platforms.
1187 t += 1000000000;
1188 secs--;
1190 secs += (time_t)(t/1000000000);
1191 nsecs = (int)(t%1000000000);
1192 rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
1193 rec->ts.secs = netmon->start_secs + secs;
1194 rec->ts.nsecs = nsecs;
1195 rec->rec_header.packet_header.caplen = packet_size;
1196 rec->rec_header.packet_header.len = orig_size;
1199 * Read the packet data.
1201 if (!wtap_read_packet_bytes(fh, buf, rec->rec_header.packet_header.caplen, err, err_info))
1202 return FAILURE;
1205 * For version 2.1 and later, there's additional information
1206 * after the frame data.
1208 if (netmon->version_major == 2 && netmon->version_minor >= 1) {
1209 switch (netmon->version_minor) {
1211 case 1:
1212 trlr_size = (int)sizeof (struct netmonrec_2_1_trlr);
1213 break;
1215 case 2:
1216 trlr_size = (int)sizeof (struct netmonrec_2_2_trlr);
1217 break;
1219 default:
1220 trlr_size = (int)sizeof (struct netmonrec_2_3_trlr);
1221 break;
1224 if (!wtap_read_bytes(fh, &trlr, trlr_size, err, err_info))
1225 return FAILURE;
1227 network = pletoh16(trlr.trlr_2_1.network);
1228 if ((network >= 0xE080) && (network <= 0xE08A)) {
1229 /* These values "violate" the LINKTYPE_ media type values
1230 * in Microsoft Analyzer and are considered a MAExportedMediaType,
1231 * so they need their own WTAP_ types
1233 switch (network)
1235 case 0xE080: // "WiFi Message"
1236 pkt_encap = WTAP_ENCAP_IEEE_802_11;
1237 break;
1238 case 0xE081: // "Ndis Etw WiFi Channel Message"
1239 case 0xE082: // "Fiddler Netmon Message"
1240 case 0xE089: // "Pef Ndis Msg";
1241 case 0xE08A: // "Pef Ndis Wifi Meta Msg";
1242 *err = WTAP_ERR_UNSUPPORTED;
1243 *err_info = ws_strdup_printf("netmon: network type %u unknown or unsupported", network);
1244 return FAILURE;
1245 case 0xE083:
1246 pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_V4;
1247 break;
1248 case 0xE084:
1249 pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_V6;
1250 break;
1251 case 0xE085:
1252 pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_2V4;
1253 break;
1254 case 0xE086:
1255 pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_2V6;
1256 break;
1257 case 0xE087:
1258 pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_AUTH_V4;
1259 break;
1260 case 0xE088:
1261 pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_AUTH_V6;
1262 break;
1263 default:
1264 pkt_encap = WTAP_ENCAP_UNKNOWN;
1265 break;
1267 } else if ((network & 0xF000) == NETMON_NET_PCAP_BASE) {
1269 * Converted pcap file - the LINKTYPE_ value
1270 * is the network value with 0xF000 masked off.
1272 network &= 0x0FFF;
1273 pkt_encap = wtap_pcap_encap_to_wtap_encap(network);
1274 if (pkt_encap == WTAP_ENCAP_UNKNOWN) {
1275 *err = WTAP_ERR_UNSUPPORTED;
1276 *err_info = ws_strdup_printf("netmon: converted pcap network type %u unknown or unsupported",
1277 network);
1278 return FAILURE;
1280 } else if (network < NUM_NETMON_ENCAPS) {
1282 * Regular NetMon encapsulation.
1284 pkt_encap = netmon_encap[network];
1285 if (pkt_encap == WTAP_ENCAP_UNKNOWN) {
1286 *err = WTAP_ERR_UNSUPPORTED;
1287 *err_info = ws_strdup_printf("netmon: network type %u unknown or unsupported",
1288 network);
1289 return FAILURE;
1291 } else {
1293 * Special packet type for metadata.
1295 switch (network) {
1297 case NETMON_NET_NETEVENT:
1299 * Event Tracing event.
1301 * https://docs.microsoft.com/en-us/windows/win32/api/evntcons/ns-evntcons-event_header
1303 pkt_encap = WTAP_ENCAP_NETMON_NET_NETEVENT;
1304 break;
1306 case NETMON_NET_NETWORK_INFO_EX:
1308 * List of adapters on which the capture
1309 * was done.
1310 * XXX - this could be translated into pcapng
1311 * blocks but for now, just treat as a frame.
1313 pkt_encap = WTAP_ENCAP_NETMON_NETWORK_INFO_EX;
1314 break;
1316 case NETMON_NET_PAYLOAD_HEADER:
1318 * Header for a fake frame constructed
1319 * by reassembly.
1321 return RETRY;
1323 case NETMON_NET_NETWORK_INFO:
1325 * List of adapters on which the capture
1326 * was done.
1328 return RETRY;
1330 case NETMON_NET_DNS_CACHE:
1332 * List of resolved IP addresses.
1334 return RETRY;
1336 case NETMON_NET_NETMON_FILTER:
1338 * NetMon capture or display filter
1339 * string.
1341 pkt_encap = WTAP_ENCAP_NETMON_NET_FILTER;
1342 break;
1344 default:
1345 *err = WTAP_ERR_UNSUPPORTED;
1346 *err_info = ws_strdup_printf("netmon: network type %u unknown or unsupported",
1347 network);
1348 return FAILURE;
1352 rec->rec_header.packet_header.pkt_encap = pkt_encap;
1353 if (netmon->version_minor >= 3) {
1355 * This is a 2.3 or later file. That format
1356 * contains a UTC per-packet time stamp; use
1357 * that instead of the start time and offset.
1359 uint64_t d;
1361 d = pletoh64(trlr.trlr_2_3.utc_timestamp);
1364 * Get the time as seconds and nanoseconds.
1365 * and overwrite the time stamp obtained
1366 * from the record header.
1368 if (!filetime_to_nstime(&rec->ts, d)) {
1369 *err = WTAP_ERR_BAD_FILE;
1370 *err_info = g_strdup("netmon: time stamp outside supported range");
1371 return FAILURE;
1376 netmon_set_pseudo_header_info(rec, buf);
1378 /* If any header specific information is present, set it as pseudo header data
1379 * and set the encapsulation type, so it can be handled to the netmon_header
1380 * dissector for further processing
1382 if (netmon->comment_table != NULL) {
1383 comment_rec = (struct netmonrec_comment*)g_hash_table_lookup(netmon->comment_table, GUINT_TO_POINTER(netmon->frame_table[netmon->current_frame-1]));
1386 if (comment_rec != NULL) {
1387 union wtap_pseudo_header temp_header;
1389 /* These are the current encapsulation types that NetMon uses.
1390 * Save them off so they can be copied to the NetMon pseudoheader
1392 switch (rec->rec_header.packet_header.pkt_encap)
1394 case WTAP_ENCAP_ATM_PDUS:
1395 memcpy(&temp_header.atm, &rec->rec_header.packet_header.pseudo_header.atm, sizeof(temp_header.atm));
1396 break;
1397 case WTAP_ENCAP_ETHERNET:
1398 memcpy(&temp_header.eth, &rec->rec_header.packet_header.pseudo_header.eth, sizeof(temp_header.eth));
1399 break;
1400 case WTAP_ENCAP_IEEE_802_11_NETMON:
1401 memcpy(&temp_header.ieee_802_11, &rec->rec_header.packet_header.pseudo_header.ieee_802_11, sizeof(temp_header.ieee_802_11));
1402 break;
1404 memset(&rec->rec_header.packet_header.pseudo_header.netmon, 0, sizeof(rec->rec_header.packet_header.pseudo_header.netmon));
1406 /* Save the current encapsulation type to the NetMon pseudoheader */
1407 rec->rec_header.packet_header.pseudo_header.netmon.sub_encap = rec->rec_header.packet_header.pkt_encap;
1409 /* Copy the comment data */
1410 rec->rec_header.packet_header.pseudo_header.netmon.title = comment_rec->title;
1411 rec->rec_header.packet_header.pseudo_header.netmon.descLength = comment_rec->descLength;
1412 rec->rec_header.packet_header.pseudo_header.netmon.description = comment_rec->description;
1414 /* Copy the saved pseudoheaders to the netmon pseudoheader structure */
1415 switch (rec->rec_header.packet_header.pkt_encap)
1417 case WTAP_ENCAP_ATM_PDUS:
1418 memcpy(&rec->rec_header.packet_header.pseudo_header.netmon.subheader.atm, &temp_header.atm, sizeof(temp_header.atm));
1419 break;
1420 case WTAP_ENCAP_ETHERNET:
1421 memcpy(&rec->rec_header.packet_header.pseudo_header.netmon.subheader.eth, &temp_header.eth, sizeof(temp_header.eth));
1422 break;
1423 case WTAP_ENCAP_IEEE_802_11_NETMON:
1424 memcpy(&rec->rec_header.packet_header.pseudo_header.netmon.subheader.ieee_802_11, &temp_header.ieee_802_11, sizeof(temp_header.ieee_802_11));
1425 break;
1428 /* Encapsulation type is now something that can be passed to netmon_header dissector */
1429 rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETMON_HEADER;
1432 return SUCCESS;
1435 /* Read the next packet */
1436 static bool netmon_read(wtap *wth, wtap_rec *rec, Buffer *buf,
1437 int *err, char **err_info, int64_t *data_offset)
1439 netmon_t *netmon = (netmon_t *)wth->priv;
1440 int64_t rec_offset;
1442 for (;;) {
1443 /* Have we reached the end of the packet data? */
1444 if (netmon->current_frame >= netmon->frame_table_size) {
1445 *err = 0; /* it's just an EOF, not an error */
1446 return false;
1449 /* Seek to the beginning of the current record, if we're
1450 not there already (seeking to the current position
1451 may still cause a seek and a read of the underlying file,
1452 so we don't want to do it unconditionally).
1454 Yes, the current record could be before the previous
1455 record. At least some captures put the trailer record
1456 with statistics as the first physical record in the
1457 file, but set the frame table up so it's the last
1458 record in sequence. */
1459 rec_offset = netmon->frame_table[netmon->current_frame];
1460 if (file_tell(wth->fh) != rec_offset) {
1461 if (file_seek(wth->fh, rec_offset, SEEK_SET, err) == -1)
1462 return false;
1464 netmon->current_frame++;
1466 *data_offset = file_tell(wth->fh);
1468 switch (netmon_process_record(wth, wth->fh, rec, buf, err,
1469 err_info)) {
1471 case RETRY:
1472 continue;
1474 case SUCCESS:
1475 return true;
1477 case FAILURE:
1478 return false;
1483 static bool
1484 netmon_seek_read(wtap *wth, int64_t seek_off,
1485 wtap_rec *rec, Buffer *buf, int *err, char **err_info)
1487 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
1488 return false;
1490 switch (netmon_process_record(wth, wth->random_fh, rec, buf, err,
1491 err_info)) {
1493 default:
1495 * This should not happen.
1497 *err = WTAP_ERR_BAD_FILE;
1498 *err_info = g_strdup("netmon: saw metadata in netmon_seek_read");
1499 return false;
1501 case SUCCESS:
1502 return true;
1504 case FAILURE:
1505 return false;
1509 static bool
1510 netmon_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
1511 int *err, char **err_info)
1513 struct netmon_atm_hdr atm_phdr;
1514 uint16_t vpi, vci;
1516 if (!wtap_read_bytes(fh, &atm_phdr, sizeof (struct netmon_atm_hdr),
1517 err, err_info))
1518 return false;
1520 vpi = g_ntohs(atm_phdr.vpi);
1521 vci = g_ntohs(atm_phdr.vci);
1523 pseudo_header->atm.vpi = vpi;
1524 pseudo_header->atm.vci = vci;
1526 /* We don't have this information */
1527 pseudo_header->atm.flags = 0;
1528 pseudo_header->atm.channel = 0;
1529 pseudo_header->atm.cells = 0;
1530 pseudo_header->atm.aal5t_u2u = 0;
1531 pseudo_header->atm.aal5t_len = 0;
1532 pseudo_header->atm.aal5t_chksum = 0;
1534 return true;
1537 /* Throw away the frame table used by the sequential I/O stream. */
1538 static void
1539 netmon_close(wtap *wth)
1541 netmon_t *netmon = (netmon_t *)wth->priv;
1543 if (netmon->frame_table != NULL) {
1544 g_free(netmon->frame_table);
1545 netmon->frame_table = NULL;
1548 if (netmon->comment_table != NULL) {
1549 g_hash_table_destroy(netmon->comment_table);
1550 netmon->comment_table = NULL;
1553 if (netmon->process_info_table != NULL) {
1554 g_hash_table_destroy(netmon->process_info_table);
1555 netmon->process_info_table = NULL;
1559 typedef struct {
1560 bool is_v2;
1561 bool got_first_record_time;
1562 nstime_t first_record_time;
1563 uint32_t frame_table_offset;
1564 uint32_t *frame_table;
1565 unsigned frame_table_index;
1566 unsigned frame_table_size;
1567 bool no_more_room; /* true if no more records can be written */
1568 } netmon_dump_t;
1570 static const int wtap_encap[] = {
1571 -1, /* WTAP_ENCAP_UNKNOWN -> unsupported */
1572 1, /* WTAP_ENCAP_ETHERNET -> NDIS Ethernet */
1573 2, /* WTAP_ENCAP_TOKEN_RING -> NDIS Token Ring */
1574 -1, /* WTAP_ENCAP_SLIP -> unsupported */
1575 -1, /* WTAP_ENCAP_PPP -> unsupported */
1576 3, /* WTAP_ENCAP_FDDI -> NDIS FDDI */
1577 3, /* WTAP_ENCAP_FDDI_BITSWAPPED -> NDIS FDDI */
1578 -1, /* WTAP_ENCAP_RAW_IP -> unsupported */
1579 -1, /* WTAP_ENCAP_ARCNET -> unsupported */
1580 -1, /* WTAP_ENCAP_ARCNET_LINUX -> unsupported */
1581 -1, /* WTAP_ENCAP_ATM_RFC1483 -> unsupported */
1582 -1, /* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */
1583 -1, /* WTAP_ENCAP_LAPB -> unsupported*/
1584 4, /* WTAP_ENCAP_ATM_PDUS -> NDIS WAN (*NOT* ATM!) */
1586 #define NUM_WTAP_ENCAPS array_length(wtap_encap)
1588 /* Returns 0 if we could write the specified encapsulation type,
1589 an error indication otherwise. */
1590 static int netmon_dump_can_write_encap_1_x(int encap)
1593 * Per-packet encapsulations are *not* supported in NetMon 1.x
1594 * format.
1596 if (encap < 0 || (unsigned) encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
1597 return WTAP_ERR_UNWRITABLE_ENCAP;
1599 return 0;
1602 static int netmon_dump_can_write_encap_2_x(int encap)
1605 * Per-packet encapsulations are supported in NetMon 2.1
1606 * format.
1608 if (encap == WTAP_ENCAP_PER_PACKET)
1609 return 0;
1611 if (encap < 0 || (unsigned) encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
1612 return WTAP_ERR_UNWRITABLE_ENCAP;
1614 return 0;
1617 /* Returns true on success, false on failure; sets "*err" to an error code on
1618 failure */
1619 static bool netmon_dump_open(wtap_dumper *wdh, bool is_v2,
1620 int *err, char **err_info _U_)
1622 netmon_dump_t *netmon;
1624 /* We can't fill in all the fields in the file header, as we
1625 haven't yet written any packets. As we'll have to rewrite
1626 the header when we've written out all the packets, we just
1627 skip over the header for now. */
1628 if (wtap_dump_file_seek(wdh, CAPTUREFILE_HEADER_SIZE, SEEK_SET, err) == -1)
1629 return false;
1631 wdh->bytes_dumped = CAPTUREFILE_HEADER_SIZE;
1632 wdh->subtype_write = netmon_dump;
1633 wdh->subtype_finish = netmon_dump_finish;
1635 netmon = g_new(netmon_dump_t, 1);
1636 wdh->priv = (void *)netmon;
1637 netmon->is_v2 = is_v2;
1638 netmon->frame_table_offset = CAPTUREFILE_HEADER_SIZE;
1639 netmon->got_first_record_time = false;
1640 netmon->frame_table = NULL;
1641 netmon->frame_table_index = 0;
1642 netmon->frame_table_size = 0;
1643 netmon->no_more_room = false;
1645 return true;
1648 static bool netmon_dump_open_1_x(wtap_dumper *wdh, int *err, char **err_info _U_)
1650 return netmon_dump_open(wdh, false, err, err_info);
1653 static bool netmon_dump_open_2_x(wtap_dumper *wdh, int *err, char **err_info _U_)
1655 return netmon_dump_open(wdh, true, err, err_info);
1658 /* Write a record for a packet to a dump file.
1659 Returns true on success, false on failure. */
1660 static bool netmon_dump(wtap_dumper *wdh, const wtap_rec *rec,
1661 const uint8_t *pd, int *err, char **err_info _U_)
1663 const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
1664 netmon_dump_t *netmon = (netmon_dump_t *)wdh->priv;
1665 struct netmonrec_1_x_hdr rec_1_x_hdr;
1666 struct netmonrec_2_x_hdr rec_2_x_hdr;
1667 void *hdrp;
1668 size_t rec_size;
1669 struct netmonrec_2_1_trlr rec_2_x_trlr;
1670 size_t hdr_size;
1671 struct netmon_atm_hdr atm_hdr;
1672 int atm_hdrsize;
1673 int64_t secs;
1674 int32_t nsecs;
1676 /* We can only write packet records. */
1677 if (rec->rec_type != REC_TYPE_PACKET) {
1678 *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
1679 return false;
1682 if (netmon->is_v2) {
1683 /* Don't write anything we're not willing to read. */
1684 if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
1685 *err = WTAP_ERR_PACKET_TOO_LARGE;
1686 return false;
1688 } else {
1690 * Make sure this packet doesn't have a link-layer type that
1691 * differs from the one for the file.
1693 if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
1694 *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
1695 return false;
1699 * The length fields are 16-bit, so there's a hard limit
1700 * of 65535.
1702 if (rec->rec_header.packet_header.caplen > 65535) {
1703 *err = WTAP_ERR_PACKET_TOO_LARGE;
1704 return false;
1708 if (wdh->file_encap == WTAP_ENCAP_PER_PACKET) {
1710 * Is this network type supported?
1712 if (rec->rec_header.packet_header.pkt_encap < 0 ||
1713 (unsigned) rec->rec_header.packet_header.pkt_encap >= NUM_WTAP_ENCAPS ||
1714 wtap_encap[rec->rec_header.packet_header.pkt_encap] == -1) {
1716 * No. Fail.
1718 *err = WTAP_ERR_UNWRITABLE_ENCAP;
1719 return false;
1723 * Fill in the trailer with the network type.
1725 phtoles(rec_2_x_trlr.network, wtap_encap[rec->rec_header.packet_header.pkt_encap]);
1729 * Will the file offset of this frame fit in a 32-bit unsigned
1730 * integer?
1732 if (netmon->no_more_room) {
1734 * No, so the file is too big for NetMon format to
1735 * handle.
1737 *err = EFBIG;
1738 return false;
1742 * NetMon files have a capture start time in the file header,
1743 * and have times relative to that in the packet headers;
1744 * pick the time of the first packet as the capture start
1745 * time.
1747 * That time has millisecond resolution, so chop any
1748 * sub-millisecond part of the time stamp off.
1750 if (!netmon->got_first_record_time) {
1751 netmon->first_record_time.secs = rec->ts.secs;
1752 netmon->first_record_time.nsecs =
1753 (rec->ts.nsecs/1000000)*1000000;
1754 netmon->got_first_record_time = true;
1757 if (wdh->file_encap == WTAP_ENCAP_ATM_PDUS)
1758 atm_hdrsize = sizeof (struct netmon_atm_hdr);
1759 else
1760 atm_hdrsize = 0;
1761 secs = (int64_t)(rec->ts.secs - netmon->first_record_time.secs);
1762 nsecs = rec->ts.nsecs - netmon->first_record_time.nsecs;
1763 while (nsecs < 0) {
1765 * Propagate a borrow into the seconds.
1766 * The seconds is a time_t, and can be < 0
1767 * (unlikely, as neither UN*X nor DOS
1768 * nor the original Mac System existed
1769 * before January 1, 1970, 00:00:00 UTC),
1770 * while the nanoseconds should be positive,
1771 * as in "nanoseconds since the instant of time
1772 * represented by the seconds".
1774 * We do not want t to be negative, as, according
1775 * to the C90 standard, "if either operand [of /
1776 * or %] is negative, whether the result of the
1777 * / operator is the largest integer less than or
1778 * equal to the algebraic quotient or the smallest
1779 * greater than or equal to the algebraic quotient
1780 * is implementation-defined, as is the sign of
1781 * the result of the % operator", and we want
1782 * the result of the division and remainder
1783 * operations to be the same on all platforms.
1785 nsecs += 1000000000;
1786 secs--;
1788 if (netmon->is_v2) {
1789 rec_2_x_hdr.ts_delta = GUINT64_TO_LE(secs*1000000 + (nsecs + 500)/1000);
1790 rec_2_x_hdr.orig_len = GUINT32_TO_LE(rec->rec_header.packet_header.len + atm_hdrsize);
1791 rec_2_x_hdr.incl_len = GUINT32_TO_LE(rec->rec_header.packet_header.caplen + atm_hdrsize);
1792 hdrp = &rec_2_x_hdr;
1793 hdr_size = sizeof rec_2_x_hdr;
1794 } else {
1795 rec_1_x_hdr.ts_delta = GUINT32_TO_LE(secs*1000 + (nsecs + 500000)/1000000);
1796 rec_1_x_hdr.orig_len = GUINT16_TO_LE(rec->rec_header.packet_header.len + atm_hdrsize);
1797 rec_1_x_hdr.incl_len = GUINT16_TO_LE(rec->rec_header.packet_header.caplen + atm_hdrsize);
1798 hdrp = &rec_1_x_hdr;
1799 hdr_size = sizeof rec_1_x_hdr;
1803 * Keep track of the record size, as we need to update
1804 * the current file offset.
1806 rec_size = 0;
1808 if (!wtap_dump_file_write(wdh, hdrp, hdr_size, err))
1809 return false;
1810 rec_size += hdr_size;
1812 if (wdh->file_encap == WTAP_ENCAP_ATM_PDUS) {
1814 * Write the ATM header.
1815 * We supply all-zero destination and source addresses.
1817 memset(&atm_hdr.dest, 0, sizeof atm_hdr.dest);
1818 memset(&atm_hdr.src, 0, sizeof atm_hdr.src);
1819 atm_hdr.vpi = g_htons(pseudo_header->atm.vpi);
1820 atm_hdr.vci = g_htons(pseudo_header->atm.vci);
1821 if (!wtap_dump_file_write(wdh, &atm_hdr, sizeof atm_hdr, err))
1822 return false;
1823 rec_size += sizeof atm_hdr;
1826 if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
1827 return false;
1828 rec_size += rec->rec_header.packet_header.caplen;
1830 if (wdh->file_encap == WTAP_ENCAP_PER_PACKET) {
1832 * Write out the trailer.
1834 if (!wtap_dump_file_write(wdh, &rec_2_x_trlr,
1835 sizeof rec_2_x_trlr, err))
1836 return false;
1837 rec_size += sizeof rec_2_x_trlr;
1841 * Stash the file offset of this frame.
1843 if (netmon->frame_table_size == 0) {
1845 * Haven't yet allocated the buffer for the frame table.
1847 netmon->frame_table = (uint32_t *)g_malloc(1024 * sizeof *netmon->frame_table);
1848 netmon->frame_table_size = 1024;
1849 } else {
1851 * We've allocated it; are we at the end?
1853 if (netmon->frame_table_index >= netmon->frame_table_size) {
1855 * Yes - double the size of the frame table.
1857 netmon->frame_table_size *= 2;
1858 netmon->frame_table = (uint32_t *)g_realloc(netmon->frame_table,
1859 netmon->frame_table_size * sizeof *netmon->frame_table);
1863 netmon->frame_table[netmon->frame_table_index] =
1864 GUINT32_TO_LE(netmon->frame_table_offset);
1867 * Is this the last record we can write?
1868 * I.e., will the frame table offset of the next record not fit
1869 * in a 32-bit frame table offset entry?
1871 * (We don't bother checking whether the number of frames
1872 * will fit in a 32-bit value, as, even if each record were
1873 * 1 byte, if there were more than 2^32-1 packets, the frame
1874 * table offset of at least one of those packets will be >
1875 * 2^32 - 1.)
1877 * Note: this also catches the unlikely possibility that
1878 * the record itself is > 2^32 - 1 bytes long.
1880 if ((uint64_t)netmon->frame_table_offset + rec_size > UINT32_MAX) {
1882 * Yup, too big.
1884 netmon->no_more_room = true;
1886 netmon->frame_table_index++;
1887 netmon->frame_table_offset += (uint32_t) rec_size;
1889 return true;
1892 /* Finish writing to a dump file.
1893 Returns true on success, false on failure. */
1894 static bool netmon_dump_finish(wtap_dumper *wdh, int *err,
1895 char **err_info _U_)
1897 netmon_dump_t *netmon = (netmon_dump_t *)wdh->priv;
1898 size_t n_to_write;
1899 struct netmon_hdr file_hdr;
1900 const char *magicp;
1901 size_t magic_size;
1902 struct tm *tm;
1903 int64_t saved_bytes_dumped;
1905 /* Write out the frame table. "netmon->frame_table_index" is
1906 the number of entries we've put into it. */
1907 n_to_write = netmon->frame_table_index * sizeof *netmon->frame_table;
1908 if (!wtap_dump_file_write(wdh, netmon->frame_table, n_to_write, err))
1909 return false;
1911 /* Now go fix up the file header. */
1912 if (wtap_dump_file_seek(wdh, 0, SEEK_SET, err) == -1)
1913 return false;
1914 /* Save bytes_dumped since following calls to wtap_dump_file_write()
1915 * will still (mistakenly) increase it.
1917 saved_bytes_dumped = wdh->bytes_dumped;
1918 memset(&file_hdr, '\0', sizeof file_hdr);
1919 if (netmon->is_v2) {
1920 magicp = netmon_2_x_magic;
1921 magic_size = sizeof netmon_2_x_magic;
1923 * NetMon file version, for 2.x, is 2.0;
1924 * for 3.0, it's 2.1.
1926 * If the file encapsulation is WTAP_ENCAP_PER_PACKET,
1927 * we need version 2.1.
1929 * XXX - version 2.3 supports UTC time stamps; when
1930 * should we use it? According to the file format
1931 * documentation, NetMon 3.3 "cannot properly
1932 * interpret" the UTC timestamp information; does
1933 * that mean it ignores it and uses the local-time
1934 * start time and time deltas, or mishandles them?
1935 * Also, NetMon 3.1 and earlier can't read version
1936 * 2.2, much less version 2.3.
1938 file_hdr.ver_major = 2;
1939 file_hdr.ver_minor =
1940 (wdh->file_encap == WTAP_ENCAP_PER_PACKET) ? 1 : 0;
1941 } else {
1942 magicp = netmon_1_x_magic;
1943 magic_size = sizeof netmon_1_x_magic;
1944 /* NetMon file version, for 1.x, is 1.1 */
1945 file_hdr.ver_major = 1;
1946 file_hdr.ver_minor = 1;
1948 if (!wtap_dump_file_write(wdh, magicp, magic_size, err))
1949 return false;
1951 if (wdh->file_encap == WTAP_ENCAP_PER_PACKET) {
1953 * We're writing NetMon 2.1 format, so the media
1954 * type in the file header is irrelevant. Set it
1955 * to 1, just as Network Monitor does.
1957 file_hdr.network = GUINT16_TO_LE(1);
1958 } else
1959 file_hdr.network = GUINT16_TO_LE(wtap_encap[wdh->file_encap]);
1960 tm = localtime(&netmon->first_record_time.secs);
1961 if (tm != NULL) {
1962 file_hdr.ts_year = GUINT16_TO_LE(1900 + tm->tm_year);
1963 file_hdr.ts_month = GUINT16_TO_LE(tm->tm_mon + 1);
1964 file_hdr.ts_dow = GUINT16_TO_LE(tm->tm_wday);
1965 file_hdr.ts_day = GUINT16_TO_LE(tm->tm_mday);
1966 file_hdr.ts_hour = GUINT16_TO_LE(tm->tm_hour);
1967 file_hdr.ts_min = GUINT16_TO_LE(tm->tm_min);
1968 file_hdr.ts_sec = GUINT16_TO_LE(tm->tm_sec);
1969 } else {
1970 file_hdr.ts_year = GUINT16_TO_LE(1900 + 0);
1971 file_hdr.ts_month = GUINT16_TO_LE(0 + 1);
1972 file_hdr.ts_dow = GUINT16_TO_LE(0);
1973 file_hdr.ts_day = GUINT16_TO_LE(0);
1974 file_hdr.ts_hour = GUINT16_TO_LE(0);
1975 file_hdr.ts_min = GUINT16_TO_LE(0);
1976 file_hdr.ts_sec = GUINT16_TO_LE(0);
1978 file_hdr.ts_msec = GUINT16_TO_LE(netmon->first_record_time.nsecs/1000000);
1979 file_hdr.frametableoffset = GUINT32_TO_LE(netmon->frame_table_offset);
1980 file_hdr.frametablelength =
1981 GUINT32_TO_LE(netmon->frame_table_index * sizeof *netmon->frame_table);
1982 if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
1983 return false;
1985 wdh->bytes_dumped = saved_bytes_dumped;
1986 return true;
1989 static const struct supported_block_type netmon_1_x_blocks_supported[] = {
1991 * We support packet blocks, with no comments or other options.
1993 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
1996 static const struct file_type_subtype_info netmon_1_x_info = {
1997 "Microsoft NetMon 1.x", "netmon1", "cap", NULL,
1998 true, BLOCKS_SUPPORTED(netmon_1_x_blocks_supported),
1999 netmon_dump_can_write_encap_1_x, netmon_dump_open_1_x, NULL
2002 static const struct supported_block_type netmon_2_x_blocks_supported[] = {
2004 * We support packet blocks, with no comments or other options.
2006 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
2009 static const struct file_type_subtype_info netmon_2_x_info = {
2010 "Microsoft NetMon 2.x", "netmon2", "cap", NULL,
2011 true, BLOCKS_SUPPORTED(netmon_2_x_blocks_supported),
2012 netmon_dump_can_write_encap_2_x, netmon_dump_open_2_x, NULL
2015 void register_netmon(void)
2017 netmon_1_x_file_type_subtype = wtap_register_file_type_subtype(&netmon_1_x_info);
2018 netmon_2_x_file_type_subtype = wtap_register_file_type_subtype(&netmon_2_x_info);
2021 * Register names for backwards compatibility with the
2022 * wtap_filetypes table in Lua.
2024 wtap_register_backwards_compatibility_lua_name("NETMON_1_x",
2025 netmon_1_x_file_type_subtype);
2026 wtap_register_backwards_compatibility_lua_name("NETMON_2_x",
2027 netmon_2_x_file_type_subtype);
2031 * Editor modelines - https://www.wireshark.org/tools/modelines.html
2033 * Local variables:
2034 * c-basic-offset: 8
2035 * tab-width: 8
2036 * indent-tabs-mode: t
2037 * End:
2039 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
2040 * :indentSize=8:tabSize=8:noTabs=false: