3 * $Id: netmon.c,v 1.26 2000/03/22 07:06:54 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.
30 #include "file_wrappers.h"
36 * ftp://ftp.microsoft.com/developr/drg/cifs/cifs/Bhfile.zip
38 * contains "STRUCT.H", which declares the typedef CAPTUREFILE_HEADER
39 * for the header of a Microsoft Network Monitor capture file.
42 /* Capture file header, *including* magic number, is padded to 128 bytes. */
43 #define CAPTUREFILE_HEADER_SIZE 128
45 /* Magic number in Network Monitor 1.x files. */
46 static const char netmon_1_x_magic[] = {
50 /* Magic number in Network Monitor 2.x files. */
51 static const char netmon_2_x_magic[] = {
55 /* Network Monitor file header (minus magic number). */
57 guint8 ver_minor; /* minor version number */
58 guint8 ver_major; /* major version number */
59 guint16 network; /* network type */
60 guint16 ts_year; /* year of capture start */
61 guint16 ts_month; /* month of capture start (January = 1) */
62 guint16 ts_dow; /* day of week of capture start (Sun = 0) */
63 guint16 ts_day; /* day of month of capture start */
64 guint16 ts_hour; /* hour of capture start */
65 guint16 ts_min; /* minute of capture start */
66 guint16 ts_sec; /* second of capture start */
67 guint16 ts_msec; /* millisecond of capture start */
68 guint32 frametableoffset; /* frame index table offset */
69 guint32 frametablelength; /* frame index table size */
70 guint32 userdataoffset; /* user data offset */
71 guint32 userdatalength; /* user data size */
72 guint32 commentdataoffset; /* comment data offset */
73 guint32 commentdatalength; /* comment data size */
74 guint32 statisticsoffset; /* offset to statistics structure */
75 guint32 statisticslength; /* length of statistics structure */
76 guint32 networkinfooffset; /* offset to network info structure */
77 guint32 networkinfolength; /* length of network info structure */
80 /* Network Monitor 1.x record header; not defined in STRUCT.H, but deduced by
81 * looking at capture files. */
82 struct netmonrec_1_x_hdr {
83 guint32 ts_delta; /* time stamp - msecs since start of capture */
84 guint16 orig_len; /* actual length of packet */
85 guint16 incl_len; /* number of octets captured in file */
88 /* Network Monitor 2.x record header; not defined in STRUCT.H, but deduced by
89 * looking at capture files. */
90 struct netmonrec_2_x_hdr {
91 guint32 ts_delta_lo; /* time stamp - usecs since start of capture */
92 guint32 ts_delta_hi; /* time stamp - usecs since start of capture */
93 guint32 orig_len; /* actual length of packet */
94 guint32 incl_len; /* number of octets captured in file */
97 static int netmon_read(wtap *wth, int *err);
98 static void netmon_close(wtap *wth);
99 static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
100 const u_char *pd, int *err);
101 static gboolean netmon_dump_close(wtap_dumper *wdh, int *err);
103 int netmon_open(wtap *wth, int *err)
106 char magic[sizeof netmon_1_x_magic];
107 struct netmon_hdr hdr;
109 static const int netmon_encap[] = {
113 WTAP_ENCAP_FDDI_BITSWAPPED,
114 WTAP_ENCAP_UNKNOWN, /* WAN */
115 WTAP_ENCAP_UNKNOWN, /* LocalTalk */
116 WTAP_ENCAP_UNKNOWN, /* "DIX" - should not occur */
117 WTAP_ENCAP_UNKNOWN, /* ARCNET raw */
118 WTAP_ENCAP_UNKNOWN, /* ARCNET 878.2 */
119 WTAP_ENCAP_UNKNOWN, /* ATM */
120 WTAP_ENCAP_UNKNOWN, /* Wireless WAN */
121 WTAP_ENCAP_UNKNOWN /* IrDA */
123 #define NUM_NETMON_ENCAPS (sizeof netmon_encap / sizeof netmon_encap[0])
125 int frame_table_offset;
126 guint32 frame_table_length;
128 /* Read in the string that should be at the start of a Network
130 file_seek(wth->fh, 0, SEEK_SET);
131 errno = WTAP_ERR_CANT_READ;
132 bytes_read = file_read(magic, 1, sizeof magic, wth->fh);
133 if (bytes_read != sizeof magic) {
134 *err = file_error(wth->fh);
140 if (memcmp(magic, netmon_1_x_magic, sizeof netmon_1_x_magic) != 0
141 && memcmp(magic, netmon_2_x_magic, sizeof netmon_1_x_magic) != 0) {
145 /* Read the rest of the header. */
146 errno = WTAP_ERR_CANT_READ;
147 bytes_read = file_read(&hdr, 1, sizeof hdr, wth->fh);
148 if (bytes_read != sizeof hdr) {
149 *err = file_error(wth->fh);
155 switch (hdr.ver_major) {
158 file_type = WTAP_FILE_NETMON_1_x;
162 file_type = WTAP_FILE_NETMON_2_x;
166 g_message("netmon: major version %u unsupported", hdr.ver_major);
167 *err = WTAP_ERR_UNSUPPORTED;
171 hdr.network = pletohs(&hdr.network);
172 if (hdr.network >= NUM_NETMON_ENCAPS
173 || netmon_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
174 g_message("netmon: network type %u unknown or unsupported",
176 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
180 /* This is a netmon file */
181 wth->file_type = file_type;
182 wth->capture.netmon = g_malloc(sizeof(netmon_t));
183 wth->subtype_read = netmon_read;
184 wth->subtype_close = netmon_close;
185 wth->file_encap = netmon_encap[hdr.network];
186 wth->snapshot_length = 16384; /* XXX - not available in header */
188 * Convert the time stamp to a "time_t" and a number of
191 tm.tm_year = pletohs(&hdr.ts_year) - 1900;
192 tm.tm_mon = pletohs(&hdr.ts_month) - 1;
193 tm.tm_mday = pletohs(&hdr.ts_day);
194 tm.tm_hour = pletohs(&hdr.ts_hour);
195 tm.tm_min = pletohs(&hdr.ts_min);
196 tm.tm_sec = pletohs(&hdr.ts_sec);
198 wth->capture.netmon->start_secs = mktime(&tm);
200 * XXX - what if "secs" is -1? Unlikely, but if the capture was
201 * done in a time zone that switches between standard and summer
202 * time sometime other than when we do, and thus the time was one
203 * that doesn't exist here because a switch from standard to summer
204 * time zips over it, it could happen.
206 * On the other hand, if the capture was done in a different time
207 * zone, this won't work right anyway; unfortunately, the time
208 * zone isn't stored in the capture file (why the hell didn't
209 * they stuff a FILETIME, which is the number of 100-nanosecond
210 * intervals since 1601-01-01 00:00:00 "UTC", there, instead
211 * of stuffing a SYSTEMTIME, which is time-zone-dependent, there?).
213 wth->capture.netmon->start_usecs = pletohs(&hdr.ts_msec)*1000;
215 wth->capture.netmon->version_major = hdr.ver_major;
218 * Get the offset of the frame index table.
220 frame_table_offset = pletohl(&hdr.frametableoffset);
223 * It appears that some NetMon 2.x files don't have the
224 * first packet starting exactly 128 bytes into the file.
226 * Furthermore, it also appears that there are "holes" in
227 * the file, i.e. frame N+1 doesn't always follow immediately
230 * Therefore, we must read the frame table, and use the offsets
231 * in it as the offsets of the frames.
233 frame_table_length = pletohl(&hdr.frametablelength);
234 wth->capture.netmon->frame_table_size = frame_table_length / sizeof (guint32);
235 if ((wth->capture.netmon->frame_table_size * sizeof (guint32)) != frame_table_length) {
236 g_message("netmon: frame table length is %u, which is not a multiple of the size of an entry",
238 *err = WTAP_ERR_UNSUPPORTED;
241 if (wth->capture.netmon->frame_table_size == 0) {
242 g_message("netmon: frame table length is %u, which means it's less than one entry in size",
244 *err = WTAP_ERR_UNSUPPORTED;
247 wth->capture.netmon->frame_table = g_malloc(frame_table_length);
248 errno = WTAP_ERR_CANT_READ;
249 file_seek(wth->fh, frame_table_offset, SEEK_SET);
250 bytes_read = file_read(wth->capture.netmon->frame_table, 1,
251 frame_table_length, wth->fh);
252 if (bytes_read != frame_table_length) {
253 *err = file_error(wth->fh);
259 /* Set up to start reading at the first frame. */
260 wth->capture.netmon->current_frame = 0;
265 /* Read the next packet */
266 static int netmon_read(wtap *wth, int *err)
268 netmon_t *netmon = wth->capture.netmon;
269 guint32 packet_size = 0;
272 struct netmonrec_1_x_hdr hdr_1_x;
273 struct netmonrec_2_x_hdr hdr_2_x;
281 /* Have we reached the end of the packet data? */
282 if (netmon->current_frame >= netmon->frame_table_size) {
287 /* Seek to the beginning of the current record, if we're
288 not there already (seeking to the current position
289 may still cause a seek and a read of the underlying file,
290 so we don't want to do it unconditionally). */
291 data_offset = netmon->frame_table[netmon->current_frame];
292 if (wth->data_offset != data_offset) {
293 wth->data_offset = data_offset;
294 file_seek(wth->fh, wth->data_offset, SEEK_SET);
296 netmon->current_frame++;
298 /* Read record header. */
299 switch (netmon->version_major) {
302 hdr_size = sizeof (struct netmonrec_1_x_hdr);
306 hdr_size = sizeof (struct netmonrec_2_x_hdr);
309 errno = WTAP_ERR_CANT_READ;
311 bytes_read = file_read(&hdr, 1, hdr_size, wth->fh);
312 if (bytes_read != hdr_size) {
313 *err = file_error(wth->fh);
316 if (bytes_read != 0) {
317 *err = WTAP_ERR_SHORT_READ;
322 wth->data_offset += hdr_size;
324 switch (netmon->version_major) {
327 packet_size = pletohs(&hdr.hdr_1_x.incl_len);
331 packet_size = pletohl(&hdr.hdr_2_x.incl_len);
334 if (packet_size > WTAP_MAX_PACKET_SIZE) {
336 * Probably a corrupt capture file; don't blow up trying
337 * to allocate space for an immensely-large packet.
339 g_message("netmon: File has %u-byte packet, bigger than maximum of %u",
340 packet_size, WTAP_MAX_PACKET_SIZE);
341 *err = WTAP_ERR_BAD_RECORD;
344 buffer_assure_space(wth->frame_buffer, packet_size);
345 data_offset = wth->data_offset;
346 errno = WTAP_ERR_CANT_READ;
347 bytes_read = file_read(buffer_start_ptr(wth->frame_buffer), 1,
348 packet_size, wth->fh);
350 if (bytes_read != packet_size) {
351 *err = file_error(wth->fh);
353 *err = WTAP_ERR_SHORT_READ;
356 wth->data_offset += packet_size;
358 t = (double)netmon->start_usecs;
359 switch (netmon->version_major) {
362 t += ((double)pletohl(&hdr.hdr_1_x.ts_delta))*1000;
366 t += (double)pletohl(&hdr.hdr_2_x.ts_delta_lo)
367 + (double)pletohl(&hdr.hdr_2_x.ts_delta_hi)*4294967296.0;
370 secs = (time_t)(t/1000000);
371 usecs = (guint32)(t - secs*1000000);
372 wth->phdr.ts.tv_sec = netmon->start_secs + secs;
373 wth->phdr.ts.tv_usec = usecs;
374 wth->phdr.caplen = packet_size;
375 switch (netmon->version_major) {
378 wth->phdr.len = pletohs(&hdr.hdr_1_x.orig_len);
382 wth->phdr.len = pletohl(&hdr.hdr_2_x.orig_len);
385 wth->phdr.pkt_encap = wth->file_encap;
391 netmon_close(wtap *wth)
393 g_free(wth->capture.netmon->frame_table);
394 g_free(wth->capture.netmon);
397 static const int wtap_encap[] = {
398 -1, /* WTAP_ENCAP_UNKNOWN -> unsupported */
399 1, /* WTAP_ENCAP_ETHERNET -> NDIS Ethernet */
400 2, /* WTAP_ENCAP_TR -> NDIS Token Ring */
401 -1, /* WTAP_ENCAP_SLIP -> unsupported */
402 -1, /* WTAP_ENCAP_PPP -> unsupported */
403 3, /* WTAP_ENCAP_FDDI -> NDIS FDDI */
404 3, /* WTAP_ENCAP_FDDI_BITSWAPPED -> NDIS FDDI */
405 -1, /* WTAP_ENCAP_RAW_IP -> unsupported */
406 -1, /* WTAP_ENCAP_ARCNET -> unsupported */
407 -1, /* WTAP_ENCAP_ATM_RFC1483 -> unsupported */
408 -1, /* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */
409 -1, /* WTAP_ENCAP_LAPB -> unsupported*/
410 -1, /* WTAP_ENCAP_ATM_SNIFFER -> unsupported */
411 -1 /* WTAP_ENCAP_NULL -> unsupported */
413 #define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
415 /* Returns 0 if we could write the specified encapsulation type,
416 an error indication otherwise. */
417 int netmon_dump_can_write_encap(int filetype, int encap)
419 /* Per-packet encapsulations aren't supported. */
420 if (encap == WTAP_ENCAP_PER_PACKET)
421 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
423 if (encap < 0 || encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
424 return WTAP_ERR_UNSUPPORTED_ENCAP;
429 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
431 gboolean netmon_dump_open(wtap_dumper *wdh, int *err)
433 /* This is a netmon file */
434 wdh->subtype_write = netmon_dump;
435 wdh->subtype_close = netmon_dump_close;
437 /* We can't fill in all the fields in the file header, as we
438 haven't yet written any packets. As we'll have to rewrite
439 the header when we've written out all the packets, we just
440 skip over the header for now. */
441 fseek(wdh->fh, CAPTUREFILE_HEADER_SIZE, SEEK_SET);
443 wdh->private.netmon = g_malloc(sizeof(netmon_dump_t));
444 wdh->private.netmon->frame_table_offset = CAPTUREFILE_HEADER_SIZE;
445 wdh->private.netmon->got_first_record_time = FALSE;
446 wdh->private.netmon->frame_table = NULL;
447 wdh->private.netmon->frame_table_index = 0;
448 wdh->private.netmon->frame_table_size = 0;
453 /* Write a record for a packet to a dump file.
454 Returns TRUE on success, FALSE on failure. */
455 static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
456 const u_char *pd, int *err)
458 netmon_dump_t *priv = wdh->private.netmon;
459 struct netmonrec_1_x_hdr rec_1_x_hdr;
460 struct netmonrec_2_x_hdr rec_2_x_hdr;
465 /* NetMon files have a capture start time in the file header,
466 and have times relative to that in the packet headers;
467 pick the time of the first packet as the capture start
469 if (!priv->got_first_record_time) {
470 priv->first_record_time = phdr->ts;
471 priv->got_first_record_time = TRUE;
474 switch (wdh->file_type) {
476 case WTAP_FILE_NETMON_1_x:
477 rec_1_x_hdr.ts_delta = htolel(
478 (phdr->ts.tv_sec - priv->first_record_time.tv_sec)*1000
479 + (phdr->ts.tv_usec - priv->first_record_time.tv_usec + 500)/1000);
480 rec_1_x_hdr.orig_len = htoles(phdr->len);
481 rec_1_x_hdr.incl_len = htoles(phdr->caplen);
482 hdrp = (char *)&rec_1_x_hdr;
483 hdr_size = sizeof rec_1_x_hdr;
486 case WTAP_FILE_NETMON_2_x:
487 /* XXX - fill in 64-bit time diff in microseconds */
488 rec_2_x_hdr.orig_len = htolel(phdr->len);
489 rec_2_x_hdr.incl_len = htolel(phdr->caplen);
490 hdrp = (char *)&rec_2_x_hdr;
491 hdr_size = sizeof rec_2_x_hdr;
495 /* We should never get here - our open routine
496 should only get called for the types above. */
497 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
501 nwritten = fwrite(hdrp, 1, hdr_size, wdh->fh);
502 if (nwritten != hdr_size) {
506 *err = WTAP_ERR_SHORT_WRITE;
509 nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
510 if (nwritten != phdr->caplen) {
514 *err = WTAP_ERR_SHORT_WRITE;
519 * Stash the file offset of this frame.
521 if (priv->frame_table_size == 0) {
523 * Haven't yet allocated the buffer for the frame table.
525 priv->frame_table = g_malloc(1024 * sizeof *priv->frame_table);
526 priv->frame_table_size = 1024;
529 * We've allocated it; are we at the end?
531 if (priv->frame_table_index >= priv->frame_table_size) {
533 * Yes - double the size of the frame table.
535 priv->frame_table_size *= 2;
536 priv->frame_table = g_realloc(priv->frame_table,
537 priv->frame_table_size * sizeof *priv->frame_table);
540 priv->frame_table[priv->frame_table_index] =
541 htolel(priv->frame_table_offset);
542 priv->frame_table_index++;
543 priv->frame_table_offset += hdr_size + phdr->caplen;
548 /* Finish writing to a dump file.
549 Returns TRUE on success, FALSE on failure. */
550 static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
552 netmon_dump_t *priv = wdh->private.netmon;
555 struct netmon_hdr file_hdr;
560 /* Write out the frame table. "priv->frame_table_index" is
561 the number of entries we've put into it. */
562 n_to_write = priv->frame_table_index * sizeof *priv->frame_table;
563 nwritten = fwrite(priv->frame_table, 1, n_to_write, wdh->fh);
564 if (nwritten != n_to_write) {
568 *err = WTAP_ERR_SHORT_WRITE;
572 /* Now go fix up the file header. */
573 fseek(wdh->fh, 0, SEEK_SET);
574 memset(&file_hdr, '\0', sizeof file_hdr);
575 switch (wdh->file_type) {
577 case WTAP_FILE_NETMON_1_x:
578 magicp = netmon_1_x_magic;
579 magic_size = sizeof netmon_1_x_magic;
580 /* current NetMon version, for 1.x, is 1.1 */
581 file_hdr.ver_minor = 1;
582 file_hdr.ver_major = 1;
585 case WTAP_FILE_NETMON_2_x:
586 magicp = netmon_2_x_magic;
587 magic_size = sizeof netmon_2_x_magic;
588 /* XXX - fill in V2 stuff. */
592 /* We should never get here - our open routine
593 should only get called for the types above. */
594 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
597 nwritten = fwrite(magicp, 1, magic_size, wdh->fh);
598 if (nwritten != magic_size) {
602 *err = WTAP_ERR_SHORT_WRITE;
606 file_hdr.network = htoles(wtap_encap[wdh->encap]);
607 tm = localtime(&priv->first_record_time.tv_sec);
608 file_hdr.ts_year = htoles(1900 + tm->tm_year);
609 file_hdr.ts_month = htoles(tm->tm_mon + 1);
610 file_hdr.ts_dow = htoles(tm->tm_wday);
611 file_hdr.ts_day = htoles(tm->tm_mday);
612 file_hdr.ts_hour = htoles(tm->tm_hour);
613 file_hdr.ts_min = htoles(tm->tm_min);
614 file_hdr.ts_sec = htoles(tm->tm_sec);
615 file_hdr.ts_msec = htoles(priv->first_record_time.tv_usec/1000);
616 /* XXX - what about rounding? */
617 file_hdr.frametableoffset = htolel(priv->frame_table_offset);
618 file_hdr.frametablelength =
619 htolel(priv->frame_table_index * sizeof *priv->frame_table);
620 nwritten = fwrite(&file_hdr, 1, sizeof file_hdr, wdh->fh);
621 if (nwritten != sizeof file_hdr) {
625 *err = WTAP_ERR_SHORT_WRITE;