3 * $Id: netmon.c,v 1.40 2001/08/25 03:18:48 guy Exp $
6 * Copyright (c) 1998 by Gilbert Ramirez <gram@xiexie.org>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #include "file_wrappers.h"
35 * ftp://ftp.microsoft.com/developr/drg/cifs/cifs/Bhfile.zip
37 * contains "STRUCT.H", which declares the typedef CAPTUREFILE_HEADER
38 * for the header of a Microsoft Network Monitor capture file.
41 /* Capture file header, *including* magic number, is padded to 128 bytes. */
42 #define CAPTUREFILE_HEADER_SIZE 128
44 /* Magic number in Network Monitor 1.x files. */
45 static const char netmon_1_x_magic[] = {
49 /* Magic number in Network Monitor 2.x files. */
50 static const char netmon_2_x_magic[] = {
54 /* Network Monitor file header (minus magic number). */
56 guint8 ver_minor; /* minor version number */
57 guint8 ver_major; /* major version number */
58 guint16 network; /* network type */
59 guint16 ts_year; /* year of capture start */
60 guint16 ts_month; /* month of capture start (January = 1) */
61 guint16 ts_dow; /* day of week of capture start (Sun = 0) */
62 guint16 ts_day; /* day of month of capture start */
63 guint16 ts_hour; /* hour of capture start */
64 guint16 ts_min; /* minute of capture start */
65 guint16 ts_sec; /* second of capture start */
66 guint16 ts_msec; /* millisecond of capture start */
67 guint32 frametableoffset; /* frame index table offset */
68 guint32 frametablelength; /* frame index table size */
69 guint32 userdataoffset; /* user data offset */
70 guint32 userdatalength; /* user data size */
71 guint32 commentdataoffset; /* comment data offset */
72 guint32 commentdatalength; /* comment data size */
73 guint32 statisticsoffset; /* offset to statistics structure */
74 guint32 statisticslength; /* length of statistics structure */
75 guint32 networkinfooffset; /* offset to network info structure */
76 guint32 networkinfolength; /* length of network info structure */
79 /* Network Monitor 1.x record header; not defined in STRUCT.H, but deduced by
80 * looking at capture files. */
81 struct netmonrec_1_x_hdr {
82 guint32 ts_delta; /* time stamp - msecs since start of capture */
83 guint16 orig_len; /* actual length of packet */
84 guint16 incl_len; /* number of octets captured in file */
87 /* Network Monitor 2.x record header; not defined in STRUCT.H, but deduced by
88 * looking at capture files. */
89 struct netmonrec_2_x_hdr {
90 guint32 ts_delta_lo; /* time stamp - usecs since start of capture */
91 guint32 ts_delta_hi; /* time stamp - usecs since start of capture */
92 guint32 orig_len; /* actual length of packet */
93 guint32 incl_len; /* number of octets captured in file */
96 static gboolean netmon_read(wtap *wth, int *err, int *data_offset);
97 static void netmon_close(wtap *wth);
98 static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
99 const union wtap_pseudo_header *pseudo_header, const u_char *pd, int *err);
100 static gboolean netmon_dump_close(wtap_dumper *wdh, int *err);
102 int netmon_open(wtap *wth, int *err)
105 char magic[sizeof netmon_1_x_magic];
106 struct netmon_hdr hdr;
108 static const int netmon_encap[] = {
111 WTAP_ENCAP_TOKEN_RING,
112 WTAP_ENCAP_FDDI_BITSWAPPED,
113 WTAP_ENCAP_UNKNOWN, /* WAN */
114 WTAP_ENCAP_UNKNOWN, /* LocalTalk */
115 WTAP_ENCAP_UNKNOWN, /* "DIX" - should not occur */
116 WTAP_ENCAP_UNKNOWN, /* ARCNET raw */
117 WTAP_ENCAP_UNKNOWN, /* ARCNET 878.2 */
118 WTAP_ENCAP_UNKNOWN, /* ATM */
119 WTAP_ENCAP_UNKNOWN, /* Wireless WAN */
120 WTAP_ENCAP_UNKNOWN /* IrDA */
122 #define NUM_NETMON_ENCAPS (sizeof netmon_encap / sizeof netmon_encap[0])
124 int frame_table_offset;
125 guint32 frame_table_length;
126 guint32 frame_table_size;
127 guint32 *frame_table;
128 #ifdef WORDS_BIGENDIAN
132 /* Read in the string that should be at the start of a Network
134 errno = WTAP_ERR_CANT_READ;
135 bytes_read = file_read(magic, 1, sizeof magic, wth->fh);
136 if (bytes_read != sizeof magic) {
137 *err = file_error(wth->fh);
143 if (memcmp(magic, netmon_1_x_magic, sizeof netmon_1_x_magic) != 0
144 && memcmp(magic, netmon_2_x_magic, sizeof netmon_1_x_magic) != 0) {
148 /* Read the rest of the header. */
149 errno = WTAP_ERR_CANT_READ;
150 bytes_read = file_read(&hdr, 1, sizeof hdr, wth->fh);
151 if (bytes_read != sizeof hdr) {
152 *err = file_error(wth->fh);
158 switch (hdr.ver_major) {
161 file_type = WTAP_FILE_NETMON_1_x;
165 file_type = WTAP_FILE_NETMON_2_x;
169 g_message("netmon: major version %u unsupported", hdr.ver_major);
170 *err = WTAP_ERR_UNSUPPORTED;
174 hdr.network = pletohs(&hdr.network);
175 if (hdr.network >= NUM_NETMON_ENCAPS
176 || netmon_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
177 g_message("netmon: network type %u unknown or unsupported",
179 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
183 /* This is a netmon file */
184 wth->file_type = file_type;
185 wth->capture.netmon = g_malloc(sizeof(netmon_t));
186 wth->subtype_read = netmon_read;
187 wth->subtype_seek_read = wtap_def_seek_read;
188 wth->subtype_close = netmon_close;
189 wth->file_encap = netmon_encap[hdr.network];
190 wth->snapshot_length = 16384; /* XXX - not available in header */
192 * Convert the time stamp to a "time_t" and a number of
195 tm.tm_year = pletohs(&hdr.ts_year) - 1900;
196 tm.tm_mon = pletohs(&hdr.ts_month) - 1;
197 tm.tm_mday = pletohs(&hdr.ts_day);
198 tm.tm_hour = pletohs(&hdr.ts_hour);
199 tm.tm_min = pletohs(&hdr.ts_min);
200 tm.tm_sec = pletohs(&hdr.ts_sec);
202 wth->capture.netmon->start_secs = mktime(&tm);
204 * XXX - what if "secs" is -1? Unlikely, but if the capture was
205 * done in a time zone that switches between standard and summer
206 * time sometime other than when we do, and thus the time was one
207 * that doesn't exist here because a switch from standard to summer
208 * time zips over it, it could happen.
210 * On the other hand, if the capture was done in a different time
211 * zone, this won't work right anyway; unfortunately, the time
212 * zone isn't stored in the capture file (why the hell didn't
213 * they stuff a FILETIME, which is the number of 100-nanosecond
214 * intervals since 1601-01-01 00:00:00 "UTC", there, instead
215 * of stuffing a SYSTEMTIME, which is time-zone-dependent, there?).
217 wth->capture.netmon->start_usecs = pletohs(&hdr.ts_msec)*1000;
219 wth->capture.netmon->version_major = hdr.ver_major;
222 * Get the offset of the frame index table.
224 frame_table_offset = pletohl(&hdr.frametableoffset);
227 * It appears that some NetMon 2.x files don't have the
228 * first packet starting exactly 128 bytes into the file.
230 * Furthermore, it also appears that there are "holes" in
231 * the file, i.e. frame N+1 doesn't always follow immediately
234 * Therefore, we must read the frame table, and use the offsets
235 * in it as the offsets of the frames.
237 frame_table_length = pletohl(&hdr.frametablelength);
238 frame_table_size = frame_table_length / sizeof (guint32);
239 if ((frame_table_size * sizeof (guint32)) != frame_table_length) {
240 g_message("netmon: frame table length is %u, which is not a multiple of the size of an entry",
242 *err = WTAP_ERR_UNSUPPORTED;
245 if (frame_table_size == 0) {
246 g_message("netmon: frame table length is %u, which means it's less than one entry in size",
248 *err = WTAP_ERR_UNSUPPORTED;
251 frame_table = g_malloc(frame_table_length);
252 errno = WTAP_ERR_CANT_READ;
253 file_seek(wth->fh, frame_table_offset, SEEK_SET);
254 bytes_read = file_read(frame_table, 1, frame_table_length, wth->fh);
255 if (bytes_read != frame_table_length) {
256 *err = file_error(wth->fh);
261 wth->capture.netmon->frame_table_size = frame_table_size;
262 wth->capture.netmon->frame_table = frame_table;
264 #ifdef WORDS_BIGENDIAN
266 * OK, now byte-swap the frame table.
268 for (i = 0; i < frame_table_size; i++)
269 frame_table[i] = pletohl(&frame_table[i]);
272 /* Set up to start reading at the first frame. */
273 wth->capture.netmon->current_frame = 0;
278 /* Read the next packet */
279 static gboolean netmon_read(wtap *wth, int *err, int *data_offset)
281 netmon_t *netmon = wth->capture.netmon;
282 guint32 packet_size = 0;
285 struct netmonrec_1_x_hdr hdr_1_x;
286 struct netmonrec_2_x_hdr hdr_2_x;
294 /* Have we reached the end of the packet data? */
295 if (netmon->current_frame >= netmon->frame_table_size) {
296 /* Yes. We won't need the frame table any more;
298 g_free(wth->capture.netmon->frame_table);
299 wth->capture.netmon->frame_table = NULL;
300 *err = 0; /* it's just an EOF, not an error */
304 /* Seek to the beginning of the current record, if we're
305 not there already (seeking to the current position
306 may still cause a seek and a read of the underlying file,
307 so we don't want to do it unconditionally). */
308 rec_offset = netmon->frame_table[netmon->current_frame];
309 if (wth->data_offset != rec_offset) {
310 wth->data_offset = rec_offset;
311 file_seek(wth->fh, wth->data_offset, SEEK_SET);
313 netmon->current_frame++;
315 /* Read record header. */
316 switch (netmon->version_major) {
319 hdr_size = sizeof (struct netmonrec_1_x_hdr);
323 hdr_size = sizeof (struct netmonrec_2_x_hdr);
326 errno = WTAP_ERR_CANT_READ;
328 bytes_read = file_read(&hdr, 1, hdr_size, wth->fh);
329 if (bytes_read != hdr_size) {
330 *err = file_error(wth->fh);
331 if (*err == 0 && bytes_read != 0) {
332 *err = WTAP_ERR_SHORT_READ;
336 wth->data_offset += hdr_size;
338 switch (netmon->version_major) {
341 packet_size = pletohs(&hdr.hdr_1_x.incl_len);
345 packet_size = pletohl(&hdr.hdr_2_x.incl_len);
348 if (packet_size > WTAP_MAX_PACKET_SIZE) {
350 * Probably a corrupt capture file; don't blow up trying
351 * to allocate space for an immensely-large packet.
353 g_message("netmon: File has %u-byte packet, bigger than maximum of %u",
354 packet_size, WTAP_MAX_PACKET_SIZE);
355 *err = WTAP_ERR_BAD_RECORD;
358 buffer_assure_space(wth->frame_buffer, packet_size);
359 *data_offset = wth->data_offset;
360 errno = WTAP_ERR_CANT_READ;
361 bytes_read = file_read(buffer_start_ptr(wth->frame_buffer), 1,
362 packet_size, wth->fh);
364 if (bytes_read != packet_size) {
365 *err = file_error(wth->fh);
367 *err = WTAP_ERR_SHORT_READ;
370 wth->data_offset += packet_size;
372 t = (double)netmon->start_usecs;
373 switch (netmon->version_major) {
376 t += ((double)pletohl(&hdr.hdr_1_x.ts_delta))*1000;
380 t += (double)pletohl(&hdr.hdr_2_x.ts_delta_lo)
381 + (double)pletohl(&hdr.hdr_2_x.ts_delta_hi)*4294967296.0;
384 secs = (time_t)(t/1000000);
385 usecs = (guint32)(t - secs*1000000);
386 wth->phdr.ts.tv_sec = netmon->start_secs + secs;
387 wth->phdr.ts.tv_usec = usecs;
388 wth->phdr.caplen = packet_size;
389 switch (netmon->version_major) {
392 wth->phdr.len = pletohs(&hdr.hdr_1_x.orig_len);
396 wth->phdr.len = pletohl(&hdr.hdr_2_x.orig_len);
399 wth->phdr.pkt_encap = wth->file_encap;
405 netmon_close(wtap *wth)
407 if (wth->capture.netmon->frame_table != NULL)
408 g_free(wth->capture.netmon->frame_table);
409 g_free(wth->capture.netmon);
412 static const int wtap_encap[] = {
413 -1, /* WTAP_ENCAP_UNKNOWN -> unsupported */
414 1, /* WTAP_ENCAP_ETHERNET -> NDIS Ethernet */
415 2, /* WTAP_ENCAP_TOKEN_RING -> NDIS Token Ring */
416 -1, /* WTAP_ENCAP_SLIP -> unsupported */
417 -1, /* WTAP_ENCAP_PPP -> unsupported */
418 3, /* WTAP_ENCAP_FDDI -> NDIS FDDI */
419 3, /* WTAP_ENCAP_FDDI_BITSWAPPED -> NDIS FDDI */
420 -1, /* WTAP_ENCAP_RAW_IP -> unsupported */
421 -1, /* WTAP_ENCAP_ARCNET -> unsupported */
422 -1, /* WTAP_ENCAP_ATM_RFC1483 -> unsupported */
423 -1, /* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */
424 -1, /* WTAP_ENCAP_LAPB -> unsupported*/
425 -1, /* WTAP_ENCAP_ATM_SNIFFER -> unsupported */
426 -1 /* WTAP_ENCAP_NULL -> unsupported */
428 #define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
430 /* Returns 0 if we could write the specified encapsulation type,
431 an error indication otherwise. */
432 int netmon_dump_can_write_encap(int filetype, int encap)
434 /* Per-packet encapsulations aren't supported. */
435 if (encap == WTAP_ENCAP_PER_PACKET)
436 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
438 if (encap < 0 || encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
439 return WTAP_ERR_UNSUPPORTED_ENCAP;
444 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
446 gboolean netmon_dump_open(wtap_dumper *wdh, int *err)
448 /* This is a netmon file */
449 wdh->subtype_write = netmon_dump;
450 wdh->subtype_close = netmon_dump_close;
452 /* We can't fill in all the fields in the file header, as we
453 haven't yet written any packets. As we'll have to rewrite
454 the header when we've written out all the packets, we just
455 skip over the header for now. */
456 fseek(wdh->fh, CAPTUREFILE_HEADER_SIZE, SEEK_SET);
458 wdh->dump.netmon = g_malloc(sizeof(netmon_dump_t));
459 wdh->dump.netmon->frame_table_offset = CAPTUREFILE_HEADER_SIZE;
460 wdh->dump.netmon->got_first_record_time = FALSE;
461 wdh->dump.netmon->frame_table = NULL;
462 wdh->dump.netmon->frame_table_index = 0;
463 wdh->dump.netmon->frame_table_size = 0;
468 /* Write a record for a packet to a dump file.
469 Returns TRUE on success, FALSE on failure. */
470 static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
471 const union wtap_pseudo_header *pseudo_header, const u_char *pd, int *err)
473 netmon_dump_t *netmon = wdh->dump.netmon;
474 struct netmonrec_1_x_hdr rec_1_x_hdr;
475 struct netmonrec_2_x_hdr rec_2_x_hdr;
480 guint32 time_low, time_high;
482 /* NetMon files have a capture start time in the file header,
483 and have times relative to that in the packet headers;
484 pick the time of the first packet as the capture start
486 if (!netmon->got_first_record_time) {
487 netmon->first_record_time = phdr->ts;
488 netmon->got_first_record_time = TRUE;
491 switch (wdh->file_type) {
493 case WTAP_FILE_NETMON_1_x:
494 rec_1_x_hdr.ts_delta = htolel(
495 (phdr->ts.tv_sec - netmon->first_record_time.tv_sec)*1000
496 + (phdr->ts.tv_usec - netmon->first_record_time.tv_usec + 500)/1000);
497 rec_1_x_hdr.orig_len = htoles(phdr->len);
498 rec_1_x_hdr.incl_len = htoles(phdr->caplen);
499 hdrp = (char *)&rec_1_x_hdr;
500 hdr_size = sizeof rec_1_x_hdr;
503 case WTAP_FILE_NETMON_2_x:
505 * Unfortunately, not all the platforms on which we run
506 * support 64-bit integral types, even though most do
507 * (even on 32-bit processors), so we do it in floating
510 t = (phdr->ts.tv_sec - netmon->first_record_time.tv_sec)*1000000.0
511 + (phdr->ts.tv_usec - netmon->first_record_time.tv_usec);
512 time_high = t/4294967296.0;
513 time_low = t - (time_high*4294967296.0);
514 rec_2_x_hdr.ts_delta_lo = htolel(time_low);
515 rec_2_x_hdr.ts_delta_hi = htolel(time_high);
516 rec_2_x_hdr.orig_len = htolel(phdr->len);
517 rec_2_x_hdr.incl_len = htolel(phdr->caplen);
518 hdrp = (char *)&rec_2_x_hdr;
519 hdr_size = sizeof rec_2_x_hdr;
523 /* We should never get here - our open routine
524 should only get called for the types above. */
525 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
529 nwritten = fwrite(hdrp, 1, hdr_size, wdh->fh);
530 if (nwritten != hdr_size) {
531 if (nwritten == 0 && ferror(wdh->fh))
534 *err = WTAP_ERR_SHORT_WRITE;
537 nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
538 if (nwritten != phdr->caplen) {
539 if (nwritten == 0 && ferror(wdh->fh))
542 *err = WTAP_ERR_SHORT_WRITE;
547 * Stash the file offset of this frame.
549 if (netmon->frame_table_size == 0) {
551 * Haven't yet allocated the buffer for the frame table.
553 netmon->frame_table = g_malloc(1024 * sizeof *netmon->frame_table);
554 netmon->frame_table_size = 1024;
557 * We've allocated it; are we at the end?
559 if (netmon->frame_table_index >= netmon->frame_table_size) {
561 * Yes - double the size of the frame table.
563 netmon->frame_table_size *= 2;
564 netmon->frame_table = g_realloc(netmon->frame_table,
565 netmon->frame_table_size * sizeof *netmon->frame_table);
568 netmon->frame_table[netmon->frame_table_index] =
569 htolel(netmon->frame_table_offset);
570 netmon->frame_table_index++;
571 netmon->frame_table_offset += hdr_size + phdr->caplen;
576 /* Finish writing to a dump file.
577 Returns TRUE on success, FALSE on failure. */
578 static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
580 netmon_dump_t *netmon = wdh->dump.netmon;
583 struct netmon_hdr file_hdr;
588 /* Write out the frame table. "netmon->frame_table_index" is
589 the number of entries we've put into it. */
590 n_to_write = netmon->frame_table_index * sizeof *netmon->frame_table;
591 nwritten = fwrite(netmon->frame_table, 1, n_to_write, wdh->fh);
592 if (nwritten != n_to_write) {
593 if (nwritten == 0 && ferror(wdh->fh))
596 *err = WTAP_ERR_SHORT_WRITE;
600 /* Now go fix up the file header. */
601 fseek(wdh->fh, 0, SEEK_SET);
602 memset(&file_hdr, '\0', sizeof file_hdr);
603 switch (wdh->file_type) {
605 case WTAP_FILE_NETMON_1_x:
606 magicp = netmon_1_x_magic;
607 magic_size = sizeof netmon_1_x_magic;
608 /* current NetMon version, for 1.x, is 1.1 */
609 file_hdr.ver_major = 1;
610 file_hdr.ver_minor = 1;
613 case WTAP_FILE_NETMON_2_x:
614 magicp = netmon_2_x_magic;
615 magic_size = sizeof netmon_2_x_magic;
616 /* current NetMon version, for 2.x, is 2.0 */
617 file_hdr.ver_major = 2;
618 file_hdr.ver_minor = 0;
622 /* We should never get here - our open routine
623 should only get called for the types above. */
624 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
627 nwritten = fwrite(magicp, 1, magic_size, wdh->fh);
628 if (nwritten != magic_size) {
629 if (nwritten == 0 && ferror(wdh->fh))
632 *err = WTAP_ERR_SHORT_WRITE;
636 file_hdr.network = htoles(wtap_encap[wdh->encap]);
637 tm = localtime(&netmon->first_record_time.tv_sec);
639 file_hdr.ts_year = htoles(1900 + tm->tm_year);
640 file_hdr.ts_month = htoles(tm->tm_mon + 1);
641 file_hdr.ts_dow = htoles(tm->tm_wday);
642 file_hdr.ts_day = htoles(tm->tm_mday);
643 file_hdr.ts_hour = htoles(tm->tm_hour);
644 file_hdr.ts_min = htoles(tm->tm_min);
645 file_hdr.ts_sec = htoles(tm->tm_sec);
647 file_hdr.ts_year = htoles(1900 + 0);
648 file_hdr.ts_month = htoles(0 + 1);
649 file_hdr.ts_dow = htoles(0);
650 file_hdr.ts_day = htoles(0);
651 file_hdr.ts_hour = htoles(0);
652 file_hdr.ts_min = htoles(0);
653 file_hdr.ts_sec = htoles(0);
655 file_hdr.ts_msec = htoles(netmon->first_record_time.tv_usec/1000);
656 /* XXX - what about rounding? */
657 file_hdr.frametableoffset = htolel(netmon->frame_table_offset);
658 file_hdr.frametablelength =
659 htolel(netmon->frame_table_index * sizeof *netmon->frame_table);
660 nwritten = fwrite(&file_hdr, 1, sizeof file_hdr, wdh->fh);
661 if (nwritten != sizeof file_hdr) {
662 if (nwritten == 0 && ferror(wdh->fh))
665 *err = WTAP_ERR_SHORT_WRITE;