3 * $Id: netmon.c,v 1.25 2000/02/19 08:00:05 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 gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
99 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 guint32 frame_table_length;
125 guint32 first_frame_table_entry;
127 /* Read in the string that should be at the start of a Network
129 file_seek(wth->fh, 0, SEEK_SET);
130 errno = WTAP_ERR_CANT_READ;
131 bytes_read = file_read(magic, 1, sizeof magic, wth->fh);
132 if (bytes_read != sizeof magic) {
133 *err = file_error(wth->fh);
139 if (memcmp(magic, netmon_1_x_magic, sizeof netmon_1_x_magic) != 0
140 && memcmp(magic, netmon_2_x_magic, sizeof netmon_1_x_magic) != 0) {
144 /* Read the rest of the header. */
145 errno = WTAP_ERR_CANT_READ;
146 bytes_read = file_read(&hdr, 1, sizeof hdr, wth->fh);
147 if (bytes_read != sizeof hdr) {
148 *err = file_error(wth->fh);
154 switch (hdr.ver_major) {
157 file_type = WTAP_FILE_NETMON_1_x;
161 file_type = WTAP_FILE_NETMON_2_x;
165 g_message("netmon: major version %u unsupported", hdr.ver_major);
166 *err = WTAP_ERR_UNSUPPORTED;
170 hdr.network = pletohs(&hdr.network);
171 if (hdr.network >= NUM_NETMON_ENCAPS
172 || netmon_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
173 g_message("netmon: network type %u unknown or unsupported",
175 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
179 /* This is a netmon file */
180 wth->file_type = file_type;
181 wth->capture.netmon = g_malloc(sizeof(netmon_t));
182 wth->subtype_read = netmon_read;
183 wth->file_encap = netmon_encap[hdr.network];
184 wth->snapshot_length = 16384; /* XXX - not available in header */
186 * Convert the time stamp to a "time_t" and a number of
189 tm.tm_year = pletohs(&hdr.ts_year) - 1900;
190 tm.tm_mon = pletohs(&hdr.ts_month) - 1;
191 tm.tm_mday = pletohs(&hdr.ts_day);
192 tm.tm_hour = pletohs(&hdr.ts_hour);
193 tm.tm_min = pletohs(&hdr.ts_min);
194 tm.tm_sec = pletohs(&hdr.ts_sec);
196 wth->capture.netmon->start_secs = mktime(&tm);
198 * XXX - what if "secs" is -1? Unlikely, but if the capture was
199 * done in a time zone that switches between standard and summer
200 * time sometime other than when we do, and thus the time was one
201 * that doesn't exist here because a switch from standard to summer
202 * time zips over it, it could happen.
204 * On the other hand, if the capture was done in a different time
205 * zone, this won't work right anyway; unfortunately, the time
206 * zone isn't stored in the capture file (why the hell didn't
207 * they stuff a FILETIME, which is the number of 100-nanosecond
208 * intervals since 1601-01-01 00:00:00 "UTC", there, instead
209 * of stuffing a SYSTEMTIME, which is time-zone-dependent, there?).
211 wth->capture.netmon->start_usecs = pletohs(&hdr.ts_msec)*1000;
213 wth->capture.netmon->version_major = hdr.ver_major;
216 * The "frame index table" appears to come after the last
217 * packet; remember its offset, so we know when we have no
218 * more packets to read.
220 wth->capture.netmon->end_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.
225 * So we read the first entry from the frame table, and
226 * use that as the offset of the first packet.
228 * First, make sure the frame table has at least one entry
231 frame_table_length = pletohl(&hdr.frametablelength);
232 if (frame_table_length < sizeof first_frame_table_entry) {
233 g_message("netmon: frame table length is %u, which means it's less than one entry in size",
235 *err = WTAP_ERR_UNSUPPORTED;
240 * Now read that entry. (It appears that the N+1st frame immediately
241 * follows the Nth frame, so we don't need any entries after the
244 errno = WTAP_ERR_CANT_READ;
245 file_seek(wth->fh, wth->capture.netmon->end_offset, SEEK_SET);
246 bytes_read = file_read(&first_frame_table_entry, 1,
247 sizeof first_frame_table_entry, wth->fh);
248 if (bytes_read != sizeof first_frame_table_entry) {
249 *err = file_error(wth->fh);
255 /* Seek to the beginning of the data records. */
256 wth->data_offset = pletohl(&first_frame_table_entry);
257 file_seek(wth->fh, wth->data_offset, SEEK_SET);
262 /* Read the next packet */
263 static int netmon_read(wtap *wth, int *err)
265 guint32 packet_size = 0;
268 struct netmonrec_1_x_hdr hdr_1_x;
269 struct netmonrec_2_x_hdr hdr_2_x;
277 /* Have we reached the end of the packet data? */
278 if (wth->data_offset >= wth->capture.netmon->end_offset) {
282 /* Read record header. */
283 switch (wth->capture.netmon->version_major) {
286 hdr_size = sizeof (struct netmonrec_1_x_hdr);
290 hdr_size = sizeof (struct netmonrec_2_x_hdr);
293 errno = WTAP_ERR_CANT_READ;
294 bytes_read = file_read(&hdr, 1, hdr_size, wth->fh);
295 if (bytes_read != hdr_size) {
296 *err = file_error(wth->fh);
299 if (bytes_read != 0) {
300 *err = WTAP_ERR_SHORT_READ;
305 wth->data_offset += hdr_size;
307 switch (wth->capture.netmon->version_major) {
310 packet_size = pletohs(&hdr.hdr_1_x.incl_len);
314 packet_size = pletohl(&hdr.hdr_2_x.incl_len);
317 if (packet_size > WTAP_MAX_PACKET_SIZE) {
319 * Probably a corrupt capture file; don't blow up trying
320 * to allocate space for an immensely-large packet.
322 g_message("netmon: File has %u-byte packet, bigger than maximum of %u",
323 packet_size, WTAP_MAX_PACKET_SIZE);
324 *err = WTAP_ERR_BAD_RECORD;
327 buffer_assure_space(wth->frame_buffer, packet_size);
328 data_offset = wth->data_offset;
329 errno = WTAP_ERR_CANT_READ;
330 bytes_read = file_read(buffer_start_ptr(wth->frame_buffer), 1,
331 packet_size, wth->fh);
333 if (bytes_read != packet_size) {
334 *err = file_error(wth->fh);
336 *err = WTAP_ERR_SHORT_READ;
339 wth->data_offset += packet_size;
341 t = (double)wth->capture.netmon->start_usecs;
342 switch (wth->capture.netmon->version_major) {
345 t += ((double)pletohl(&hdr.hdr_1_x.ts_delta))*1000;
349 t += (double)pletohl(&hdr.hdr_2_x.ts_delta_lo)
350 + (double)pletohl(&hdr.hdr_2_x.ts_delta_hi)*4294967296.0;
353 secs = (time_t)(t/1000000);
354 usecs = (guint32)(t - secs*1000000);
355 wth->phdr.ts.tv_sec = wth->capture.netmon->start_secs + secs;
356 wth->phdr.ts.tv_usec = usecs;
357 wth->phdr.caplen = packet_size;
358 switch (wth->capture.netmon->version_major) {
361 wth->phdr.len = pletohs(&hdr.hdr_1_x.orig_len);
365 wth->phdr.len = pletohl(&hdr.hdr_2_x.orig_len);
368 wth->phdr.pkt_encap = wth->file_encap;
373 static const int wtap_encap[] = {
374 -1, /* WTAP_ENCAP_UNKNOWN -> unsupported */
375 1, /* WTAP_ENCAP_ETHERNET -> NDIS Ethernet */
376 2, /* WTAP_ENCAP_TR -> NDIS Token Ring */
377 -1, /* WTAP_ENCAP_SLIP -> unsupported */
378 -1, /* WTAP_ENCAP_PPP -> unsupported */
379 3, /* WTAP_ENCAP_FDDI -> NDIS FDDI */
380 3, /* WTAP_ENCAP_FDDI_BITSWAPPED -> NDIS FDDI */
381 -1, /* WTAP_ENCAP_RAW_IP -> unsupported */
382 -1, /* WTAP_ENCAP_ARCNET -> unsupported */
383 -1, /* WTAP_ENCAP_ATM_RFC1483 -> unsupported */
384 -1, /* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */
385 -1, /* WTAP_ENCAP_LAPB -> unsupported*/
386 -1, /* WTAP_ENCAP_ATM_SNIFFER -> unsupported */
387 -1 /* WTAP_ENCAP_NULL -> unsupported */
389 #define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
391 /* Returns 0 if we could write the specified encapsulation type,
392 an error indication otherwise. */
393 int netmon_dump_can_write_encap(int filetype, int encap)
395 /* Per-packet encapsulations aren't supported. */
396 if (encap == WTAP_ENCAP_PER_PACKET)
397 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
399 if (encap < 0 || encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
400 return WTAP_ERR_UNSUPPORTED_ENCAP;
405 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
407 gboolean netmon_dump_open(wtap_dumper *wdh, int *err)
409 /* This is a netmon file */
410 wdh->subtype_write = netmon_dump;
411 wdh->subtype_close = netmon_dump_close;
413 /* We can't fill in all the fields in the file header, as we
414 haven't yet written any packets. As we'll have to rewrite
415 the header when we've written out all the packets, we just
416 skip over the header for now. */
417 fseek(wdh->fh, CAPTUREFILE_HEADER_SIZE, SEEK_SET);
419 wdh->private.netmon = g_malloc(sizeof(netmon_dump_t));
420 wdh->private.netmon->frame_table_offset = CAPTUREFILE_HEADER_SIZE;
421 wdh->private.netmon->got_first_record_time = FALSE;
422 wdh->private.netmon->frame_table = NULL;
423 wdh->private.netmon->frame_table_index = 0;
424 wdh->private.netmon->frame_table_size = 0;
429 /* Write a record for a packet to a dump file.
430 Returns TRUE on success, FALSE on failure. */
431 static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
432 const u_char *pd, int *err)
434 netmon_dump_t *priv = wdh->private.netmon;
435 struct netmonrec_1_x_hdr rec_1_x_hdr;
436 struct netmonrec_2_x_hdr rec_2_x_hdr;
441 /* NetMon files have a capture start time in the file header,
442 and have times relative to that in the packet headers;
443 pick the time of the first packet as the capture start
445 if (!priv->got_first_record_time) {
446 priv->first_record_time = phdr->ts;
447 priv->got_first_record_time = TRUE;
450 switch (wdh->file_type) {
452 case WTAP_FILE_NETMON_1_x:
453 rec_1_x_hdr.ts_delta = htolel(
454 (phdr->ts.tv_sec - priv->first_record_time.tv_sec)*1000
455 + (phdr->ts.tv_usec - priv->first_record_time.tv_usec + 500)/1000);
456 rec_1_x_hdr.orig_len = htoles(phdr->len);
457 rec_1_x_hdr.incl_len = htoles(phdr->caplen);
458 hdrp = (char *)&rec_1_x_hdr;
459 hdr_size = sizeof rec_1_x_hdr;
462 case WTAP_FILE_NETMON_2_x:
463 /* XXX - fill in 64-bit time diff in microseconds */
464 rec_2_x_hdr.orig_len = htolel(phdr->len);
465 rec_2_x_hdr.incl_len = htolel(phdr->caplen);
466 hdrp = (char *)&rec_2_x_hdr;
467 hdr_size = sizeof rec_2_x_hdr;
471 /* We should never get here - our open routine
472 should only get called for the types above. */
473 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
477 nwritten = fwrite(hdrp, 1, hdr_size, wdh->fh);
478 if (nwritten != hdr_size) {
482 *err = WTAP_ERR_SHORT_WRITE;
485 nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
486 if (nwritten != phdr->caplen) {
490 *err = WTAP_ERR_SHORT_WRITE;
495 * Stash the file offset of this frame.
497 if (priv->frame_table_size == 0) {
499 * Haven't yet allocated the buffer for the frame table.
501 priv->frame_table = g_malloc(1024 * sizeof *priv->frame_table);
502 priv->frame_table_size = 1024;
505 * We've allocated it; are we at the end?
507 if (priv->frame_table_index >= priv->frame_table_size) {
509 * Yes - double the size of the frame table.
511 priv->frame_table_size *= 2;
512 priv->frame_table = g_realloc(priv->frame_table,
513 priv->frame_table_size * sizeof *priv->frame_table);
516 priv->frame_table[priv->frame_table_index] =
517 htolel(priv->frame_table_offset);
518 priv->frame_table_index++;
519 priv->frame_table_offset += hdr_size + phdr->caplen;
524 /* Finish writing to a dump file.
525 Returns TRUE on success, FALSE on failure. */
526 static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
528 netmon_dump_t *priv = wdh->private.netmon;
531 struct netmon_hdr file_hdr;
536 /* Write out the frame table. "priv->frame_table_index" is
537 the number of entries we've put into it. */
538 n_to_write = priv->frame_table_index * sizeof *priv->frame_table;
539 nwritten = fwrite(priv->frame_table, 1, n_to_write, wdh->fh);
540 if (nwritten != n_to_write) {
544 *err = WTAP_ERR_SHORT_WRITE;
548 /* Now go fix up the file header. */
549 fseek(wdh->fh, 0, SEEK_SET);
550 memset(&file_hdr, '\0', sizeof file_hdr);
551 switch (wdh->file_type) {
553 case WTAP_FILE_NETMON_1_x:
554 magicp = netmon_1_x_magic;
555 magic_size = sizeof netmon_1_x_magic;
556 /* current NetMon version, for 1.x, is 1.1 */
557 file_hdr.ver_minor = 1;
558 file_hdr.ver_major = 1;
561 case WTAP_FILE_NETMON_2_x:
562 magicp = netmon_2_x_magic;
563 magic_size = sizeof netmon_2_x_magic;
564 /* XXX - fill in V2 stuff. */
568 /* We should never get here - our open routine
569 should only get called for the types above. */
570 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
573 nwritten = fwrite(magicp, 1, magic_size, wdh->fh);
574 if (nwritten != magic_size) {
578 *err = WTAP_ERR_SHORT_WRITE;
582 file_hdr.network = htoles(wtap_encap[wdh->encap]);
583 tm = localtime(&priv->first_record_time.tv_sec);
584 file_hdr.ts_year = htoles(1900 + tm->tm_year);
585 file_hdr.ts_month = htoles(tm->tm_mon + 1);
586 file_hdr.ts_dow = htoles(tm->tm_wday);
587 file_hdr.ts_day = htoles(tm->tm_mday);
588 file_hdr.ts_hour = htoles(tm->tm_hour);
589 file_hdr.ts_min = htoles(tm->tm_min);
590 file_hdr.ts_sec = htoles(tm->tm_sec);
591 file_hdr.ts_msec = htoles(priv->first_record_time.tv_usec/1000);
592 /* XXX - what about rounding? */
593 file_hdr.frametableoffset = htolel(priv->frame_table_offset);
594 file_hdr.frametablelength =
595 htolel(priv->frame_table_index * sizeof *priv->frame_table);
596 nwritten = fwrite(&file_hdr, 1, sizeof file_hdr, wdh->fh);
597 if (nwritten != sizeof file_hdr) {
601 *err = WTAP_ERR_SHORT_WRITE;