3 * $Id: netmon.c,v 1.32 2000/08/25 21:25:39 gram 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 int netmon_read(wtap *wth, int *err);
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[] = {
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 int 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 file_seek(wth->fh, 0, SEEK_SET);
135 errno = WTAP_ERR_CANT_READ;
136 bytes_read = file_read(magic, 1, sizeof magic, wth->fh);
137 if (bytes_read != sizeof magic) {
138 *err = file_error(wth->fh);
144 if (memcmp(magic, netmon_1_x_magic, sizeof netmon_1_x_magic) != 0
145 && memcmp(magic, netmon_2_x_magic, sizeof netmon_1_x_magic) != 0) {
149 /* Read the rest of the header. */
150 errno = WTAP_ERR_CANT_READ;
151 bytes_read = file_read(&hdr, 1, sizeof hdr, wth->fh);
152 if (bytes_read != sizeof hdr) {
153 *err = file_error(wth->fh);
159 switch (hdr.ver_major) {
162 file_type = WTAP_FILE_NETMON_1_x;
166 file_type = WTAP_FILE_NETMON_2_x;
170 g_message("netmon: major version %u unsupported", hdr.ver_major);
171 *err = WTAP_ERR_UNSUPPORTED;
175 hdr.network = pletohs(&hdr.network);
176 if (hdr.network >= NUM_NETMON_ENCAPS
177 || netmon_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
178 g_message("netmon: network type %u unknown or unsupported",
180 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
184 /* This is a netmon file */
185 wth->file_type = file_type;
186 wth->capture.netmon = g_malloc(sizeof(netmon_t));
187 wth->subtype_read = netmon_read;
188 wth->subtype_seek_read = wtap_def_seek_read;
189 wth->subtype_close = netmon_close;
190 wth->file_encap = netmon_encap[hdr.network];
191 wth->snapshot_length = 16384; /* XXX - not available in header */
193 * Convert the time stamp to a "time_t" and a number of
196 tm.tm_year = pletohs(&hdr.ts_year) - 1900;
197 tm.tm_mon = pletohs(&hdr.ts_month) - 1;
198 tm.tm_mday = pletohs(&hdr.ts_day);
199 tm.tm_hour = pletohs(&hdr.ts_hour);
200 tm.tm_min = pletohs(&hdr.ts_min);
201 tm.tm_sec = pletohs(&hdr.ts_sec);
203 wth->capture.netmon->start_secs = mktime(&tm);
205 * XXX - what if "secs" is -1? Unlikely, but if the capture was
206 * done in a time zone that switches between standard and summer
207 * time sometime other than when we do, and thus the time was one
208 * that doesn't exist here because a switch from standard to summer
209 * time zips over it, it could happen.
211 * On the other hand, if the capture was done in a different time
212 * zone, this won't work right anyway; unfortunately, the time
213 * zone isn't stored in the capture file (why the hell didn't
214 * they stuff a FILETIME, which is the number of 100-nanosecond
215 * intervals since 1601-01-01 00:00:00 "UTC", there, instead
216 * of stuffing a SYSTEMTIME, which is time-zone-dependent, there?).
218 wth->capture.netmon->start_usecs = pletohs(&hdr.ts_msec)*1000;
220 wth->capture.netmon->version_major = hdr.ver_major;
223 * Get the offset of the frame index table.
225 frame_table_offset = pletohl(&hdr.frametableoffset);
228 * It appears that some NetMon 2.x files don't have the
229 * first packet starting exactly 128 bytes into the file.
231 * Furthermore, it also appears that there are "holes" in
232 * the file, i.e. frame N+1 doesn't always follow immediately
235 * Therefore, we must read the frame table, and use the offsets
236 * in it as the offsets of the frames.
238 frame_table_length = pletohl(&hdr.frametablelength);
239 frame_table_size = frame_table_length / sizeof (guint32);
240 if ((frame_table_size * sizeof (guint32)) != frame_table_length) {
241 g_message("netmon: frame table length is %u, which is not a multiple of the size of an entry",
243 *err = WTAP_ERR_UNSUPPORTED;
246 if (frame_table_size == 0) {
247 g_message("netmon: frame table length is %u, which means it's less than one entry in size",
249 *err = WTAP_ERR_UNSUPPORTED;
252 frame_table = g_malloc(frame_table_length);
253 errno = WTAP_ERR_CANT_READ;
254 file_seek(wth->fh, frame_table_offset, SEEK_SET);
255 bytes_read = file_read(frame_table, 1, frame_table_length, wth->fh);
256 if (bytes_read != frame_table_length) {
257 *err = file_error(wth->fh);
262 wth->capture.netmon->frame_table_size = frame_table_size;
263 wth->capture.netmon->frame_table = frame_table;
265 #ifdef WORDS_BIGENDIAN
267 * OK, now byte-swap the frame table.
269 for (i = 0; i < frame_table_size; i++)
270 frame_table[i] = pletohl(&frame_table[i]);
273 /* Set up to start reading at the first frame. */
274 wth->capture.netmon->current_frame = 0;
279 /* Read the next packet */
280 static int netmon_read(wtap *wth, int *err)
282 netmon_t *netmon = wth->capture.netmon;
283 guint32 packet_size = 0;
286 struct netmonrec_1_x_hdr hdr_1_x;
287 struct netmonrec_2_x_hdr hdr_2_x;
295 /* Have we reached the end of the packet data? */
296 if (netmon->current_frame >= netmon->frame_table_size) {
297 /* Yes. We won't need the frame table any more;
299 g_free(wth->capture.netmon->frame_table);
300 wth->capture.netmon->frame_table = NULL;
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 data_offset = netmon->frame_table[netmon->current_frame];
309 if (wth->data_offset != data_offset) {
310 wth->data_offset = data_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);
333 if (bytes_read != 0) {
334 *err = WTAP_ERR_SHORT_READ;
339 wth->data_offset += hdr_size;
341 switch (netmon->version_major) {
344 packet_size = pletohs(&hdr.hdr_1_x.incl_len);
348 packet_size = pletohl(&hdr.hdr_2_x.incl_len);
351 if (packet_size > WTAP_MAX_PACKET_SIZE) {
353 * Probably a corrupt capture file; don't blow up trying
354 * to allocate space for an immensely-large packet.
356 g_message("netmon: File has %u-byte packet, bigger than maximum of %u",
357 packet_size, WTAP_MAX_PACKET_SIZE);
358 *err = WTAP_ERR_BAD_RECORD;
361 buffer_assure_space(wth->frame_buffer, packet_size);
362 data_offset = wth->data_offset;
363 errno = WTAP_ERR_CANT_READ;
364 bytes_read = file_read(buffer_start_ptr(wth->frame_buffer), 1,
365 packet_size, wth->fh);
367 if (bytes_read != packet_size) {
368 *err = file_error(wth->fh);
370 *err = WTAP_ERR_SHORT_READ;
373 wth->data_offset += packet_size;
375 t = (double)netmon->start_usecs;
376 switch (netmon->version_major) {
379 t += ((double)pletohl(&hdr.hdr_1_x.ts_delta))*1000;
383 t += (double)pletohl(&hdr.hdr_2_x.ts_delta_lo)
384 + (double)pletohl(&hdr.hdr_2_x.ts_delta_hi)*4294967296.0;
387 secs = (time_t)(t/1000000);
388 usecs = (guint32)(t - secs*1000000);
389 wth->phdr.ts.tv_sec = netmon->start_secs + secs;
390 wth->phdr.ts.tv_usec = usecs;
391 wth->phdr.caplen = packet_size;
392 switch (netmon->version_major) {
395 wth->phdr.len = pletohs(&hdr.hdr_1_x.orig_len);
399 wth->phdr.len = pletohl(&hdr.hdr_2_x.orig_len);
402 wth->phdr.pkt_encap = wth->file_encap;
408 netmon_close(wtap *wth)
410 if (wth->capture.netmon->frame_table != NULL)
411 g_free(wth->capture.netmon->frame_table);
412 g_free(wth->capture.netmon);
415 static const int wtap_encap[] = {
416 -1, /* WTAP_ENCAP_UNKNOWN -> unsupported */
417 1, /* WTAP_ENCAP_ETHERNET -> NDIS Ethernet */
418 2, /* WTAP_ENCAP_TR -> NDIS Token Ring */
419 -1, /* WTAP_ENCAP_SLIP -> unsupported */
420 -1, /* WTAP_ENCAP_PPP -> unsupported */
421 3, /* WTAP_ENCAP_FDDI -> NDIS FDDI */
422 3, /* WTAP_ENCAP_FDDI_BITSWAPPED -> NDIS FDDI */
423 -1, /* WTAP_ENCAP_RAW_IP -> unsupported */
424 -1, /* WTAP_ENCAP_ARCNET -> unsupported */
425 -1, /* WTAP_ENCAP_ATM_RFC1483 -> unsupported */
426 -1, /* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */
427 -1, /* WTAP_ENCAP_LAPB -> unsupported*/
428 -1, /* WTAP_ENCAP_ATM_SNIFFER -> unsupported */
429 -1 /* WTAP_ENCAP_NULL -> unsupported */
431 #define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
433 /* Returns 0 if we could write the specified encapsulation type,
434 an error indication otherwise. */
435 int netmon_dump_can_write_encap(int filetype, int encap)
437 /* Per-packet encapsulations aren't supported. */
438 if (encap == WTAP_ENCAP_PER_PACKET)
439 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
441 if (encap < 0 || encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
442 return WTAP_ERR_UNSUPPORTED_ENCAP;
447 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
449 gboolean netmon_dump_open(wtap_dumper *wdh, int *err)
451 /* This is a netmon file */
452 wdh->subtype_write = netmon_dump;
453 wdh->subtype_close = netmon_dump_close;
455 /* We can't fill in all the fields in the file header, as we
456 haven't yet written any packets. As we'll have to rewrite
457 the header when we've written out all the packets, we just
458 skip over the header for now. */
459 fseek(wdh->fh, CAPTUREFILE_HEADER_SIZE, SEEK_SET);
461 wdh->dump.netmon = g_malloc(sizeof(netmon_dump_t));
462 wdh->dump.netmon->frame_table_offset = CAPTUREFILE_HEADER_SIZE;
463 wdh->dump.netmon->got_first_record_time = FALSE;
464 wdh->dump.netmon->frame_table = NULL;
465 wdh->dump.netmon->frame_table_index = 0;
466 wdh->dump.netmon->frame_table_size = 0;
471 /* Write a record for a packet to a dump file.
472 Returns TRUE on success, FALSE on failure. */
473 static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
474 const union wtap_pseudo_header *pseudo_header, const u_char *pd, int *err)
476 netmon_dump_t *netmon = wdh->dump.netmon;
477 struct netmonrec_1_x_hdr rec_1_x_hdr;
478 struct netmonrec_2_x_hdr rec_2_x_hdr;
483 /* NetMon files have a capture start time in the file header,
484 and have times relative to that in the packet headers;
485 pick the time of the first packet as the capture start
487 if (!netmon->got_first_record_time) {
488 netmon->first_record_time = phdr->ts;
489 netmon->got_first_record_time = TRUE;
492 switch (wdh->file_type) {
494 case WTAP_FILE_NETMON_1_x:
495 rec_1_x_hdr.ts_delta = htolel(
496 (phdr->ts.tv_sec - netmon->first_record_time.tv_sec)*1000
497 + (phdr->ts.tv_usec - netmon->first_record_time.tv_usec + 500)/1000);
498 rec_1_x_hdr.orig_len = htoles(phdr->len);
499 rec_1_x_hdr.incl_len = htoles(phdr->caplen);
500 hdrp = (char *)&rec_1_x_hdr;
501 hdr_size = sizeof rec_1_x_hdr;
504 case WTAP_FILE_NETMON_2_x:
505 /* XXX - fill in 64-bit time diff in microseconds */
506 rec_2_x_hdr.orig_len = htolel(phdr->len);
507 rec_2_x_hdr.incl_len = htolel(phdr->caplen);
508 hdrp = (char *)&rec_2_x_hdr;
509 hdr_size = sizeof rec_2_x_hdr;
513 /* We should never get here - our open routine
514 should only get called for the types above. */
515 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
519 nwritten = fwrite(hdrp, 1, hdr_size, wdh->fh);
520 if (nwritten != hdr_size) {
524 *err = WTAP_ERR_SHORT_WRITE;
527 nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
528 if (nwritten != phdr->caplen) {
532 *err = WTAP_ERR_SHORT_WRITE;
537 * Stash the file offset of this frame.
539 if (netmon->frame_table_size == 0) {
541 * Haven't yet allocated the buffer for the frame table.
543 netmon->frame_table = g_malloc(1024 * sizeof *netmon->frame_table);
544 netmon->frame_table_size = 1024;
547 * We've allocated it; are we at the end?
549 if (netmon->frame_table_index >= netmon->frame_table_size) {
551 * Yes - double the size of the frame table.
553 netmon->frame_table_size *= 2;
554 netmon->frame_table = g_realloc(netmon->frame_table,
555 netmon->frame_table_size * sizeof *netmon->frame_table);
558 netmon->frame_table[netmon->frame_table_index] =
559 htolel(netmon->frame_table_offset);
560 netmon->frame_table_index++;
561 netmon->frame_table_offset += hdr_size + phdr->caplen;
566 /* Finish writing to a dump file.
567 Returns TRUE on success, FALSE on failure. */
568 static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
570 netmon_dump_t *netmon = wdh->dump.netmon;
573 struct netmon_hdr file_hdr;
578 /* Write out the frame table. "netmon->frame_table_index" is
579 the number of entries we've put into it. */
580 n_to_write = netmon->frame_table_index * sizeof *netmon->frame_table;
581 nwritten = fwrite(netmon->frame_table, 1, n_to_write, wdh->fh);
582 if (nwritten != n_to_write) {
586 *err = WTAP_ERR_SHORT_WRITE;
590 /* Now go fix up the file header. */
591 fseek(wdh->fh, 0, SEEK_SET);
592 memset(&file_hdr, '\0', sizeof file_hdr);
593 switch (wdh->file_type) {
595 case WTAP_FILE_NETMON_1_x:
596 magicp = netmon_1_x_magic;
597 magic_size = sizeof netmon_1_x_magic;
598 /* current NetMon version, for 1.x, is 1.1 */
599 file_hdr.ver_minor = 1;
600 file_hdr.ver_major = 1;
603 case WTAP_FILE_NETMON_2_x:
604 magicp = netmon_2_x_magic;
605 magic_size = sizeof netmon_2_x_magic;
606 /* XXX - fill in V2 stuff. */
610 /* We should never get here - our open routine
611 should only get called for the types above. */
612 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
615 nwritten = fwrite(magicp, 1, magic_size, wdh->fh);
616 if (nwritten != magic_size) {
620 *err = WTAP_ERR_SHORT_WRITE;
624 file_hdr.network = htoles(wtap_encap[wdh->encap]);
625 tm = localtime(&netmon->first_record_time.tv_sec);
626 file_hdr.ts_year = htoles(1900 + tm->tm_year);
627 file_hdr.ts_month = htoles(tm->tm_mon + 1);
628 file_hdr.ts_dow = htoles(tm->tm_wday);
629 file_hdr.ts_day = htoles(tm->tm_mday);
630 file_hdr.ts_hour = htoles(tm->tm_hour);
631 file_hdr.ts_min = htoles(tm->tm_min);
632 file_hdr.ts_sec = htoles(tm->tm_sec);
633 file_hdr.ts_msec = htoles(netmon->first_record_time.tv_usec/1000);
634 /* XXX - what about rounding? */
635 file_hdr.frametableoffset = htolel(netmon->frame_table_offset);
636 file_hdr.frametablelength =
637 htolel(netmon->frame_table_index * sizeof *netmon->frame_table);
638 nwritten = fwrite(&file_hdr, 1, sizeof file_hdr, wdh->fh);
639 if (nwritten != sizeof file_hdr) {
643 *err = WTAP_ERR_SHORT_WRITE;