/src/wireshark/wiretap/netmon.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* netmon.c |
2 | | * |
3 | | * Wiretap Library |
4 | | * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu> |
5 | | * |
6 | | * SPDX-License-Identifier: GPL-2.0-or-later |
7 | | */ |
8 | | |
9 | | #include "config.h" |
10 | | #include "netmon.h" |
11 | | |
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" |
19 | | |
20 | | /* The file at |
21 | | * |
22 | | * ftp://ftp.microsoft.com/developr/drg/cifs/cifs/Bhfile.zip |
23 | | * |
24 | | * contains "STRUCT.H", which declares the typedef CAPTUREFILE_HEADER |
25 | | * for the header of a Microsoft Network Monitor 1.x capture file. |
26 | | * |
27 | | * The help files for Network Monitor 3.x document the 2.x file format. |
28 | | */ |
29 | | |
30 | | /* Capture file header, *including* magic number, is padded to 128 bytes. */ |
31 | 0 | #define CAPTUREFILE_HEADER_SIZE 128 |
32 | | |
33 | | /* Magic number size, for both 1.x and 2.x. */ |
34 | 0 | #define MAGIC_SIZE 4 |
35 | | |
36 | | /* Magic number in Network Monitor 1.x files. */ |
37 | | static const char netmon_1_x_magic[MAGIC_SIZE] = { |
38 | | 'R', 'T', 'S', 'S' |
39 | | }; |
40 | | |
41 | | /* Magic number in Network Monitor 2.x files. */ |
42 | | static const char netmon_2_x_magic[MAGIC_SIZE] = { |
43 | | 'G', 'M', 'B', 'U' |
44 | | }; |
45 | | |
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 */ |
69 | | }; |
70 | | |
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 */ |
77 | | }; |
78 | | |
79 | | /* |
80 | | * Network Monitor 2.x record header, as documented in NetMon 3.x's |
81 | | * help files. |
82 | | */ |
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 */ |
87 | | }; |
88 | | |
89 | | /* |
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. |
93 | | * |
94 | | * Some fields are multi-byte integers, but they're not aligned on their |
95 | | * natural boundaries. |
96 | | */ |
97 | | struct netmonrec_2_1_trlr { |
98 | | uint8_t network[2]; /* network type for this packet */ |
99 | | }; |
100 | | |
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 */ |
104 | | }; |
105 | | |
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 */ |
111 | | }; |
112 | | |
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 */ |
119 | | }; |
120 | | |
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; |
126 | | }; |
127 | | |
128 | | union ip_address { |
129 | | uint32_t ipv4; |
130 | | ws_in6_addr ipv6; |
131 | | }; |
132 | | |
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; |
143 | | }; |
144 | | |
145 | | /* |
146 | | * The link-layer header on ATM packets. |
147 | | */ |
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 */ |
153 | | }; |
154 | | |
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; |
166 | | |
167 | | /* |
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. |
171 | | * |
172 | | * See |
173 | | * |
174 | | * https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation |
175 | | * |
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. |
179 | | * |
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. |
182 | | */ |
183 | 0 | #define MATH_PROCINFO_PATH_SIZE 65536 |
184 | | |
185 | | /* |
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? |
190 | | */ |
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 */ |
205 | | }; |
206 | 0 | #define NUM_NETMON_ENCAPS array_length(netmon_encap) |
207 | | |
208 | | /* |
209 | | * Special link-layer types. |
210 | | */ |
211 | 0 | #define NETMON_NET_PCAP_BASE 0xE000 |
212 | 0 | #define NETMON_NET_NETEVENT 0xFFE0 |
213 | 0 | #define NETMON_NET_NETWORK_INFO_EX 0xFFFB |
214 | 0 | #define NETMON_NET_PAYLOAD_HEADER 0xFFFC |
215 | 0 | #define NETMON_NET_NETWORK_INFO 0xFFFD |
216 | 0 | #define NETMON_NET_DNS_CACHE 0xFFFE |
217 | 0 | #define NETMON_NET_NETMON_FILTER 0xFFFF |
218 | | |
219 | | static bool netmon_read(wtap *wth, wtap_rec *rec, |
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, 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); |
230 | | |
231 | | static int netmon_1_x_file_type_subtype = -1; |
232 | | static int netmon_2_x_file_type_subtype = -1; |
233 | | |
234 | | void register_netmon(void); |
235 | | |
236 | | /* |
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. |
240 | | */ |
241 | | static uint8_t * |
242 | | utf_16_to_utf_8(const uint8_t *in, uint32_t length) |
243 | 0 | { |
244 | 0 | uint8_t *result, *out; |
245 | 0 | gunichar2 uchar2; |
246 | 0 | gunichar uchar; |
247 | 0 | size_t n_bytes; |
248 | 0 | uint32_t i; |
249 | | |
250 | | /* |
251 | | * Get the length of the resulting UTF-8 string, and validate |
252 | | * the input string in the process. |
253 | | */ |
254 | 0 | n_bytes = 0; |
255 | 0 | for (i = 0; i + 1 < length && (uchar2 = pletoh16(in + i)) != '\0'; |
256 | 0 | i += 2) { |
257 | 0 | if (IS_LEAD_SURROGATE(uchar2)) { |
258 | | /* |
259 | | * Lead surrogate. Must be followed by a trail |
260 | | * surrogate. |
261 | | */ |
262 | 0 | gunichar2 lead_surrogate; |
263 | |
|
264 | 0 | i += 2; |
265 | 0 | if (i + 1 >= length) { |
266 | | /* |
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? |
271 | | */ |
272 | 0 | break; |
273 | 0 | } |
274 | 0 | lead_surrogate = uchar2; |
275 | 0 | uchar2 = pletoh16(in + i); |
276 | 0 | if (uchar2 == '\0') { |
277 | | /* |
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? |
282 | | */ |
283 | 0 | break; |
284 | 0 | } |
285 | 0 | if (IS_TRAIL_SURROGATE(uchar2)) { |
286 | | /* Trail surrogate. */ |
287 | 0 | uchar = SURROGATE_VALUE(lead_surrogate, uchar2); |
288 | 0 | n_bytes += g_unichar_to_utf8(uchar, NULL); |
289 | 0 | } else { |
290 | | /* |
291 | | * Not a trail surrogate. |
292 | | * Ignore the entire pair. |
293 | | * XXX - insert "substitute" character? |
294 | | * Report the error in some other fashion? |
295 | | */ |
296 | 0 | ; |
297 | 0 | } |
298 | 0 | } else { |
299 | 0 | if (IS_TRAIL_SURROGATE(uchar2)) { |
300 | | /* |
301 | | * Trail surrogate without a preceding |
302 | | * lead surrogate. Ignore it. |
303 | | * XXX - insert "substitute" character? |
304 | | * Report the error in some other fashion? |
305 | | */ |
306 | 0 | ; |
307 | 0 | } else { |
308 | | /* |
309 | | * Non-surrogate; just count it. |
310 | | */ |
311 | 0 | n_bytes += g_unichar_to_utf8(uchar2, NULL); |
312 | 0 | } |
313 | 0 | } |
314 | 0 | } |
315 | | |
316 | | /* |
317 | | * Now allocate a buffer big enough for the UTF-8 string plus a |
318 | | * trailing NUL, and generate the string. |
319 | | */ |
320 | 0 | result = (uint8_t *)g_malloc(n_bytes + 1); |
321 | |
|
322 | 0 | out = result; |
323 | 0 | for (i = 0; i + 1 < length && (uchar2 = pletoh16(in + i)) != '\0'; |
324 | 0 | i += 2) { |
325 | 0 | if (IS_LEAD_SURROGATE(uchar2)) { |
326 | | /* |
327 | | * Lead surrogate. Must be followed by a trail |
328 | | * surrogate. |
329 | | */ |
330 | 0 | gunichar2 lead_surrogate; |
331 | |
|
332 | 0 | i += 2; |
333 | 0 | if (i + 1 >= length) { |
334 | | /* |
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? |
339 | | */ |
340 | 0 | break; |
341 | 0 | } |
342 | 0 | lead_surrogate = uchar2; |
343 | 0 | uchar2 = pletoh16(in + i); |
344 | 0 | if (uchar2 == '\0') { |
345 | | /* |
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? |
350 | | */ |
351 | 0 | break; |
352 | 0 | } |
353 | 0 | if (IS_TRAIL_SURROGATE(uchar2)) { |
354 | | /* Trail surrogate. */ |
355 | 0 | uchar = SURROGATE_VALUE(lead_surrogate, uchar2); |
356 | 0 | out += g_unichar_to_utf8(uchar, out); |
357 | 0 | } else { |
358 | | /* |
359 | | * Not a trail surrogate. |
360 | | * Ignore the entire pair. |
361 | | * XXX - insert "substitute" character? |
362 | | * Report the error in some other fashion? |
363 | | */ |
364 | 0 | ; |
365 | 0 | } |
366 | 0 | } else { |
367 | 0 | if (IS_TRAIL_SURROGATE(uchar2)) { |
368 | | /* |
369 | | * Trail surrogate without a preceding |
370 | | * lead surrogate. Ignore it. |
371 | | * XXX - insert "substitute" character? |
372 | | * Report the error in some other fashion? |
373 | | */ |
374 | 0 | ; |
375 | 0 | } else { |
376 | | /* |
377 | | * Non-surrogate; just count it. |
378 | | */ |
379 | 0 | out += g_unichar_to_utf8(uchar2, out); |
380 | 0 | } |
381 | 0 | } |
382 | 0 | } |
383 | 0 | *out = '\0'; |
384 | | |
385 | | /* |
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. |
388 | | */ |
389 | 0 | return result; |
390 | 0 | } |
391 | | |
392 | | |
393 | 0 | static void netmonrec_comment_destroy(void *key) { |
394 | 0 | struct netmonrec_comment *comment = (struct netmonrec_comment*) key; |
395 | |
|
396 | 0 | g_free(comment->title); |
397 | 0 | g_free(comment->description); |
398 | 0 | g_free(comment); |
399 | 0 | } |
400 | | |
401 | 0 | static void netmonrec_process_info_destroy(void *key) { |
402 | 0 | struct netmonrec_process_info *process_info = (struct netmonrec_process_info*) key; |
403 | |
|
404 | 0 | g_free(process_info->path); |
405 | 0 | g_free(process_info->iconData); |
406 | 0 | g_free(process_info); |
407 | 0 | } |
408 | | |
409 | | wtap_open_return_val netmon_open(wtap *wth, int *err, char **err_info) |
410 | 0 | { |
411 | 0 | char magic[MAGIC_SIZE]; |
412 | 0 | struct netmon_hdr hdr; |
413 | 0 | int file_type; |
414 | 0 | struct tm tm; |
415 | 0 | uint32_t frame_table_offset; |
416 | 0 | uint32_t frame_table_length; |
417 | 0 | uint32_t frame_table_size; |
418 | 0 | uint32_t *frame_table; |
419 | 0 | uint32_t comment_table_offset, process_info_table_offset; |
420 | 0 | uint32_t comment_table_size, process_info_table_count; |
421 | 0 | GHashTable *comment_table, *process_info_table; |
422 | 0 | struct netmonrec_comment* comment_rec; |
423 | 0 | int64_t file_size = wtap_file_size(wth, err); |
424 | | #if G_BYTE_ORDER == G_BIG_ENDIAN |
425 | | unsigned int i; |
426 | | #endif |
427 | 0 | netmon_t *netmon; |
428 | | |
429 | | /* Read in the string that should be at the start of a Network |
430 | | * Monitor file */ |
431 | 0 | if (!wtap_read_bytes(wth->fh, magic, MAGIC_SIZE, err, err_info)) { |
432 | 0 | if (*err != WTAP_ERR_SHORT_READ) |
433 | 0 | return WTAP_OPEN_ERROR; |
434 | 0 | return WTAP_OPEN_NOT_MINE; |
435 | 0 | } |
436 | | |
437 | 0 | if (memcmp(magic, netmon_1_x_magic, MAGIC_SIZE) != 0 && |
438 | 0 | memcmp(magic, netmon_2_x_magic, MAGIC_SIZE) != 0) { |
439 | 0 | return WTAP_OPEN_NOT_MINE; |
440 | 0 | } |
441 | | |
442 | | /* Read the rest of the header. */ |
443 | 0 | if (!wtap_read_bytes(wth->fh, &hdr, sizeof hdr, err, err_info)) |
444 | 0 | return WTAP_OPEN_ERROR; |
445 | | |
446 | 0 | switch (hdr.ver_major) { |
447 | | |
448 | 0 | case 1: |
449 | 0 | file_type = netmon_1_x_file_type_subtype; |
450 | 0 | break; |
451 | | |
452 | 0 | case 2: |
453 | 0 | file_type = netmon_2_x_file_type_subtype; |
454 | 0 | break; |
455 | | |
456 | 0 | default: |
457 | 0 | *err = WTAP_ERR_UNSUPPORTED; |
458 | 0 | *err_info = ws_strdup_printf("netmon: major version %u unsupported", hdr.ver_major); |
459 | 0 | return WTAP_OPEN_ERROR; |
460 | 0 | } |
461 | | |
462 | 0 | hdr.network = pletoh16(&hdr.network); |
463 | 0 | if (hdr.network >= NUM_NETMON_ENCAPS |
464 | 0 | || netmon_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) { |
465 | 0 | *err = WTAP_ERR_UNSUPPORTED; |
466 | 0 | *err_info = ws_strdup_printf("netmon: network type %u unknown or unsupported", |
467 | 0 | hdr.network); |
468 | 0 | return WTAP_OPEN_ERROR; |
469 | 0 | } |
470 | | |
471 | | /* This is a netmon file */ |
472 | 0 | wth->file_type_subtype = file_type; |
473 | 0 | netmon = g_new0(netmon_t, 1); |
474 | 0 | wth->priv = (void *)netmon; |
475 | 0 | wth->subtype_read = netmon_read; |
476 | 0 | wth->subtype_seek_read = netmon_seek_read; |
477 | 0 | wth->subtype_close = netmon_close; |
478 | | |
479 | | /* NetMon capture file formats v2.1+ use per-packet encapsulation types. NetMon 3 sets the value in |
480 | | * the header to 1 (Ethernet) for backwards compatibility. */ |
481 | 0 | if((hdr.ver_major == 2 && hdr.ver_minor >= 1) || hdr.ver_major > 2) |
482 | 0 | wth->file_encap = WTAP_ENCAP_PER_PACKET; |
483 | 0 | else |
484 | 0 | wth->file_encap = netmon_encap[hdr.network]; |
485 | |
|
486 | 0 | wth->snapshot_length = 0; /* not available in header */ |
487 | | /* |
488 | | * Convert the time stamp to a "time_t" and a number of |
489 | | * milliseconds. |
490 | | */ |
491 | 0 | tm.tm_year = pletoh16(&hdr.ts_year) - 1900; |
492 | 0 | tm.tm_mon = pletoh16(&hdr.ts_month) - 1; |
493 | 0 | tm.tm_mday = pletoh16(&hdr.ts_day); |
494 | 0 | tm.tm_hour = pletoh16(&hdr.ts_hour); |
495 | 0 | tm.tm_min = pletoh16(&hdr.ts_min); |
496 | 0 | tm.tm_sec = pletoh16(&hdr.ts_sec); |
497 | 0 | tm.tm_isdst = -1; |
498 | 0 | netmon->start_secs = mktime(&tm); |
499 | | /* |
500 | | * XXX - what if "secs" is -1? Unlikely, but if the capture was |
501 | | * done in a time zone that switches between standard and summer |
502 | | * time sometime other than when we do, and thus the time was one |
503 | | * that doesn't exist here because a switch from standard to summer |
504 | | * time zips over it, it could happen. |
505 | | * |
506 | | * On the other hand, if the capture was done in a different time |
507 | | * zone, this won't work right anyway; unfortunately, the time |
508 | | * zone isn't stored in the capture file (why the hell didn't |
509 | | * they stuff a FILETIME, which is the number of 100-nanosecond |
510 | | * intervals since 1601-01-01 00:00:00 "UTC", there, instead |
511 | | * of stuffing a SYSTEMTIME, which is time-zone-dependent, there?). |
512 | | * |
513 | | * Eventually they went with per-packet FILETIMEs in a later |
514 | | * version. |
515 | | */ |
516 | 0 | netmon->start_nsecs = pletoh16(&hdr.ts_msec)*1000000; |
517 | |
|
518 | 0 | netmon->version_major = hdr.ver_major; |
519 | 0 | netmon->version_minor = hdr.ver_minor; |
520 | | |
521 | | /* |
522 | | * Get the offset of the frame index table. |
523 | | */ |
524 | 0 | frame_table_offset = pletoh32(&hdr.frametableoffset); |
525 | | |
526 | | /* |
527 | | * For NetMon 2.2 format and later, get the offset and length of |
528 | | * the comment index table and process info table. |
529 | | * |
530 | | * For earlier versions, set them to zero; they appear to be |
531 | | * uninitialized, so they're not necessarily zero. |
532 | | */ |
533 | 0 | if ((netmon->version_major == 2 && netmon->version_minor >= 2) || |
534 | 0 | netmon->version_major > 2) { |
535 | 0 | comment_table_offset = pletoh32(&hdr.commentdataoffset); |
536 | 0 | comment_table_size = pletoh32(&hdr.commentdatalength); |
537 | 0 | process_info_table_offset = pletoh32(&hdr.processinfooffset); |
538 | 0 | process_info_table_count = pletoh32(&hdr.processinfocount); |
539 | 0 | } else { |
540 | 0 | comment_table_offset = 0; |
541 | 0 | comment_table_size = 0; |
542 | 0 | process_info_table_offset = 0; |
543 | 0 | process_info_table_count = 0; |
544 | 0 | } |
545 | | |
546 | | /* |
547 | | * It appears that some NetMon 2.x files don't have the |
548 | | * first packet starting exactly 128 bytes into the file. |
549 | | * |
550 | | * Furthermore, it also appears that there are "holes" in |
551 | | * the file, i.e. frame N+1 doesn't always follow immediately |
552 | | * after frame N. |
553 | | * |
554 | | * Therefore, we must read the frame table, and use the offsets |
555 | | * in it as the offsets of the frames. |
556 | | */ |
557 | 0 | frame_table_length = pletoh32(&hdr.frametablelength); |
558 | 0 | frame_table_size = frame_table_length / (uint32_t)sizeof (uint32_t); |
559 | 0 | if ((frame_table_size * sizeof (uint32_t)) != frame_table_length) { |
560 | 0 | *err = WTAP_ERR_BAD_FILE; |
561 | 0 | *err_info = ws_strdup_printf("netmon: frame table length is %u, which is not a multiple of the size of an entry", |
562 | 0 | frame_table_length); |
563 | 0 | return WTAP_OPEN_ERROR; |
564 | 0 | } |
565 | 0 | if (frame_table_size == 0) { |
566 | 0 | *err = WTAP_ERR_BAD_FILE; |
567 | 0 | *err_info = ws_strdup_printf("netmon: frame table length is %u, which means it's less than one entry in size", |
568 | 0 | frame_table_length); |
569 | 0 | return WTAP_OPEN_ERROR; |
570 | 0 | } |
571 | | /* |
572 | | * XXX - clamp the size of the frame table, so that we don't |
573 | | * attempt to allocate a huge frame table and fail. |
574 | | * |
575 | | * Given that file offsets in the frame table are 32-bit, |
576 | | * a NetMon file cannot be bigger than 2^32 bytes. |
577 | | * Given that a NetMon 1.x-format packet header is 8 bytes, |
578 | | * that means a NetMon file cannot have more than |
579 | | * 512*2^20 packets. We'll pick that as the limit for |
580 | | * now; it's 1/8th of a 32-bit address space, which is |
581 | | * probably not going to exhaust the address space all by |
582 | | * itself, and probably won't exhaust the backing store. |
583 | | */ |
584 | 0 | if (frame_table_size > 512*1024*1024) { |
585 | 0 | *err = WTAP_ERR_BAD_FILE; |
586 | 0 | *err_info = ws_strdup_printf("netmon: frame table length is %u, which is larger than we support", |
587 | 0 | frame_table_length); |
588 | 0 | return WTAP_OPEN_ERROR; |
589 | 0 | } |
590 | 0 | if (file_seek(wth->fh, frame_table_offset, SEEK_SET, err) == -1) { |
591 | 0 | return WTAP_OPEN_ERROR; |
592 | 0 | } |
593 | | |
594 | | /* |
595 | | * Sanity check the comment table information before we bother to allocate |
596 | | * large chunks of memory for the frame table |
597 | | */ |
598 | 0 | if (comment_table_size > 0) { |
599 | | /* |
600 | | * XXX - clamp the size of the comment table, so that we don't |
601 | | * attempt to allocate a huge comment table and fail. |
602 | | * |
603 | | * Just use same size requires as frame table |
604 | | */ |
605 | 0 | if (comment_table_size > 512*1024*1024) { |
606 | 0 | *err = WTAP_ERR_BAD_FILE; |
607 | 0 | *err_info = ws_strdup_printf("netmon: comment table size is %u, which is larger than we support", |
608 | 0 | comment_table_size); |
609 | 0 | return WTAP_OPEN_ERROR; |
610 | 0 | } |
611 | | |
612 | 0 | if (comment_table_size < 17) { |
613 | 0 | *err = WTAP_ERR_BAD_FILE; |
614 | 0 | *err_info = ws_strdup_printf("netmon: comment table size is %u, which is too small to use", |
615 | 0 | comment_table_size); |
616 | 0 | return WTAP_OPEN_ERROR; |
617 | 0 | } |
618 | | |
619 | 0 | if (comment_table_offset > file_size) { |
620 | 0 | *err = WTAP_ERR_BAD_FILE; |
621 | 0 | *err_info = ws_strdup_printf("netmon: comment table offset (%u) is larger than file", |
622 | 0 | comment_table_offset); |
623 | 0 | return WTAP_OPEN_ERROR; |
624 | 0 | } |
625 | 0 | } |
626 | | |
627 | | /* |
628 | | * Sanity check the process info table information before we bother to allocate |
629 | | * large chunks of memory for the frame table |
630 | | */ |
631 | 0 | if ((process_info_table_offset > 0) && (process_info_table_count > 0)) { |
632 | | /* |
633 | | * XXX - clamp the size of the process info table, so that we don't |
634 | | * attempt to allocate a huge process info table and fail. |
635 | | */ |
636 | 0 | if (process_info_table_count > 512*1024) { |
637 | 0 | *err = WTAP_ERR_BAD_FILE; |
638 | 0 | *err_info = ws_strdup_printf("netmon: process info table size is %u, which is larger than we support", |
639 | 0 | process_info_table_count); |
640 | 0 | return WTAP_OPEN_ERROR; |
641 | 0 | } |
642 | | |
643 | 0 | if (process_info_table_offset > file_size) { |
644 | 0 | *err = WTAP_ERR_BAD_FILE; |
645 | 0 | *err_info = ws_strdup_printf("netmon: process info table offset (%u) is larger than file", |
646 | 0 | process_info_table_offset); |
647 | 0 | return WTAP_OPEN_ERROR; |
648 | 0 | } |
649 | 0 | } |
650 | | |
651 | | /* |
652 | | * Return back to the frame table offset |
653 | | */ |
654 | 0 | if (file_seek(wth->fh, frame_table_offset, SEEK_SET, err) == -1) { |
655 | 0 | return WTAP_OPEN_ERROR; |
656 | 0 | } |
657 | | |
658 | | /* |
659 | | * Sanity check the process info table information before we bother to allocate |
660 | | * large chunks of memory for the frame table |
661 | | */ |
662 | | |
663 | 0 | frame_table = (uint32_t *)g_try_malloc(frame_table_length); |
664 | 0 | if (frame_table_length != 0 && frame_table == NULL) { |
665 | 0 | *err = ENOMEM; /* we assume we're out of memory */ |
666 | 0 | return WTAP_OPEN_ERROR; |
667 | 0 | } |
668 | 0 | if (!wtap_read_bytes(wth->fh, frame_table, frame_table_length, |
669 | 0 | err, err_info)) { |
670 | 0 | g_free(frame_table); |
671 | 0 | return WTAP_OPEN_ERROR; |
672 | 0 | } |
673 | 0 | netmon->frame_table_size = frame_table_size; |
674 | 0 | netmon->frame_table = frame_table; |
675 | |
|
676 | 0 | if (comment_table_size > 0) { |
677 | 0 | comment_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, netmonrec_comment_destroy); |
678 | 0 | if (comment_table == NULL) { |
679 | 0 | *err = ENOMEM; /* we assume we're out of memory */ |
680 | 0 | return WTAP_OPEN_ERROR; |
681 | 0 | } |
682 | | |
683 | | /* Make sure the file contains the full comment section */ |
684 | 0 | if (file_seek(wth->fh, comment_table_offset+comment_table_size, SEEK_SET, err) == -1) { |
685 | 0 | g_hash_table_destroy(comment_table); |
686 | 0 | return WTAP_OPEN_ERROR; |
687 | 0 | } |
688 | | |
689 | 0 | if (file_seek(wth->fh, comment_table_offset, SEEK_SET, err) == -1) { |
690 | | /* Shouldn't fail... */ |
691 | 0 | g_hash_table_destroy(comment_table); |
692 | 0 | return WTAP_OPEN_ERROR; |
693 | 0 | } |
694 | | |
695 | 0 | while (comment_table_size > 16) { |
696 | 0 | struct netmonrec_comment_header comment_header; |
697 | 0 | uint32_t title_length; |
698 | 0 | uint32_t desc_length; |
699 | 0 | uint8_t *utf16_str; |
700 | | |
701 | | /* Read the first 12 bytes of the structure */ |
702 | 0 | if (!wtap_read_bytes(wth->fh, &comment_header, 12, err, err_info)) { |
703 | 0 | g_hash_table_destroy(comment_table); |
704 | 0 | return WTAP_OPEN_ERROR; |
705 | 0 | } |
706 | 0 | comment_table_size -= 12; |
707 | | |
708 | | /* Make sure comment size is sane */ |
709 | 0 | title_length = pletoh32(&comment_header.titleLength); |
710 | 0 | if (title_length == 0) { |
711 | 0 | *err = WTAP_ERR_BAD_FILE; |
712 | 0 | *err_info = g_strdup("netmon: comment title size can't be 0"); |
713 | 0 | g_hash_table_destroy(comment_table); |
714 | 0 | return WTAP_OPEN_ERROR; |
715 | 0 | } |
716 | 0 | if (title_length > comment_table_size) { |
717 | 0 | *err = WTAP_ERR_BAD_FILE; |
718 | 0 | *err_info = ws_strdup_printf("netmon: comment title size is %u, which is larger than the amount remaining in the comment section (%u)", |
719 | 0 | title_length, comment_table_size); |
720 | 0 | g_hash_table_destroy(comment_table); |
721 | 0 | return WTAP_OPEN_ERROR; |
722 | 0 | } |
723 | | |
724 | 0 | comment_rec = g_new0(struct netmonrec_comment, 1); |
725 | 0 | comment_rec->numFramePerComment = pletoh32(&comment_header.numFramePerComment); |
726 | 0 | comment_rec->frameOffset = pletoh32(&comment_header.frameOffset); |
727 | |
|
728 | 0 | g_hash_table_insert(comment_table, GUINT_TO_POINTER(comment_rec->frameOffset), comment_rec); |
729 | | |
730 | | /* |
731 | | * Read in the comment title. |
732 | | * |
733 | | * It is in UTF-16-encoded Unicode, and the title |
734 | | * size is a count of octets, not octet pairs or |
735 | | * Unicode characters. |
736 | | */ |
737 | 0 | utf16_str = (uint8_t*)g_malloc(title_length); |
738 | 0 | if (!wtap_read_bytes(wth->fh, utf16_str, title_length, |
739 | 0 | err, err_info)) { |
740 | 0 | g_hash_table_destroy(comment_table); |
741 | 0 | return WTAP_OPEN_ERROR; |
742 | 0 | } |
743 | 0 | comment_table_size -= title_length; |
744 | | |
745 | | /* |
746 | | * Now convert it to UTF-8 for internal use. |
747 | | */ |
748 | 0 | comment_rec->title = utf_16_to_utf_8(utf16_str, |
749 | 0 | title_length); |
750 | 0 | g_free(utf16_str); |
751 | |
|
752 | 0 | if (comment_table_size < 4) { |
753 | 0 | *err = WTAP_ERR_BAD_FILE; |
754 | 0 | *err_info = g_strdup("netmon: corrupt comment section"); |
755 | 0 | g_hash_table_destroy(comment_table); |
756 | 0 | return WTAP_OPEN_ERROR; |
757 | 0 | } |
758 | | |
759 | 0 | if (!wtap_read_bytes(wth->fh, &desc_length, 4, err, err_info)) { |
760 | 0 | g_hash_table_destroy(comment_table); |
761 | 0 | return WTAP_OPEN_ERROR; |
762 | 0 | } |
763 | 0 | comment_table_size -= 4; |
764 | |
|
765 | 0 | comment_rec->descLength = pletoh32(&desc_length); |
766 | 0 | if (comment_rec->descLength > 0) { |
767 | | /* Make sure comment size is sane */ |
768 | 0 | if (comment_rec->descLength > comment_table_size) { |
769 | 0 | *err = WTAP_ERR_BAD_FILE; |
770 | 0 | *err_info = ws_strdup_printf("netmon: comment description size is %u, which is larger than the amount remaining in the comment section (%u)", |
771 | 0 | comment_rec->descLength, comment_table_size); |
772 | 0 | g_hash_table_destroy(comment_table); |
773 | 0 | return WTAP_OPEN_ERROR; |
774 | 0 | } |
775 | | |
776 | 0 | comment_rec->description = (uint8_t*)g_malloc(comment_rec->descLength); |
777 | | |
778 | | /* Read the comment description */ |
779 | 0 | if (!wtap_read_bytes(wth->fh, comment_rec->description, comment_rec->descLength, err, err_info)) { |
780 | 0 | g_hash_table_destroy(comment_table); |
781 | 0 | return WTAP_OPEN_ERROR; |
782 | 0 | } |
783 | | |
784 | 0 | comment_table_size -= comment_rec->descLength; |
785 | 0 | } |
786 | 0 | } |
787 | 0 | netmon->comment_table = comment_table; |
788 | 0 | } |
789 | | |
790 | 0 | if ((process_info_table_offset > 0) && (process_info_table_count > 0)) { |
791 | 0 | uint16_t version; |
792 | | |
793 | | /* Go to the process table offset */ |
794 | 0 | if (file_seek(wth->fh, process_info_table_offset, SEEK_SET, err) == -1) { |
795 | 0 | return WTAP_OPEN_ERROR; |
796 | 0 | } |
797 | | |
798 | 0 | process_info_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, netmonrec_process_info_destroy); |
799 | 0 | if (process_info_table == NULL) { |
800 | 0 | *err = ENOMEM; /* we assume we're out of memory */ |
801 | 0 | return WTAP_OPEN_ERROR; |
802 | 0 | } |
803 | | |
804 | | /* Read the version (ignored for now) */ |
805 | 0 | if (!wtap_read_bytes(wth->fh, &version, 2, err, err_info)) { |
806 | 0 | g_hash_table_destroy(process_info_table); |
807 | 0 | return WTAP_OPEN_ERROR; |
808 | 0 | } |
809 | | |
810 | 0 | while (process_info_table_count > 0) |
811 | 0 | { |
812 | 0 | struct netmonrec_process_info* process_info; |
813 | 0 | uint32_t tmp32; |
814 | 0 | uint16_t tmp16; |
815 | 0 | uint32_t path_size; |
816 | 0 | uint8_t *utf16_str; |
817 | |
|
818 | 0 | process_info = g_new0(struct netmonrec_process_info, 1); |
819 | | |
820 | | /* Read path */ |
821 | 0 | if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) { |
822 | 0 | g_free(process_info); |
823 | 0 | g_hash_table_destroy(process_info_table); |
824 | 0 | return WTAP_OPEN_ERROR; |
825 | 0 | } |
826 | | |
827 | 0 | path_size = pletoh32(&tmp32); |
828 | 0 | if (path_size > MATH_PROCINFO_PATH_SIZE) { |
829 | 0 | *err = WTAP_ERR_BAD_FILE; |
830 | 0 | *err_info = ws_strdup_printf("netmon: Path size for process info record is %u, which is larger than allowed max value (%u)", |
831 | 0 | path_size, MATH_PROCINFO_PATH_SIZE); |
832 | 0 | g_free(process_info); |
833 | 0 | g_hash_table_destroy(process_info_table); |
834 | 0 | return WTAP_OPEN_ERROR; |
835 | 0 | } |
836 | | |
837 | | /* |
838 | | * Read in the path string. |
839 | | * |
840 | | * It is in UTF-16-encoded Unicode, and the path |
841 | | * size is a count of octets, not octet pairs or |
842 | | * Unicode characters. |
843 | | */ |
844 | 0 | utf16_str = (uint8_t*)g_malloc(path_size); |
845 | 0 | if (!wtap_read_bytes(wth->fh, utf16_str, path_size, |
846 | 0 | err, err_info)) { |
847 | 0 | g_free(process_info); |
848 | 0 | g_hash_table_destroy(process_info_table); |
849 | 0 | return WTAP_OPEN_ERROR; |
850 | 0 | } |
851 | | |
852 | | /* |
853 | | * Now convert it to UTF-8 for internal use. |
854 | | */ |
855 | 0 | process_info->path = utf_16_to_utf_8(utf16_str, |
856 | 0 | path_size); |
857 | 0 | g_free(utf16_str); |
858 | | |
859 | | /* Read icon (currently not saved) */ |
860 | 0 | if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) { |
861 | 0 | g_free(process_info); |
862 | 0 | g_hash_table_destroy(process_info_table); |
863 | 0 | return WTAP_OPEN_ERROR; |
864 | 0 | } |
865 | | |
866 | 0 | process_info->iconSize = pletoh32(&tmp32); |
867 | | |
868 | | /* XXX - skip the icon for now */ |
869 | 0 | if (file_seek(wth->fh, process_info->iconSize, SEEK_CUR, err) == -1) { |
870 | 0 | g_free(process_info); |
871 | 0 | g_hash_table_destroy(process_info_table); |
872 | 0 | return WTAP_OPEN_ERROR; |
873 | 0 | } |
874 | 0 | process_info->iconSize = 0; |
875 | |
|
876 | 0 | if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) { |
877 | 0 | g_free(process_info); |
878 | 0 | g_hash_table_destroy(process_info_table); |
879 | 0 | return WTAP_OPEN_ERROR; |
880 | 0 | } |
881 | 0 | process_info->pid = pletoh32(&tmp32); |
882 | | |
883 | | /* XXX - Currently index process information by PID */ |
884 | 0 | g_hash_table_insert(process_info_table, GUINT_TO_POINTER(process_info->pid), process_info); |
885 | | |
886 | | /* Read local port */ |
887 | 0 | if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) { |
888 | 0 | g_hash_table_destroy(process_info_table); |
889 | 0 | return WTAP_OPEN_ERROR; |
890 | 0 | } |
891 | 0 | process_info->localPort = pletoh16(&tmp16); |
892 | | |
893 | | /* Skip padding */ |
894 | 0 | if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) { |
895 | 0 | g_hash_table_destroy(process_info_table); |
896 | 0 | return WTAP_OPEN_ERROR; |
897 | 0 | } |
898 | | |
899 | | /* Read remote port */ |
900 | 0 | if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) { |
901 | 0 | g_hash_table_destroy(process_info_table); |
902 | 0 | return WTAP_OPEN_ERROR; |
903 | 0 | } |
904 | 0 | process_info->remotePort = pletoh16(&tmp16); |
905 | | |
906 | | /* Skip padding */ |
907 | 0 | if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) { |
908 | 0 | g_hash_table_destroy(process_info_table); |
909 | 0 | return WTAP_OPEN_ERROR; |
910 | 0 | } |
911 | | |
912 | | /* Determine IP version */ |
913 | 0 | if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) { |
914 | 0 | g_hash_table_destroy(process_info_table); |
915 | 0 | return WTAP_OPEN_ERROR; |
916 | 0 | } |
917 | 0 | process_info->isIPv6 = ((pletoh32(&tmp32) == 0) ? false : true); |
918 | |
|
919 | 0 | if (process_info->isIPv6) { |
920 | 0 | if (!wtap_read_bytes(wth->fh, &process_info->localAddr.ipv6, 16, err, err_info)) { |
921 | 0 | g_hash_table_destroy(process_info_table); |
922 | 0 | return WTAP_OPEN_ERROR; |
923 | 0 | } |
924 | 0 | if (!wtap_read_bytes(wth->fh, &process_info->remoteAddr.ipv6, 16, err, err_info)) { |
925 | 0 | g_hash_table_destroy(process_info_table); |
926 | 0 | return WTAP_OPEN_ERROR; |
927 | 0 | } |
928 | 0 | } else { |
929 | 0 | uint8_t ipbuffer[16]; |
930 | 0 | if (!wtap_read_bytes(wth->fh, ipbuffer, 16, err, err_info)) { |
931 | 0 | g_hash_table_destroy(process_info_table); |
932 | 0 | return WTAP_OPEN_ERROR; |
933 | 0 | } |
934 | 0 | process_info->localAddr.ipv4 = pletoh32(ipbuffer); |
935 | |
|
936 | 0 | if (!wtap_read_bytes(wth->fh, ipbuffer, 16, err, err_info)) { |
937 | 0 | g_hash_table_destroy(process_info_table); |
938 | 0 | return WTAP_OPEN_ERROR; |
939 | 0 | } |
940 | 0 | process_info->remoteAddr.ipv4 = pletoh32(ipbuffer); |
941 | 0 | } |
942 | | |
943 | 0 | process_info_table_count--; |
944 | 0 | } |
945 | | |
946 | 0 | netmon->process_info_table = process_info_table; |
947 | 0 | } |
948 | | |
949 | | #if G_BYTE_ORDER == G_BIG_ENDIAN |
950 | | /* |
951 | | * OK, now byte-swap the frame table. |
952 | | */ |
953 | | for (i = 0; i < frame_table_size; i++) |
954 | | frame_table[i] = pletoh32(&frame_table[i]); |
955 | | #endif |
956 | | |
957 | | /* Set up to start reading at the first frame. */ |
958 | 0 | netmon->current_frame = 0; |
959 | 0 | switch (netmon->version_major) { |
960 | | |
961 | 0 | case 1: |
962 | | /* |
963 | | * Version 1.x of the file format supports |
964 | | * millisecond precision. |
965 | | */ |
966 | 0 | wth->file_tsprec = WTAP_TSPREC_MSEC; |
967 | 0 | break; |
968 | | |
969 | 0 | case 2: |
970 | | /* |
971 | | * Versions 2.0 through 2.2 support microsecond |
972 | | * precision; version 2.3 supports 100-nanosecond |
973 | | * precision (2.3 was the last version). |
974 | | */ |
975 | 0 | if (netmon->version_minor >= 3) |
976 | 0 | wth->file_tsprec = WTAP_TSPREC_100_NSEC; |
977 | 0 | else |
978 | 0 | wth->file_tsprec = WTAP_TSPREC_USEC; |
979 | 0 | break; |
980 | 0 | } |
981 | 0 | return WTAP_OPEN_MINE; |
982 | 0 | } |
983 | | |
984 | | static void |
985 | | netmon_set_pseudo_header_info(wtap_rec *rec) |
986 | 0 | { |
987 | 0 | switch (rec->rec_header.packet_header.pkt_encap) { |
988 | | |
989 | 0 | case WTAP_ENCAP_ATM_PDUS: |
990 | | /* |
991 | | * Attempt to guess from the packet data, the VPI, and |
992 | | * the VCI information about the type of traffic. |
993 | | */ |
994 | 0 | atm_guess_traffic_type(rec); |
995 | 0 | break; |
996 | | |
997 | 0 | case WTAP_ENCAP_ETHERNET: |
998 | | /* |
999 | | * We assume there's no FCS in this frame. |
1000 | | */ |
1001 | 0 | rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0; |
1002 | 0 | break; |
1003 | | |
1004 | 0 | case WTAP_ENCAP_IEEE_802_11_NETMON: |
1005 | | /* |
1006 | | * The 802.11 metadata at the beginnning of the frame data |
1007 | | * is processed by a dissector, which fills in a pseudo- |
1008 | | * header and passes it to the 802.11 radio dissector, |
1009 | | * just as is done with other 802.11 radio metadata headers |
1010 | | * that are part of the packet data, such as radiotap. |
1011 | | */ |
1012 | 0 | break; |
1013 | 0 | } |
1014 | 0 | } |
1015 | | |
1016 | | typedef enum { |
1017 | | SUCCESS, |
1018 | | FAILURE, |
1019 | | RETRY |
1020 | | } process_record_retval; |
1021 | | |
1022 | | static process_record_retval |
1023 | | netmon_process_record(wtap *wth, FILE_T fh, wtap_rec *rec, |
1024 | | int *err, char **err_info) |
1025 | 0 | { |
1026 | 0 | netmon_t *netmon = (netmon_t *)wth->priv; |
1027 | 0 | int hdr_size = 0; |
1028 | 0 | union { |
1029 | 0 | struct netmonrec_1_x_hdr hdr_1_x; |
1030 | 0 | struct netmonrec_2_x_hdr hdr_2_x; |
1031 | 0 | } hdr; |
1032 | 0 | int64_t delta = 0; /* signed - frame times can be before the nominal start */ |
1033 | 0 | int64_t t; |
1034 | 0 | time_t secs; |
1035 | 0 | int nsecs; |
1036 | 0 | uint32_t packet_size = 0; |
1037 | 0 | uint32_t orig_size = 0; |
1038 | 0 | int trlr_size; |
1039 | 0 | union { |
1040 | 0 | struct netmonrec_2_1_trlr trlr_2_1; |
1041 | 0 | struct netmonrec_2_2_trlr trlr_2_2; |
1042 | 0 | struct netmonrec_2_3_trlr trlr_2_3; |
1043 | 0 | } trlr; |
1044 | 0 | uint16_t network; |
1045 | 0 | int pkt_encap; |
1046 | 0 | struct netmonrec_comment* comment_rec = NULL; |
1047 | | |
1048 | | /* Read record header. */ |
1049 | 0 | switch (netmon->version_major) { |
1050 | | |
1051 | 0 | case 1: |
1052 | 0 | hdr_size = sizeof (struct netmonrec_1_x_hdr); |
1053 | 0 | break; |
1054 | | |
1055 | 0 | case 2: |
1056 | 0 | hdr_size = sizeof (struct netmonrec_2_x_hdr); |
1057 | 0 | break; |
1058 | 0 | } |
1059 | 0 | if (!wtap_read_bytes_or_eof(fh, &hdr, hdr_size, err, err_info)) |
1060 | 0 | return FAILURE; |
1061 | | |
1062 | 0 | switch (netmon->version_major) { |
1063 | | |
1064 | 0 | case 1: |
1065 | 0 | orig_size = pletoh16(&hdr.hdr_1_x.orig_len); |
1066 | 0 | packet_size = pletoh16(&hdr.hdr_1_x.incl_len); |
1067 | 0 | break; |
1068 | | |
1069 | 0 | case 2: |
1070 | 0 | orig_size = pletoh32(&hdr.hdr_2_x.orig_len); |
1071 | 0 | packet_size = pletoh32(&hdr.hdr_2_x.incl_len); |
1072 | 0 | break; |
1073 | 0 | } |
1074 | 0 | if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) { |
1075 | | /* |
1076 | | * Probably a corrupt capture file; don't blow up trying |
1077 | | * to allocate space for an immensely-large packet. |
1078 | | */ |
1079 | 0 | *err = WTAP_ERR_BAD_FILE; |
1080 | 0 | *err_info = ws_strdup_printf("netmon: File has %u-byte packet, bigger than maximum of %u", |
1081 | 0 | packet_size, WTAP_MAX_PACKET_SIZE_STANDARD); |
1082 | 0 | return FAILURE; |
1083 | 0 | } |
1084 | | |
1085 | 0 | rec->rec_type = REC_TYPE_PACKET; |
1086 | 0 | rec->block = wtap_block_create(WTAP_BLOCK_PACKET); |
1087 | | |
1088 | | /* |
1089 | | * If this is an ATM packet, the first |
1090 | | * "sizeof (struct netmon_atm_hdr)" bytes have destination and |
1091 | | * source addresses (6 bytes - MAC addresses of some sort?) |
1092 | | * and the VPI and VCI; read them and generate the pseudo-header |
1093 | | * from them. |
1094 | | */ |
1095 | 0 | switch (wth->file_encap) { |
1096 | | |
1097 | 0 | case WTAP_ENCAP_ATM_PDUS: |
1098 | 0 | if (packet_size < sizeof (struct netmon_atm_hdr)) { |
1099 | | /* |
1100 | | * Uh-oh, the packet isn't big enough to even |
1101 | | * have a pseudo-header. |
1102 | | */ |
1103 | 0 | *err = WTAP_ERR_BAD_FILE; |
1104 | 0 | *err_info = ws_strdup_printf("netmon: ATM file has a %u-byte packet, too small to have even an ATM pseudo-header", |
1105 | 0 | packet_size); |
1106 | 0 | return FAILURE; |
1107 | 0 | } |
1108 | 0 | if (!netmon_read_atm_pseudoheader(fh, &rec->rec_header.packet_header.pseudo_header, |
1109 | 0 | err, err_info)) |
1110 | 0 | return FAILURE; /* Read error */ |
1111 | | |
1112 | | /* |
1113 | | * Don't count the pseudo-header as part of the packet. |
1114 | | */ |
1115 | 0 | orig_size -= (unsigned)sizeof (struct netmon_atm_hdr); |
1116 | 0 | packet_size -= (unsigned)sizeof (struct netmon_atm_hdr); |
1117 | 0 | break; |
1118 | | |
1119 | 0 | default: |
1120 | 0 | break; |
1121 | 0 | } |
1122 | | |
1123 | 0 | switch (netmon->version_major) { |
1124 | | |
1125 | 0 | case 1: |
1126 | | /* |
1127 | | * According to Paul Long, this offset is unsigned. |
1128 | | * It's 32 bits, so the maximum value will fit in |
1129 | | * a int64_t such as delta, even after multiplying |
1130 | | * it by 1000000. |
1131 | | * |
1132 | | * pletoh32() returns a uint32_t; we cast it to int64_t |
1133 | | * before multiplying, so that the product doesn't |
1134 | | * overflow a uint32_t. |
1135 | | */ |
1136 | 0 | delta = ((int64_t)pletoh32(&hdr.hdr_1_x.ts_delta))*1000000; |
1137 | 0 | break; |
1138 | | |
1139 | 0 | case 2: |
1140 | | /* |
1141 | | * OK, this is weird. Microsoft's documentation |
1142 | | * says this is in microseconds and is a 64-bit |
1143 | | * unsigned number, but it can be negative; they |
1144 | | * say what appears to amount to "treat it as an |
1145 | | * unsigned number, multiply it by 10, and then |
1146 | | * interpret the resulting 64-bit quantity as a |
1147 | | * signed number". That operation can turn a |
1148 | | * value with the uppermost bit 0 to a value with |
1149 | | * the uppermost bit 1, hence turning a large |
1150 | | * positive number-of-microseconds into a small |
1151 | | * negative number-of-100-nanosecond-increments. |
1152 | | */ |
1153 | 0 | delta = pletoh64(&hdr.hdr_2_x.ts_delta)*10; |
1154 | | |
1155 | | /* |
1156 | | * OK, it's now a signed value in 100-nanosecond |
1157 | | * units. Now convert it to nanosecond units. |
1158 | | */ |
1159 | 0 | delta *= 100; |
1160 | 0 | break; |
1161 | 0 | } |
1162 | 0 | secs = 0; |
1163 | 0 | t = netmon->start_nsecs + delta; |
1164 | 0 | while (t < 0) { |
1165 | | /* |
1166 | | * Propagate a borrow into the seconds. |
1167 | | * The seconds is a time_t, and can be < 0 |
1168 | | * (unlikely, as Windows didn't exist before |
1169 | | * January 1, 1970, 00:00:00 UTC), while the |
1170 | | * nanoseconds should be positive, as in |
1171 | | * "nanoseconds since the instant of time |
1172 | | * represented by the seconds". |
1173 | | * |
1174 | | * We do not want t to be negative, as, according |
1175 | | * to the C90 standard, "if either operand [of / |
1176 | | * or %] is negative, whether the result of the |
1177 | | * / operator is the largest integer less than or |
1178 | | * equal to the algebraic quotient or the smallest |
1179 | | * greater than or equal to the algebraic quotient |
1180 | | * is implementation-defined, as is the sign of |
1181 | | * the result of the % operator", and we want |
1182 | | * the result of the division and remainder |
1183 | | * operations to be the same on all platforms. |
1184 | | */ |
1185 | 0 | t += 1000000000; |
1186 | 0 | secs--; |
1187 | 0 | } |
1188 | 0 | secs += (time_t)(t/1000000000); |
1189 | 0 | nsecs = (int)(t%1000000000); |
1190 | 0 | rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN; |
1191 | 0 | rec->ts.secs = netmon->start_secs + secs; |
1192 | 0 | rec->ts.nsecs = nsecs; |
1193 | 0 | rec->rec_header.packet_header.caplen = packet_size; |
1194 | 0 | rec->rec_header.packet_header.len = orig_size; |
1195 | | |
1196 | | /* |
1197 | | * Read the packet data. |
1198 | | */ |
1199 | 0 | if (!wtap_read_bytes_buffer(fh, &rec->data, rec->rec_header.packet_header.caplen, err, err_info)) |
1200 | 0 | return FAILURE; |
1201 | | |
1202 | | /* |
1203 | | * For version 2.1 and later, there's additional information |
1204 | | * after the frame data. |
1205 | | */ |
1206 | 0 | if (netmon->version_major == 2 && netmon->version_minor >= 1) { |
1207 | 0 | switch (netmon->version_minor) { |
1208 | | |
1209 | 0 | case 1: |
1210 | 0 | trlr_size = (int)sizeof (struct netmonrec_2_1_trlr); |
1211 | 0 | break; |
1212 | | |
1213 | 0 | case 2: |
1214 | 0 | trlr_size = (int)sizeof (struct netmonrec_2_2_trlr); |
1215 | 0 | break; |
1216 | | |
1217 | 0 | default: |
1218 | 0 | trlr_size = (int)sizeof (struct netmonrec_2_3_trlr); |
1219 | 0 | break; |
1220 | 0 | } |
1221 | | |
1222 | 0 | if (!wtap_read_bytes(fh, &trlr, trlr_size, err, err_info)) |
1223 | 0 | return FAILURE; |
1224 | | |
1225 | 0 | network = pletoh16(trlr.trlr_2_1.network); |
1226 | 0 | if ((network >= 0xE080) && (network <= 0xE08A)) { |
1227 | | /* These values "violate" the LINKTYPE_ media type values |
1228 | | * in Microsoft Analyzer and are considered a MAExportedMediaType, |
1229 | | * so they need their own WTAP_ types |
1230 | | */ |
1231 | 0 | switch (network) |
1232 | 0 | { |
1233 | 0 | case 0xE080: // "WiFi Message" |
1234 | 0 | pkt_encap = WTAP_ENCAP_IEEE_802_11; |
1235 | 0 | break; |
1236 | 0 | case 0xE081: // "Ndis Etw WiFi Channel Message" |
1237 | 0 | case 0xE082: // "Fiddler Netmon Message" |
1238 | 0 | case 0xE089: // "Pef Ndis Msg"; |
1239 | 0 | case 0xE08A: // "Pef Ndis Wifi Meta Msg"; |
1240 | 0 | *err = WTAP_ERR_UNSUPPORTED; |
1241 | 0 | *err_info = ws_strdup_printf("netmon: network type %u unknown or unsupported", network); |
1242 | 0 | return FAILURE; |
1243 | 0 | case 0xE083: |
1244 | 0 | pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_V4; |
1245 | 0 | break; |
1246 | 0 | case 0xE084: |
1247 | 0 | pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_V6; |
1248 | 0 | break; |
1249 | 0 | case 0xE085: |
1250 | 0 | pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_2V4; |
1251 | 0 | break; |
1252 | 0 | case 0xE086: |
1253 | 0 | pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_2V6; |
1254 | 0 | break; |
1255 | 0 | case 0xE087: |
1256 | 0 | pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_AUTH_V4; |
1257 | 0 | break; |
1258 | 0 | case 0xE088: |
1259 | 0 | pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_AUTH_V6; |
1260 | 0 | break; |
1261 | 0 | default: |
1262 | 0 | pkt_encap = WTAP_ENCAP_UNKNOWN; |
1263 | 0 | break; |
1264 | 0 | } |
1265 | 0 | } else if ((network & 0xF000) == NETMON_NET_PCAP_BASE) { |
1266 | | /* |
1267 | | * Converted pcap file - the LINKTYPE_ value |
1268 | | * is the network value with 0xF000 masked off. |
1269 | | */ |
1270 | 0 | network &= 0x0FFF; |
1271 | 0 | pkt_encap = wtap_pcap_encap_to_wtap_encap(network); |
1272 | 0 | if (pkt_encap == WTAP_ENCAP_UNKNOWN) { |
1273 | 0 | *err = WTAP_ERR_UNSUPPORTED; |
1274 | 0 | *err_info = ws_strdup_printf("netmon: converted pcap network type %u unknown or unsupported", |
1275 | 0 | network); |
1276 | 0 | return FAILURE; |
1277 | 0 | } |
1278 | 0 | } else if (network < NUM_NETMON_ENCAPS) { |
1279 | | /* |
1280 | | * Regular NetMon encapsulation. |
1281 | | */ |
1282 | 0 | pkt_encap = netmon_encap[network]; |
1283 | 0 | if (pkt_encap == WTAP_ENCAP_UNKNOWN) { |
1284 | 0 | *err = WTAP_ERR_UNSUPPORTED; |
1285 | 0 | *err_info = ws_strdup_printf("netmon: network type %u unknown or unsupported", |
1286 | 0 | network); |
1287 | 0 | return FAILURE; |
1288 | 0 | } |
1289 | 0 | } else { |
1290 | | /* |
1291 | | * Special packet type for metadata. |
1292 | | */ |
1293 | 0 | switch (network) { |
1294 | | |
1295 | 0 | case NETMON_NET_NETEVENT: |
1296 | | /* |
1297 | | * Event Tracing event. |
1298 | | * |
1299 | | * https://docs.microsoft.com/en-us/windows/win32/api/evntcons/ns-evntcons-event_header |
1300 | | */ |
1301 | 0 | pkt_encap = WTAP_ENCAP_NETMON_NET_NETEVENT; |
1302 | 0 | break; |
1303 | | |
1304 | 0 | case NETMON_NET_NETWORK_INFO_EX: |
1305 | | /* |
1306 | | * List of adapters on which the capture |
1307 | | * was done. |
1308 | | * XXX - this could be translated into pcapng |
1309 | | * blocks but for now, just treat as a frame. |
1310 | | */ |
1311 | 0 | pkt_encap = WTAP_ENCAP_NETMON_NETWORK_INFO_EX; |
1312 | 0 | break; |
1313 | | |
1314 | 0 | case NETMON_NET_PAYLOAD_HEADER: |
1315 | | /* |
1316 | | * Header for a fake frame constructed |
1317 | | * by reassembly. |
1318 | | */ |
1319 | 0 | return RETRY; |
1320 | | |
1321 | 0 | case NETMON_NET_NETWORK_INFO: |
1322 | | /* |
1323 | | * List of adapters on which the capture |
1324 | | * was done. |
1325 | | */ |
1326 | 0 | return RETRY; |
1327 | | |
1328 | 0 | case NETMON_NET_DNS_CACHE: |
1329 | | /* |
1330 | | * List of resolved IP addresses. |
1331 | | */ |
1332 | 0 | return RETRY; |
1333 | | |
1334 | 0 | case NETMON_NET_NETMON_FILTER: |
1335 | | /* |
1336 | | * NetMon capture or display filter |
1337 | | * string. |
1338 | | */ |
1339 | 0 | pkt_encap = WTAP_ENCAP_NETMON_NET_FILTER; |
1340 | 0 | break; |
1341 | | |
1342 | 0 | default: |
1343 | 0 | *err = WTAP_ERR_UNSUPPORTED; |
1344 | 0 | *err_info = ws_strdup_printf("netmon: network type %u unknown or unsupported", |
1345 | 0 | network); |
1346 | 0 | return FAILURE; |
1347 | 0 | } |
1348 | 0 | } |
1349 | | |
1350 | 0 | rec->rec_header.packet_header.pkt_encap = pkt_encap; |
1351 | 0 | if (netmon->version_minor >= 3) { |
1352 | | /* |
1353 | | * This is a 2.3 or later file. That format |
1354 | | * contains a UTC per-packet time stamp; use |
1355 | | * that instead of the start time and offset. |
1356 | | */ |
1357 | 0 | uint64_t d; |
1358 | |
|
1359 | 0 | d = pletoh64(trlr.trlr_2_3.utc_timestamp); |
1360 | | |
1361 | | /* |
1362 | | * Get the time as seconds and nanoseconds. |
1363 | | * and overwrite the time stamp obtained |
1364 | | * from the record header. |
1365 | | */ |
1366 | 0 | if (!filetime_to_nstime(&rec->ts, d)) { |
1367 | 0 | *err = WTAP_ERR_BAD_FILE; |
1368 | 0 | *err_info = g_strdup("netmon: time stamp outside supported range"); |
1369 | 0 | return FAILURE; |
1370 | 0 | } |
1371 | 0 | } |
1372 | 0 | } |
1373 | | |
1374 | 0 | netmon_set_pseudo_header_info(rec); |
1375 | | |
1376 | | /* If any header specific information is present, set it as pseudo header data |
1377 | | * and set the encapsulation type, so it can be handled to the netmon_header |
1378 | | * dissector for further processing |
1379 | | */ |
1380 | 0 | if (netmon->comment_table != NULL) { |
1381 | 0 | comment_rec = (struct netmonrec_comment*)g_hash_table_lookup(netmon->comment_table, GUINT_TO_POINTER(netmon->frame_table[netmon->current_frame-1])); |
1382 | 0 | } |
1383 | |
|
1384 | 0 | if (comment_rec != NULL) { |
1385 | 0 | union wtap_pseudo_header temp_header; |
1386 | | |
1387 | | /* These are the current encapsulation types that NetMon uses. |
1388 | | * Save them off so they can be copied to the NetMon pseudoheader |
1389 | | */ |
1390 | 0 | switch (rec->rec_header.packet_header.pkt_encap) |
1391 | 0 | { |
1392 | 0 | case WTAP_ENCAP_ATM_PDUS: |
1393 | 0 | memcpy(&temp_header.atm, &rec->rec_header.packet_header.pseudo_header.atm, sizeof(temp_header.atm)); |
1394 | 0 | break; |
1395 | 0 | case WTAP_ENCAP_ETHERNET: |
1396 | 0 | memcpy(&temp_header.eth, &rec->rec_header.packet_header.pseudo_header.eth, sizeof(temp_header.eth)); |
1397 | 0 | break; |
1398 | 0 | case WTAP_ENCAP_IEEE_802_11_NETMON: |
1399 | 0 | memcpy(&temp_header.ieee_802_11, &rec->rec_header.packet_header.pseudo_header.ieee_802_11, sizeof(temp_header.ieee_802_11)); |
1400 | 0 | break; |
1401 | 0 | } |
1402 | 0 | memset(&rec->rec_header.packet_header.pseudo_header.netmon, 0, sizeof(rec->rec_header.packet_header.pseudo_header.netmon)); |
1403 | | |
1404 | | /* Save the current encapsulation type to the NetMon pseudoheader */ |
1405 | 0 | rec->rec_header.packet_header.pseudo_header.netmon.sub_encap = rec->rec_header.packet_header.pkt_encap; |
1406 | | |
1407 | | /* Copy the comment data */ |
1408 | 0 | rec->rec_header.packet_header.pseudo_header.netmon.title = comment_rec->title; |
1409 | 0 | rec->rec_header.packet_header.pseudo_header.netmon.descLength = comment_rec->descLength; |
1410 | 0 | rec->rec_header.packet_header.pseudo_header.netmon.description = comment_rec->description; |
1411 | | |
1412 | | /* Copy the saved pseudoheaders to the netmon pseudoheader structure */ |
1413 | 0 | switch (rec->rec_header.packet_header.pkt_encap) |
1414 | 0 | { |
1415 | 0 | case WTAP_ENCAP_ATM_PDUS: |
1416 | 0 | memcpy(&rec->rec_header.packet_header.pseudo_header.netmon.subheader.atm, &temp_header.atm, sizeof(temp_header.atm)); |
1417 | 0 | break; |
1418 | 0 | case WTAP_ENCAP_ETHERNET: |
1419 | 0 | memcpy(&rec->rec_header.packet_header.pseudo_header.netmon.subheader.eth, &temp_header.eth, sizeof(temp_header.eth)); |
1420 | 0 | break; |
1421 | 0 | case WTAP_ENCAP_IEEE_802_11_NETMON: |
1422 | 0 | memcpy(&rec->rec_header.packet_header.pseudo_header.netmon.subheader.ieee_802_11, &temp_header.ieee_802_11, sizeof(temp_header.ieee_802_11)); |
1423 | 0 | break; |
1424 | 0 | } |
1425 | | |
1426 | | /* Encapsulation type is now something that can be passed to netmon_header dissector */ |
1427 | 0 | rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETMON_HEADER; |
1428 | 0 | } |
1429 | | |
1430 | 0 | return SUCCESS; |
1431 | 0 | } |
1432 | | |
1433 | | /* Read the next packet */ |
1434 | | static bool netmon_read(wtap *wth, wtap_rec *rec, |
1435 | | int *err, char **err_info, int64_t *data_offset) |
1436 | 0 | { |
1437 | 0 | netmon_t *netmon = (netmon_t *)wth->priv; |
1438 | 0 | int64_t rec_offset; |
1439 | |
|
1440 | 0 | for (;;) { |
1441 | | /* Have we reached the end of the packet data? */ |
1442 | 0 | if (netmon->current_frame >= netmon->frame_table_size) { |
1443 | 0 | *err = 0; /* it's just an EOF, not an error */ |
1444 | 0 | return false; |
1445 | 0 | } |
1446 | | |
1447 | | /* Seek to the beginning of the current record, if we're |
1448 | | not there already (seeking to the current position |
1449 | | may still cause a seek and a read of the underlying file, |
1450 | | so we don't want to do it unconditionally). |
1451 | | |
1452 | | Yes, the current record could be before the previous |
1453 | | record. At least some captures put the trailer record |
1454 | | with statistics as the first physical record in the |
1455 | | file, but set the frame table up so it's the last |
1456 | | record in sequence. */ |
1457 | 0 | rec_offset = netmon->frame_table[netmon->current_frame]; |
1458 | 0 | if (file_tell(wth->fh) != rec_offset) { |
1459 | 0 | if (file_seek(wth->fh, rec_offset, SEEK_SET, err) == -1) |
1460 | 0 | return false; |
1461 | 0 | } |
1462 | 0 | netmon->current_frame++; |
1463 | |
|
1464 | 0 | *data_offset = file_tell(wth->fh); |
1465 | |
|
1466 | 0 | switch (netmon_process_record(wth, wth->fh, rec, err, err_info)) { |
1467 | | |
1468 | 0 | case RETRY: |
1469 | 0 | continue; |
1470 | | |
1471 | 0 | case SUCCESS: |
1472 | 0 | return true; |
1473 | | |
1474 | 0 | case FAILURE: |
1475 | 0 | return false; |
1476 | 0 | } |
1477 | 0 | } |
1478 | 0 | } |
1479 | | |
1480 | | static bool |
1481 | | netmon_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, |
1482 | | int *err, char **err_info) |
1483 | 0 | { |
1484 | 0 | if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) |
1485 | 0 | return false; |
1486 | | |
1487 | 0 | switch (netmon_process_record(wth, wth->random_fh, rec, err, err_info)) { |
1488 | | |
1489 | 0 | default: |
1490 | | /* |
1491 | | * This should not happen. |
1492 | | */ |
1493 | 0 | *err = WTAP_ERR_BAD_FILE; |
1494 | 0 | *err_info = g_strdup("netmon: saw metadata in netmon_seek_read"); |
1495 | 0 | return false; |
1496 | | |
1497 | 0 | case SUCCESS: |
1498 | 0 | return true; |
1499 | | |
1500 | 0 | case FAILURE: |
1501 | 0 | return false; |
1502 | 0 | } |
1503 | 0 | } |
1504 | | |
1505 | | static bool |
1506 | | netmon_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header, |
1507 | | int *err, char **err_info) |
1508 | 0 | { |
1509 | 0 | struct netmon_atm_hdr atm_phdr; |
1510 | 0 | uint16_t vpi, vci; |
1511 | |
|
1512 | 0 | if (!wtap_read_bytes(fh, &atm_phdr, sizeof (struct netmon_atm_hdr), |
1513 | 0 | err, err_info)) |
1514 | 0 | return false; |
1515 | | |
1516 | 0 | vpi = g_ntohs(atm_phdr.vpi); |
1517 | 0 | vci = g_ntohs(atm_phdr.vci); |
1518 | |
|
1519 | 0 | pseudo_header->atm.vpi = vpi; |
1520 | 0 | pseudo_header->atm.vci = vci; |
1521 | | |
1522 | | /* We don't have this information */ |
1523 | 0 | pseudo_header->atm.flags = 0; |
1524 | 0 | pseudo_header->atm.channel = 0; |
1525 | 0 | pseudo_header->atm.cells = 0; |
1526 | 0 | pseudo_header->atm.aal5t_u2u = 0; |
1527 | 0 | pseudo_header->atm.aal5t_len = 0; |
1528 | 0 | pseudo_header->atm.aal5t_chksum = 0; |
1529 | |
|
1530 | 0 | return true; |
1531 | 0 | } |
1532 | | |
1533 | | /* Throw away the frame table used by the sequential I/O stream. */ |
1534 | | static void |
1535 | | netmon_close(wtap *wth) |
1536 | 0 | { |
1537 | 0 | netmon_t *netmon = (netmon_t *)wth->priv; |
1538 | |
|
1539 | 0 | if (netmon->frame_table != NULL) { |
1540 | 0 | g_free(netmon->frame_table); |
1541 | 0 | netmon->frame_table = NULL; |
1542 | 0 | } |
1543 | |
|
1544 | 0 | if (netmon->comment_table != NULL) { |
1545 | 0 | g_hash_table_destroy(netmon->comment_table); |
1546 | 0 | netmon->comment_table = NULL; |
1547 | 0 | } |
1548 | |
|
1549 | 0 | if (netmon->process_info_table != NULL) { |
1550 | 0 | g_hash_table_destroy(netmon->process_info_table); |
1551 | 0 | netmon->process_info_table = NULL; |
1552 | 0 | } |
1553 | 0 | } |
1554 | | |
1555 | | typedef struct { |
1556 | | bool is_v2; |
1557 | | bool got_first_record_time; |
1558 | | nstime_t first_record_time; |
1559 | | uint32_t frame_table_offset; |
1560 | | uint32_t *frame_table; |
1561 | | unsigned frame_table_index; |
1562 | | unsigned frame_table_size; |
1563 | | bool no_more_room; /* true if no more records can be written */ |
1564 | | } netmon_dump_t; |
1565 | | |
1566 | | static const int wtap_encap[] = { |
1567 | | -1, /* WTAP_ENCAP_UNKNOWN -> unsupported */ |
1568 | | 1, /* WTAP_ENCAP_ETHERNET -> NDIS Ethernet */ |
1569 | | 2, /* WTAP_ENCAP_TOKEN_RING -> NDIS Token Ring */ |
1570 | | -1, /* WTAP_ENCAP_SLIP -> unsupported */ |
1571 | | -1, /* WTAP_ENCAP_PPP -> unsupported */ |
1572 | | 3, /* WTAP_ENCAP_FDDI -> NDIS FDDI */ |
1573 | | 3, /* WTAP_ENCAP_FDDI_BITSWAPPED -> NDIS FDDI */ |
1574 | | -1, /* WTAP_ENCAP_RAW_IP -> unsupported */ |
1575 | | -1, /* WTAP_ENCAP_ARCNET -> unsupported */ |
1576 | | -1, /* WTAP_ENCAP_ARCNET_LINUX -> unsupported */ |
1577 | | -1, /* WTAP_ENCAP_ATM_RFC1483 -> unsupported */ |
1578 | | -1, /* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */ |
1579 | | -1, /* WTAP_ENCAP_LAPB -> unsupported*/ |
1580 | | 4, /* WTAP_ENCAP_ATM_PDUS -> NDIS WAN (*NOT* ATM!) */ |
1581 | | }; |
1582 | 0 | #define NUM_WTAP_ENCAPS array_length(wtap_encap) |
1583 | | |
1584 | | /* Returns 0 if we could write the specified encapsulation type, |
1585 | | an error indication otherwise. */ |
1586 | | static int netmon_dump_can_write_encap_1_x(int encap) |
1587 | 0 | { |
1588 | | /* |
1589 | | * Per-packet encapsulations are *not* supported in NetMon 1.x |
1590 | | * format. |
1591 | | */ |
1592 | 0 | if (encap < 0 || (unsigned) encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1) |
1593 | 0 | return WTAP_ERR_UNWRITABLE_ENCAP; |
1594 | | |
1595 | 0 | return 0; |
1596 | 0 | } |
1597 | | |
1598 | | static int netmon_dump_can_write_encap_2_x(int encap) |
1599 | 0 | { |
1600 | | /* |
1601 | | * Per-packet encapsulations are supported in NetMon 2.1 |
1602 | | * format. |
1603 | | */ |
1604 | 0 | if (encap == WTAP_ENCAP_PER_PACKET) |
1605 | 0 | return 0; |
1606 | | |
1607 | 0 | if (encap < 0 || (unsigned) encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1) |
1608 | 0 | return WTAP_ERR_UNWRITABLE_ENCAP; |
1609 | | |
1610 | 0 | return 0; |
1611 | 0 | } |
1612 | | |
1613 | | /* Returns true on success, false on failure; sets "*err" to an error code on |
1614 | | failure */ |
1615 | | static bool netmon_dump_open(wtap_dumper *wdh, bool is_v2, |
1616 | | int *err, char **err_info _U_) |
1617 | 0 | { |
1618 | 0 | netmon_dump_t *netmon; |
1619 | | |
1620 | | /* We can't fill in all the fields in the file header, as we |
1621 | | haven't yet written any packets. As we'll have to rewrite |
1622 | | the header when we've written out all the packets, we just |
1623 | | skip over the header for now. */ |
1624 | 0 | if (wtap_dump_file_seek(wdh, CAPTUREFILE_HEADER_SIZE, SEEK_SET, err) == -1) |
1625 | 0 | return false; |
1626 | | |
1627 | 0 | wdh->bytes_dumped = CAPTUREFILE_HEADER_SIZE; |
1628 | 0 | wdh->subtype_write = netmon_dump; |
1629 | 0 | wdh->subtype_finish = netmon_dump_finish; |
1630 | |
|
1631 | 0 | netmon = g_new(netmon_dump_t, 1); |
1632 | 0 | wdh->priv = (void *)netmon; |
1633 | 0 | netmon->is_v2 = is_v2; |
1634 | 0 | netmon->frame_table_offset = CAPTUREFILE_HEADER_SIZE; |
1635 | 0 | netmon->got_first_record_time = false; |
1636 | 0 | netmon->frame_table = NULL; |
1637 | 0 | netmon->frame_table_index = 0; |
1638 | 0 | netmon->frame_table_size = 0; |
1639 | 0 | netmon->no_more_room = false; |
1640 | |
|
1641 | 0 | return true; |
1642 | 0 | } |
1643 | | |
1644 | | static bool netmon_dump_open_1_x(wtap_dumper *wdh, int *err, char **err_info _U_) |
1645 | 0 | { |
1646 | 0 | return netmon_dump_open(wdh, false, err, err_info); |
1647 | 0 | } |
1648 | | |
1649 | | static bool netmon_dump_open_2_x(wtap_dumper *wdh, int *err, char **err_info _U_) |
1650 | 0 | { |
1651 | 0 | return netmon_dump_open(wdh, true, err, err_info); |
1652 | 0 | } |
1653 | | |
1654 | | /* Write a record for a packet to a dump file. |
1655 | | Returns true on success, false on failure. */ |
1656 | | static bool netmon_dump(wtap_dumper *wdh, const wtap_rec *rec, |
1657 | | const uint8_t *pd, int *err, char **err_info _U_) |
1658 | 0 | { |
1659 | 0 | const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header; |
1660 | 0 | netmon_dump_t *netmon = (netmon_dump_t *)wdh->priv; |
1661 | 0 | struct netmonrec_1_x_hdr rec_1_x_hdr; |
1662 | 0 | struct netmonrec_2_x_hdr rec_2_x_hdr; |
1663 | 0 | void *hdrp; |
1664 | 0 | size_t rec_size; |
1665 | 0 | struct netmonrec_2_1_trlr rec_2_x_trlr; |
1666 | 0 | size_t hdr_size; |
1667 | 0 | struct netmon_atm_hdr atm_hdr; |
1668 | 0 | int atm_hdrsize; |
1669 | 0 | int64_t secs; |
1670 | 0 | int32_t nsecs; |
1671 | | |
1672 | | /* We can only write packet records. */ |
1673 | 0 | if (rec->rec_type != REC_TYPE_PACKET) { |
1674 | 0 | *err = WTAP_ERR_UNWRITABLE_REC_TYPE; |
1675 | 0 | return false; |
1676 | 0 | } |
1677 | | |
1678 | 0 | if (netmon->is_v2) { |
1679 | | /* Don't write anything we're not willing to read. */ |
1680 | 0 | if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) { |
1681 | 0 | *err = WTAP_ERR_PACKET_TOO_LARGE; |
1682 | 0 | return false; |
1683 | 0 | } |
1684 | 0 | } else { |
1685 | | /* |
1686 | | * Make sure this packet doesn't have a link-layer type that |
1687 | | * differs from the one for the file. |
1688 | | */ |
1689 | 0 | if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) { |
1690 | 0 | *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; |
1691 | 0 | return false; |
1692 | 0 | } |
1693 | | |
1694 | | /* |
1695 | | * The length fields are 16-bit, so there's a hard limit |
1696 | | * of 65535. |
1697 | | */ |
1698 | 0 | if (rec->rec_header.packet_header.caplen > 65535) { |
1699 | 0 | *err = WTAP_ERR_PACKET_TOO_LARGE; |
1700 | 0 | return false; |
1701 | 0 | } |
1702 | 0 | } |
1703 | | |
1704 | 0 | if (wdh->file_encap == WTAP_ENCAP_PER_PACKET) { |
1705 | | /* |
1706 | | * Is this network type supported? |
1707 | | */ |
1708 | 0 | if (rec->rec_header.packet_header.pkt_encap < 0 || |
1709 | 0 | (unsigned) rec->rec_header.packet_header.pkt_encap >= NUM_WTAP_ENCAPS || |
1710 | 0 | wtap_encap[rec->rec_header.packet_header.pkt_encap] == -1) { |
1711 | | /* |
1712 | | * No. Fail. |
1713 | | */ |
1714 | 0 | *err = WTAP_ERR_UNWRITABLE_ENCAP; |
1715 | 0 | return false; |
1716 | 0 | } |
1717 | | |
1718 | | /* |
1719 | | * Fill in the trailer with the network type. |
1720 | | */ |
1721 | 0 | phtoles(rec_2_x_trlr.network, wtap_encap[rec->rec_header.packet_header.pkt_encap]); |
1722 | 0 | } |
1723 | | |
1724 | | /* |
1725 | | * Will the file offset of this frame fit in a 32-bit unsigned |
1726 | | * integer? |
1727 | | */ |
1728 | 0 | if (netmon->no_more_room) { |
1729 | | /* |
1730 | | * No, so the file is too big for NetMon format to |
1731 | | * handle. |
1732 | | */ |
1733 | 0 | *err = EFBIG; |
1734 | 0 | return false; |
1735 | 0 | } |
1736 | | |
1737 | | /* |
1738 | | * NetMon files have a capture start time in the file header, |
1739 | | * and have times relative to that in the packet headers; |
1740 | | * pick the time of the first packet as the capture start |
1741 | | * time. |
1742 | | * |
1743 | | * That time has millisecond resolution, so chop any |
1744 | | * sub-millisecond part of the time stamp off. |
1745 | | */ |
1746 | 0 | if (!netmon->got_first_record_time) { |
1747 | 0 | netmon->first_record_time.secs = rec->ts.secs; |
1748 | 0 | netmon->first_record_time.nsecs = |
1749 | 0 | (rec->ts.nsecs/1000000)*1000000; |
1750 | 0 | netmon->got_first_record_time = true; |
1751 | 0 | } |
1752 | |
|
1753 | 0 | if (wdh->file_encap == WTAP_ENCAP_ATM_PDUS) |
1754 | 0 | atm_hdrsize = sizeof (struct netmon_atm_hdr); |
1755 | 0 | else |
1756 | 0 | atm_hdrsize = 0; |
1757 | 0 | secs = (int64_t)(rec->ts.secs - netmon->first_record_time.secs); |
1758 | 0 | nsecs = rec->ts.nsecs - netmon->first_record_time.nsecs; |
1759 | 0 | while (nsecs < 0) { |
1760 | | /* |
1761 | | * Propagate a borrow into the seconds. |
1762 | | * The seconds is a time_t, and can be < 0 |
1763 | | * (unlikely, as neither UN*X nor DOS |
1764 | | * nor the original Mac System existed |
1765 | | * before January 1, 1970, 00:00:00 UTC), |
1766 | | * while the nanoseconds should be positive, |
1767 | | * as in "nanoseconds since the instant of time |
1768 | | * represented by the seconds". |
1769 | | * |
1770 | | * We do not want t to be negative, as, according |
1771 | | * to the C90 standard, "if either operand [of / |
1772 | | * or %] is negative, whether the result of the |
1773 | | * / operator is the largest integer less than or |
1774 | | * equal to the algebraic quotient or the smallest |
1775 | | * greater than or equal to the algebraic quotient |
1776 | | * is implementation-defined, as is the sign of |
1777 | | * the result of the % operator", and we want |
1778 | | * the result of the division and remainder |
1779 | | * operations to be the same on all platforms. |
1780 | | */ |
1781 | 0 | nsecs += 1000000000; |
1782 | 0 | secs--; |
1783 | 0 | } |
1784 | 0 | if (netmon->is_v2) { |
1785 | 0 | rec_2_x_hdr.ts_delta = GUINT64_TO_LE(secs*1000000 + (nsecs + 500)/1000); |
1786 | 0 | rec_2_x_hdr.orig_len = GUINT32_TO_LE(rec->rec_header.packet_header.len + atm_hdrsize); |
1787 | 0 | rec_2_x_hdr.incl_len = GUINT32_TO_LE(rec->rec_header.packet_header.caplen + atm_hdrsize); |
1788 | 0 | hdrp = &rec_2_x_hdr; |
1789 | 0 | hdr_size = sizeof rec_2_x_hdr; |
1790 | 0 | } else { |
1791 | 0 | rec_1_x_hdr.ts_delta = GUINT32_TO_LE(secs*1000 + (nsecs + 500000)/1000000); |
1792 | 0 | rec_1_x_hdr.orig_len = GUINT16_TO_LE(rec->rec_header.packet_header.len + atm_hdrsize); |
1793 | 0 | rec_1_x_hdr.incl_len = GUINT16_TO_LE(rec->rec_header.packet_header.caplen + atm_hdrsize); |
1794 | 0 | hdrp = &rec_1_x_hdr; |
1795 | 0 | hdr_size = sizeof rec_1_x_hdr; |
1796 | 0 | } |
1797 | | |
1798 | | /* |
1799 | | * Keep track of the record size, as we need to update |
1800 | | * the current file offset. |
1801 | | */ |
1802 | 0 | rec_size = 0; |
1803 | |
|
1804 | 0 | if (!wtap_dump_file_write(wdh, hdrp, hdr_size, err)) |
1805 | 0 | return false; |
1806 | 0 | rec_size += hdr_size; |
1807 | |
|
1808 | 0 | if (wdh->file_encap == WTAP_ENCAP_ATM_PDUS) { |
1809 | | /* |
1810 | | * Write the ATM header. |
1811 | | * We supply all-zero destination and source addresses. |
1812 | | */ |
1813 | 0 | memset(&atm_hdr.dest, 0, sizeof atm_hdr.dest); |
1814 | 0 | memset(&atm_hdr.src, 0, sizeof atm_hdr.src); |
1815 | 0 | atm_hdr.vpi = g_htons(pseudo_header->atm.vpi); |
1816 | 0 | atm_hdr.vci = g_htons(pseudo_header->atm.vci); |
1817 | 0 | if (!wtap_dump_file_write(wdh, &atm_hdr, sizeof atm_hdr, err)) |
1818 | 0 | return false; |
1819 | 0 | rec_size += sizeof atm_hdr; |
1820 | 0 | } |
1821 | | |
1822 | 0 | if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err)) |
1823 | 0 | return false; |
1824 | 0 | rec_size += rec->rec_header.packet_header.caplen; |
1825 | |
|
1826 | 0 | if (wdh->file_encap == WTAP_ENCAP_PER_PACKET) { |
1827 | | /* |
1828 | | * Write out the trailer. |
1829 | | */ |
1830 | 0 | if (!wtap_dump_file_write(wdh, &rec_2_x_trlr, |
1831 | 0 | sizeof rec_2_x_trlr, err)) |
1832 | 0 | return false; |
1833 | 0 | rec_size += sizeof rec_2_x_trlr; |
1834 | 0 | } |
1835 | | |
1836 | | /* |
1837 | | * Stash the file offset of this frame. |
1838 | | */ |
1839 | 0 | if (netmon->frame_table_size == 0) { |
1840 | | /* |
1841 | | * Haven't yet allocated the buffer for the frame table. |
1842 | | */ |
1843 | 0 | netmon->frame_table = (uint32_t *)g_malloc(1024 * sizeof *netmon->frame_table); |
1844 | 0 | netmon->frame_table_size = 1024; |
1845 | 0 | } else { |
1846 | | /* |
1847 | | * We've allocated it; are we at the end? |
1848 | | */ |
1849 | 0 | if (netmon->frame_table_index >= netmon->frame_table_size) { |
1850 | | /* |
1851 | | * Yes - double the size of the frame table. |
1852 | | */ |
1853 | 0 | netmon->frame_table_size *= 2; |
1854 | 0 | netmon->frame_table = (uint32_t *)g_realloc(netmon->frame_table, |
1855 | 0 | netmon->frame_table_size * sizeof *netmon->frame_table); |
1856 | 0 | } |
1857 | 0 | } |
1858 | |
|
1859 | 0 | netmon->frame_table[netmon->frame_table_index] = |
1860 | 0 | GUINT32_TO_LE(netmon->frame_table_offset); |
1861 | | |
1862 | | /* |
1863 | | * Is this the last record we can write? |
1864 | | * I.e., will the frame table offset of the next record not fit |
1865 | | * in a 32-bit frame table offset entry? |
1866 | | * |
1867 | | * (We don't bother checking whether the number of frames |
1868 | | * will fit in a 32-bit value, as, even if each record were |
1869 | | * 1 byte, if there were more than 2^32-1 packets, the frame |
1870 | | * table offset of at least one of those packets will be > |
1871 | | * 2^32 - 1.) |
1872 | | * |
1873 | | * Note: this also catches the unlikely possibility that |
1874 | | * the record itself is > 2^32 - 1 bytes long. |
1875 | | */ |
1876 | 0 | if ((uint64_t)netmon->frame_table_offset + rec_size > UINT32_MAX) { |
1877 | | /* |
1878 | | * Yup, too big. |
1879 | | */ |
1880 | 0 | netmon->no_more_room = true; |
1881 | 0 | } |
1882 | 0 | netmon->frame_table_index++; |
1883 | 0 | netmon->frame_table_offset += (uint32_t) rec_size; |
1884 | |
|
1885 | 0 | return true; |
1886 | 0 | } |
1887 | | |
1888 | | /* Finish writing to a dump file. |
1889 | | Returns true on success, false on failure. */ |
1890 | | static bool netmon_dump_finish(wtap_dumper *wdh, int *err, |
1891 | | char **err_info _U_) |
1892 | 0 | { |
1893 | 0 | netmon_dump_t *netmon = (netmon_dump_t *)wdh->priv; |
1894 | 0 | size_t n_to_write; |
1895 | 0 | struct netmon_hdr file_hdr; |
1896 | 0 | const char *magicp; |
1897 | 0 | size_t magic_size; |
1898 | 0 | struct tm *tm; |
1899 | 0 | int64_t saved_bytes_dumped; |
1900 | | |
1901 | | /* Write out the frame table. "netmon->frame_table_index" is |
1902 | | the number of entries we've put into it. */ |
1903 | 0 | n_to_write = netmon->frame_table_index * sizeof *netmon->frame_table; |
1904 | 0 | if (!wtap_dump_file_write(wdh, netmon->frame_table, n_to_write, err)) |
1905 | 0 | return false; |
1906 | | |
1907 | | /* Now go fix up the file header. */ |
1908 | 0 | if (wtap_dump_file_seek(wdh, 0, SEEK_SET, err) == -1) |
1909 | 0 | return false; |
1910 | | /* Save bytes_dumped since following calls to wtap_dump_file_write() |
1911 | | * will still (mistakenly) increase it. |
1912 | | */ |
1913 | 0 | saved_bytes_dumped = wdh->bytes_dumped; |
1914 | 0 | memset(&file_hdr, '\0', sizeof file_hdr); |
1915 | 0 | if (netmon->is_v2) { |
1916 | 0 | magicp = netmon_2_x_magic; |
1917 | 0 | magic_size = sizeof netmon_2_x_magic; |
1918 | | /* |
1919 | | * NetMon file version, for 2.x, is 2.0; |
1920 | | * for 3.0, it's 2.1. |
1921 | | * |
1922 | | * If the file encapsulation is WTAP_ENCAP_PER_PACKET, |
1923 | | * we need version 2.1. |
1924 | | * |
1925 | | * XXX - version 2.3 supports UTC time stamps; when |
1926 | | * should we use it? According to the file format |
1927 | | * documentation, NetMon 3.3 "cannot properly |
1928 | | * interpret" the UTC timestamp information; does |
1929 | | * that mean it ignores it and uses the local-time |
1930 | | * start time and time deltas, or mishandles them? |
1931 | | * Also, NetMon 3.1 and earlier can't read version |
1932 | | * 2.2, much less version 2.3. |
1933 | | */ |
1934 | 0 | file_hdr.ver_major = 2; |
1935 | 0 | file_hdr.ver_minor = |
1936 | 0 | (wdh->file_encap == WTAP_ENCAP_PER_PACKET) ? 1 : 0; |
1937 | 0 | } else { |
1938 | 0 | magicp = netmon_1_x_magic; |
1939 | 0 | magic_size = sizeof netmon_1_x_magic; |
1940 | | /* NetMon file version, for 1.x, is 1.1 */ |
1941 | 0 | file_hdr.ver_major = 1; |
1942 | 0 | file_hdr.ver_minor = 1; |
1943 | 0 | } |
1944 | 0 | if (!wtap_dump_file_write(wdh, magicp, magic_size, err)) |
1945 | 0 | return false; |
1946 | | |
1947 | 0 | if (wdh->file_encap == WTAP_ENCAP_PER_PACKET) { |
1948 | | /* |
1949 | | * We're writing NetMon 2.1 format, so the media |
1950 | | * type in the file header is irrelevant. Set it |
1951 | | * to 1, just as Network Monitor does. |
1952 | | */ |
1953 | 0 | file_hdr.network = GUINT16_TO_LE(1); |
1954 | 0 | } else |
1955 | 0 | file_hdr.network = GUINT16_TO_LE(wtap_encap[wdh->file_encap]); |
1956 | 0 | tm = localtime(&netmon->first_record_time.secs); |
1957 | 0 | if (tm != NULL) { |
1958 | 0 | file_hdr.ts_year = GUINT16_TO_LE(1900 + tm->tm_year); |
1959 | 0 | file_hdr.ts_month = GUINT16_TO_LE(tm->tm_mon + 1); |
1960 | 0 | file_hdr.ts_dow = GUINT16_TO_LE(tm->tm_wday); |
1961 | 0 | file_hdr.ts_day = GUINT16_TO_LE(tm->tm_mday); |
1962 | 0 | file_hdr.ts_hour = GUINT16_TO_LE(tm->tm_hour); |
1963 | 0 | file_hdr.ts_min = GUINT16_TO_LE(tm->tm_min); |
1964 | 0 | file_hdr.ts_sec = GUINT16_TO_LE(tm->tm_sec); |
1965 | 0 | } else { |
1966 | 0 | file_hdr.ts_year = GUINT16_TO_LE(1900 + 0); |
1967 | 0 | file_hdr.ts_month = GUINT16_TO_LE(0 + 1); |
1968 | 0 | file_hdr.ts_dow = GUINT16_TO_LE(0); |
1969 | 0 | file_hdr.ts_day = GUINT16_TO_LE(0); |
1970 | 0 | file_hdr.ts_hour = GUINT16_TO_LE(0); |
1971 | 0 | file_hdr.ts_min = GUINT16_TO_LE(0); |
1972 | 0 | file_hdr.ts_sec = GUINT16_TO_LE(0); |
1973 | 0 | } |
1974 | 0 | file_hdr.ts_msec = GUINT16_TO_LE(netmon->first_record_time.nsecs/1000000); |
1975 | 0 | file_hdr.frametableoffset = GUINT32_TO_LE(netmon->frame_table_offset); |
1976 | 0 | file_hdr.frametablelength = |
1977 | 0 | GUINT32_TO_LE(netmon->frame_table_index * sizeof *netmon->frame_table); |
1978 | 0 | if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err)) |
1979 | 0 | return false; |
1980 | | |
1981 | 0 | wdh->bytes_dumped = saved_bytes_dumped; |
1982 | 0 | return true; |
1983 | 0 | } |
1984 | | |
1985 | | static const struct supported_block_type netmon_1_x_blocks_supported[] = { |
1986 | | /* |
1987 | | * We support packet blocks, with no comments or other options. |
1988 | | */ |
1989 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
1990 | | }; |
1991 | | |
1992 | | static const struct file_type_subtype_info netmon_1_x_info = { |
1993 | | "Microsoft NetMon 1.x", "netmon1", "cap", NULL, |
1994 | | true, BLOCKS_SUPPORTED(netmon_1_x_blocks_supported), |
1995 | | netmon_dump_can_write_encap_1_x, netmon_dump_open_1_x, NULL |
1996 | | }; |
1997 | | |
1998 | | static const struct supported_block_type netmon_2_x_blocks_supported[] = { |
1999 | | /* |
2000 | | * We support packet blocks, with no comments or other options. |
2001 | | */ |
2002 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
2003 | | }; |
2004 | | |
2005 | | static const struct file_type_subtype_info netmon_2_x_info = { |
2006 | | "Microsoft NetMon 2.x", "netmon2", "cap", NULL, |
2007 | | true, BLOCKS_SUPPORTED(netmon_2_x_blocks_supported), |
2008 | | netmon_dump_can_write_encap_2_x, netmon_dump_open_2_x, NULL |
2009 | | }; |
2010 | | |
2011 | | void register_netmon(void) |
2012 | 14 | { |
2013 | 14 | netmon_1_x_file_type_subtype = wtap_register_file_type_subtype(&netmon_1_x_info); |
2014 | 14 | netmon_2_x_file_type_subtype = wtap_register_file_type_subtype(&netmon_2_x_info); |
2015 | | |
2016 | | /* |
2017 | | * Register names for backwards compatibility with the |
2018 | | * wtap_filetypes table in Lua. |
2019 | | */ |
2020 | 14 | wtap_register_backwards_compatibility_lua_name("NETMON_1_x", |
2021 | 14 | netmon_1_x_file_type_subtype); |
2022 | 14 | wtap_register_backwards_compatibility_lua_name("NETMON_2_x", |
2023 | 14 | netmon_2_x_file_type_subtype); |
2024 | 14 | } |
2025 | | |
2026 | | /* |
2027 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
2028 | | * |
2029 | | * Local variables: |
2030 | | * c-basic-offset: 8 |
2031 | | * tab-width: 8 |
2032 | | * indent-tabs-mode: t |
2033 | | * End: |
2034 | | * |
2035 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
2036 | | * :indentSize=8:tabSize=8:noTabs=false: |
2037 | | */ |