From Joerg Mayer:
[obnox/wireshark/wip.git] / wiretap / netmon.c
1 /* netmon.c
2  *
3  * $Id: netmon.c,v 1.58 2002/07/29 06:09:59 guy Exp $
4  *
5  * Wiretap Library
6  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
7  * 
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.
12  * 
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.
17  * 
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.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include <errno.h>
27 #include <string.h>
28 #include "wtap-int.h"
29 #include "file_wrappers.h"
30 #include "buffer.h"
31 #include "netmon.h"
32 /* The file at
33  *
34  *      ftp://ftp.microsoft.com/developr/drg/cifs/cifs/Bhfile.zip
35  *
36  * contains "STRUCT.H", which declares the typedef CAPTUREFILE_HEADER
37  * for the header of a Microsoft Network Monitor capture file.
38  */
39
40 /* Capture file header, *including* magic number, is padded to 128 bytes. */
41 #define CAPTUREFILE_HEADER_SIZE 128
42
43 /* Magic number in Network Monitor 1.x files. */
44 static const char netmon_1_x_magic[] = {
45         'R', 'T', 'S', 'S'
46 };
47
48 /* Magic number in Network Monitor 2.x files. */
49 static const char netmon_2_x_magic[] = {
50         'G', 'M', 'B', 'U'
51 };
52
53 /* Network Monitor file header (minus magic number). */
54 struct netmon_hdr {
55         guint8  ver_minor;      /* minor version number */
56         guint8  ver_major;      /* major version number */
57         guint16 network;        /* network type */
58         guint16 ts_year;        /* year of capture start */
59         guint16 ts_month;       /* month of capture start (January = 1) */
60         guint16 ts_dow;         /* day of week of capture start (Sun = 0) */
61         guint16 ts_day;         /* day of month of capture start */
62         guint16 ts_hour;        /* hour of capture start */
63         guint16 ts_min;         /* minute of capture start */
64         guint16 ts_sec;         /* second of capture start */
65         guint16 ts_msec;        /* millisecond of capture start */
66         guint32 frametableoffset;       /* frame index table offset */
67         guint32 frametablelength;       /* frame index table size */
68         guint32 userdataoffset;         /* user data offset */
69         guint32 userdatalength;         /* user data size */
70         guint32 commentdataoffset;      /* comment data offset */
71         guint32 commentdatalength;      /* comment data size */
72         guint32 statisticsoffset;       /* offset to statistics structure */
73         guint32 statisticslength;       /* length of statistics structure */
74         guint32 networkinfooffset;      /* offset to network info structure */
75         guint32 networkinfolength;      /* length of network info structure */
76 };
77
78 /* Network Monitor 1.x record header; not defined in STRUCT.H, but deduced by
79  * looking at capture files. */
80 struct netmonrec_1_x_hdr {
81         guint32 ts_delta;       /* time stamp - msecs since start of capture */
82         guint16 orig_len;       /* actual length of packet */
83         guint16 incl_len;       /* number of octets captured in file */
84 };
85
86 /* Network Monitor 2.x record header; not defined in STRUCT.H, but deduced by
87  * looking at capture files. */
88 struct netmonrec_2_x_hdr {
89         guint32 ts_delta_lo;    /* time stamp - usecs since start of capture */
90         guint32 ts_delta_hi;    /* time stamp - usecs since start of capture */
91         guint32 orig_len;       /* actual length of packet */
92         guint32 incl_len;       /* number of octets captured in file */
93 };
94
95 /*
96  * The link-layer header on ATM packets.
97  */
98 struct netmon_atm_hdr {
99         guint8  dest[6];        /* "Destination address" - what is it? */
100         guint8  src[6];         /* "Source address" - what is it? */
101         guint16 vpi;            /* VPI */
102         guint16 vci;            /* VCI */
103 };
104
105 static gboolean netmon_read(wtap *wth, int *err, long *data_offset);
106 static gboolean netmon_seek_read(wtap *wth, long seek_off,
107     union wtap_pseudo_header *pseudo_header, guchar *pd, int length, int *err);
108 static gboolean netmon_read_atm_pseudoheader(FILE_T fh,
109     union wtap_pseudo_header *pseudo_header, int *err);
110 static gboolean netmon_read_rec_data(FILE_T fh, guchar *pd, int length,
111     int *err);
112 static void netmon_sequential_close(wtap *wth);
113 static void netmon_close(wtap *wth);
114 static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
115     const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err);
116 static gboolean netmon_dump_close(wtap_dumper *wdh, int *err);
117
118 int netmon_open(wtap *wth, int *err)
119 {
120         int bytes_read;
121         char magic[sizeof netmon_1_x_magic];
122         struct netmon_hdr hdr;
123         int file_type;
124         static const int netmon_encap[] = {
125                 WTAP_ENCAP_UNKNOWN,
126                 WTAP_ENCAP_ETHERNET,
127                 WTAP_ENCAP_TOKEN_RING,
128                 WTAP_ENCAP_FDDI_BITSWAPPED,
129                 WTAP_ENCAP_ATM_SNIFFER, /* NDIS WAN - this is what's used for ATM */
130                 WTAP_ENCAP_UNKNOWN,     /* NDIS LocalTalk */
131                 WTAP_ENCAP_UNKNOWN,     /* NDIS "DIX" - should not occur */
132                 WTAP_ENCAP_UNKNOWN,     /* NDIS ARCNET raw */
133                 WTAP_ENCAP_UNKNOWN,     /* NDIS ARCNET 878.2 */
134                 WTAP_ENCAP_UNKNOWN,     /* NDIS ATM (no, this is NOT used for ATM) */
135                 WTAP_ENCAP_UNKNOWN,     /* NDIS Wireless WAN */
136                 WTAP_ENCAP_UNKNOWN      /* NDIS IrDA */
137         };
138         #define NUM_NETMON_ENCAPS (sizeof netmon_encap / sizeof netmon_encap[0])
139         struct tm tm;
140         int frame_table_offset;
141         guint32 frame_table_length;
142         guint32 frame_table_size;
143         guint32 *frame_table;
144 #ifdef WORDS_BIGENDIAN
145         unsigned int i;
146 #endif
147
148         /* Read in the string that should be at the start of a Network
149          * Monitor file */
150         errno = WTAP_ERR_CANT_READ;
151         bytes_read = file_read(magic, 1, sizeof magic, wth->fh);
152         if (bytes_read != sizeof magic) {
153                 *err = file_error(wth->fh);
154                 if (*err != 0)
155                         return -1;
156                 return 0;
157         }
158
159         if (memcmp(magic, netmon_1_x_magic, sizeof netmon_1_x_magic) != 0
160          && memcmp(magic, netmon_2_x_magic, sizeof netmon_1_x_magic) != 0) {
161                 return 0;
162         }
163
164         /* Read the rest of the header. */
165         errno = WTAP_ERR_CANT_READ;
166         bytes_read = file_read(&hdr, 1, sizeof hdr, wth->fh);
167         if (bytes_read != sizeof hdr) {
168                 *err = file_error(wth->fh);
169                 if (*err != 0)
170                         return -1;
171                 return 0;
172         }
173
174         switch (hdr.ver_major) {
175
176         case 1:
177                 file_type = WTAP_FILE_NETMON_1_x;
178                 break;
179
180         case 2:
181                 file_type = WTAP_FILE_NETMON_2_x;
182                 break;
183
184         default:
185                 g_message("netmon: major version %u unsupported", hdr.ver_major);
186                 *err = WTAP_ERR_UNSUPPORTED;
187                 return -1;
188         }
189
190         hdr.network = pletohs(&hdr.network);
191         if (hdr.network >= NUM_NETMON_ENCAPS
192             || netmon_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
193                 g_message("netmon: network type %u unknown or unsupported",
194                     hdr.network);
195                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
196                 return -1;
197         }
198
199         /* This is a netmon file */
200         wth->file_type = file_type;
201         wth->capture.netmon = g_malloc(sizeof(netmon_t));
202         wth->subtype_read = netmon_read;
203         wth->subtype_seek_read = netmon_seek_read;
204         wth->subtype_sequential_close = netmon_sequential_close;
205         wth->subtype_close = netmon_close;
206         wth->file_encap = netmon_encap[hdr.network];
207         wth->snapshot_length = 0;       /* not available in header */
208         /*
209          * Convert the time stamp to a "time_t" and a number of
210          * milliseconds.
211          */
212         tm.tm_year = pletohs(&hdr.ts_year) - 1900;
213         tm.tm_mon = pletohs(&hdr.ts_month) - 1;
214         tm.tm_mday = pletohs(&hdr.ts_day);
215         tm.tm_hour = pletohs(&hdr.ts_hour);
216         tm.tm_min = pletohs(&hdr.ts_min);
217         tm.tm_sec = pletohs(&hdr.ts_sec);
218         tm.tm_isdst = -1;
219         wth->capture.netmon->start_secs = mktime(&tm);
220         /*
221          * XXX - what if "secs" is -1?  Unlikely, but if the capture was
222          * done in a time zone that switches between standard and summer
223          * time sometime other than when we do, and thus the time was one
224          * that doesn't exist here because a switch from standard to summer
225          * time zips over it, it could happen.
226          *
227          * On the other hand, if the capture was done in a different time
228          * zone, this won't work right anyway; unfortunately, the time
229          * zone isn't stored in the capture file (why the hell didn't
230          * they stuff a FILETIME, which is the number of 100-nanosecond
231          * intervals since 1601-01-01 00:00:00 "UTC", there, instead
232          * of stuffing a SYSTEMTIME, which is time-zone-dependent, there?).
233          */
234         wth->capture.netmon->start_usecs = pletohs(&hdr.ts_msec)*1000;
235
236         wth->capture.netmon->version_major = hdr.ver_major;
237
238         /*
239          * Get the offset of the frame index table.
240          */
241         frame_table_offset = pletohl(&hdr.frametableoffset);
242
243         /*
244          * It appears that some NetMon 2.x files don't have the
245          * first packet starting exactly 128 bytes into the file.
246          *
247          * Furthermore, it also appears that there are "holes" in
248          * the file, i.e. frame N+1 doesn't always follow immediately
249          * after frame N.
250          *
251          * Therefore, we must read the frame table, and use the offsets
252          * in it as the offsets of the frames.
253          */
254         frame_table_length = pletohl(&hdr.frametablelength);
255         frame_table_size = frame_table_length / sizeof (guint32);
256         if ((frame_table_size * sizeof (guint32)) != frame_table_length) {
257                 g_message("netmon: frame table length is %u, which is not a multiple of the size of an entry",
258                     frame_table_length);
259                 *err = WTAP_ERR_UNSUPPORTED;
260                 return -1;
261         }
262         if (frame_table_size == 0) {
263                 g_message("netmon: frame table length is %u, which means it's less than one entry in size",
264                     frame_table_length);
265                 *err = WTAP_ERR_UNSUPPORTED;
266                 return -1;
267         }
268         if (file_seek(wth->fh, frame_table_offset, SEEK_SET, err) == -1) {
269                 g_free(wth->capture.netmon);
270                 return -1;
271         }
272         frame_table = g_malloc(frame_table_length);
273         errno = WTAP_ERR_CANT_READ;
274         bytes_read = file_read(frame_table, 1, frame_table_length, wth->fh);
275         if ((guint32)bytes_read != frame_table_length) {
276                 *err = file_error(wth->fh);
277                 if (*err == 0)
278                         *err = WTAP_ERR_SHORT_READ;
279                 return -1;
280         }
281         wth->capture.netmon->frame_table_size = frame_table_size;
282         wth->capture.netmon->frame_table = frame_table;
283
284 #ifdef WORDS_BIGENDIAN
285         /*
286          * OK, now byte-swap the frame table.
287          */
288         for (i = 0; i < frame_table_size; i++)
289                 frame_table[i] = pletohl(&frame_table[i]);
290 #endif
291
292         /* Set up to start reading at the first frame. */
293         wth->capture.netmon->current_frame = 0;
294
295         return 1;
296 }
297
298 /* Read the next packet */
299 static gboolean netmon_read(wtap *wth, int *err, long *data_offset)
300 {
301         netmon_t *netmon = wth->capture.netmon;
302         guint32 packet_size = 0;
303         guint32 orig_size = 0;
304         int     bytes_read;
305         union {
306                 struct netmonrec_1_x_hdr hdr_1_x;
307                 struct netmonrec_2_x_hdr hdr_2_x;
308         }       hdr;
309         int     hdr_size = 0;
310         int     rec_offset;
311         time_t  secs;
312         guint32 usecs;
313         double  t;
314
315         /* Have we reached the end of the packet data? */
316         if (netmon->current_frame >= netmon->frame_table_size) {
317                 /* Yes.  We won't need the frame table any more;
318                    free it. */
319                 g_free(wth->capture.netmon->frame_table);
320                 wth->capture.netmon->frame_table = NULL;
321                 *err = 0;       /* it's just an EOF, not an error */
322                 return FALSE;
323         }
324
325         /* Seek to the beginning of the current record, if we're
326            not there already (seeking to the current position
327            may still cause a seek and a read of the underlying file,
328            so we don't want to do it unconditionally). */
329         rec_offset = netmon->frame_table[netmon->current_frame];
330         if (wth->data_offset != rec_offset) {
331                 wth->data_offset = rec_offset;
332                 if (file_seek(wth->fh, wth->data_offset, SEEK_SET, err) == -1)
333                         return FALSE;
334         }
335         netmon->current_frame++;
336
337         /* Read record header. */
338         switch (netmon->version_major) {
339
340         case 1:
341                 hdr_size = sizeof (struct netmonrec_1_x_hdr);
342                 break;
343
344         case 2:
345                 hdr_size = sizeof (struct netmonrec_2_x_hdr);
346                 break;
347         }
348         errno = WTAP_ERR_CANT_READ;
349
350         bytes_read = file_read(&hdr, 1, hdr_size, wth->fh);
351         if (bytes_read != hdr_size) {
352                 *err = file_error(wth->fh);
353                 if (*err == 0 && bytes_read != 0) {
354                         *err = WTAP_ERR_SHORT_READ;
355                 }
356                 return FALSE;
357         }
358         wth->data_offset += hdr_size;
359
360         switch (netmon->version_major) {
361
362         case 1:
363                 orig_size = pletohs(&hdr.hdr_1_x.orig_len);
364                 packet_size = pletohs(&hdr.hdr_1_x.incl_len);
365                 break;
366
367         case 2:
368                 orig_size = pletohl(&hdr.hdr_2_x.orig_len);
369                 packet_size = pletohl(&hdr.hdr_2_x.incl_len);
370                 break;
371         }
372         if (packet_size > WTAP_MAX_PACKET_SIZE) {
373                 /*
374                  * Probably a corrupt capture file; don't blow up trying
375                  * to allocate space for an immensely-large packet.
376                  */
377                 g_message("netmon: File has %u-byte packet, bigger than maximum of %u",
378                     packet_size, WTAP_MAX_PACKET_SIZE);
379                 *err = WTAP_ERR_BAD_RECORD;
380                 return FALSE;
381         }
382
383         *data_offset = wth->data_offset;
384
385         /*
386          * If this is an ATM packet, the first
387          * "sizeof (struct netmon_atm_hdr)" bytes have destination and
388          * source addresses (6 bytes - MAC addresses of some sort?)
389          * and the VPI and VCI; read them and generate the pseudo-header
390          * from them.
391          */
392         if (wth->file_encap == WTAP_ENCAP_ATM_SNIFFER) {
393                 if (packet_size < sizeof (struct netmon_atm_hdr)) {
394                         /*
395                          * Uh-oh, the packet isn't big enough to even
396                          * have a pseudo-header.
397                          */
398                         g_message("netmon: ATM file has a %u-byte packet, too small to have even an ATM pseudo-header\n",
399                             packet_size);
400                         *err = WTAP_ERR_BAD_RECORD;
401                         return FALSE;
402                 }
403                 if (!netmon_read_atm_pseudoheader(wth->fh, &wth->pseudo_header,
404                     err))
405                         return FALSE;   /* Read error */
406
407                 /*
408                  * Don't count the pseudo-header as part of the packet.
409                  */
410                 orig_size -= sizeof (struct netmon_atm_hdr);
411                 packet_size -= sizeof (struct netmon_atm_hdr);
412                 wth->data_offset += sizeof (struct netmon_atm_hdr);
413         }
414
415         buffer_assure_space(wth->frame_buffer, packet_size);
416         if (!netmon_read_rec_data(wth->fh, buffer_start_ptr(wth->frame_buffer),
417             packet_size, err))
418                 return FALSE;   /* Read error */
419         wth->data_offset += packet_size;
420
421         t = (double)netmon->start_usecs;
422         switch (netmon->version_major) {
423
424         case 1:
425                 t += ((double)pletohl(&hdr.hdr_1_x.ts_delta))*1000;
426                 break;
427
428         case 2:
429                 t += (double)pletohl(&hdr.hdr_2_x.ts_delta_lo)
430                     + (double)pletohl(&hdr.hdr_2_x.ts_delta_hi)*4294967296.0;
431                 break;
432         }
433         secs = (time_t)(t/1000000);
434         usecs = (guint32)(t - secs*1000000);
435         wth->phdr.ts.tv_sec = netmon->start_secs + secs;
436         wth->phdr.ts.tv_usec = usecs;
437         wth->phdr.caplen = packet_size;
438         wth->phdr.len = orig_size;
439         wth->phdr.pkt_encap = wth->file_encap;
440
441         return TRUE;
442 }
443
444 static gboolean
445 netmon_seek_read(wtap *wth, long seek_off,
446     union wtap_pseudo_header *pseudo_header, guchar *pd, int length, int *err)
447 {
448         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
449                 return FALSE;
450
451         if (wth->file_encap == WTAP_ENCAP_ATM_SNIFFER) {
452                 if (!netmon_read_atm_pseudoheader(wth->random_fh, pseudo_header,
453                     err)) {
454                         /* Read error */
455                         return FALSE;
456                 }
457         }
458
459         /*
460          * Read the packet data.
461          */
462         return netmon_read_rec_data(wth->random_fh, pd, length, err);
463 }
464
465 static gboolean
466 netmon_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
467     int *err)
468 {
469         struct netmon_atm_hdr atm_phdr;
470         int     bytes_read;
471         guint16 vpi, vci;
472
473         errno = WTAP_ERR_CANT_READ;
474         bytes_read = file_read(&atm_phdr, 1, sizeof (struct netmon_atm_hdr), fh);
475         if (bytes_read != sizeof (struct netmon_atm_hdr)) {
476                 *err = file_error(fh);
477                 if (*err == 0)
478                         *err = WTAP_ERR_SHORT_READ;
479                 return FALSE;
480         }
481
482         vpi = g_ntohs(atm_phdr.vpi);
483         vci = g_ntohs(atm_phdr.vci);
484
485         /*
486          * Assume it's AAL5, unless it's VPI 0 and VCI 5, in which case
487          * assume it's AAL_SIGNALLING; we know nothing more about it.
488          */
489         if (vpi == 0 && vci == 5)
490                 pseudo_header->atm.aal = AAL_SIGNALLING;
491         else
492                 pseudo_header->atm.aal = AAL_5;
493         pseudo_header->atm.type = TRAF_UNKNOWN;
494         pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
495
496         pseudo_header->atm.vpi = vpi;
497         pseudo_header->atm.vci = vci;
498
499         /* We don't have this information */
500         pseudo_header->atm.channel = 0;
501         pseudo_header->atm.cells = 0;
502         pseudo_header->atm.aal5t_u2u = 0;
503         pseudo_header->atm.aal5t_len = 0;
504         pseudo_header->atm.aal5t_chksum = 0;
505
506         return TRUE;
507 }
508
509 static gboolean
510 netmon_read_rec_data(FILE_T fh, guchar *pd, int length, int *err)
511 {
512         int     bytes_read;
513
514         errno = WTAP_ERR_CANT_READ;
515         bytes_read = file_read(pd, 1, length, fh);
516
517         if (bytes_read != length) {
518                 *err = file_error(fh);
519                 if (*err == 0)
520                         *err = WTAP_ERR_SHORT_READ;
521                 return FALSE;
522         }
523         return TRUE;
524 }
525
526 /* Throw away the frame table used by the sequential I/O stream. */
527 static void
528 netmon_sequential_close(wtap *wth)
529 {
530         if (wth->capture.netmon->frame_table != NULL) {
531                 g_free(wth->capture.netmon->frame_table);
532                 wth->capture.netmon->frame_table = NULL;
533         }
534 }
535
536 /* Close stuff used by the random I/O stream, if any, and free up any
537    private data structures.  (If there's a "sequential_close" routine
538    for a capture file type, it'll be called before the "close" routine
539    is called, so we don't have to free the frame table here.) */
540 static void
541 netmon_close(wtap *wth)
542 {
543         g_free(wth->capture.netmon);
544 }
545
546 static const int wtap_encap[] = {
547         -1,             /* WTAP_ENCAP_UNKNOWN -> unsupported */
548         1,              /* WTAP_ENCAP_ETHERNET -> NDIS Ethernet */
549         2,              /* WTAP_ENCAP_TOKEN_RING -> NDIS Token Ring */
550         -1,             /* WTAP_ENCAP_SLIP -> unsupported */
551         -1,             /* WTAP_ENCAP_PPP -> unsupported */
552         3,              /* WTAP_ENCAP_FDDI -> NDIS FDDI */
553         3,              /* WTAP_ENCAP_FDDI_BITSWAPPED -> NDIS FDDI */
554         -1,             /* WTAP_ENCAP_RAW_IP -> unsupported */
555         -1,             /* WTAP_ENCAP_ARCNET -> unsupported */
556         -1,             /* WTAP_ENCAP_ATM_RFC1483 -> unsupported */
557         -1,             /* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */
558         -1,             /* WTAP_ENCAP_LAPB -> unsupported*/
559         4,              /* WTAP_ENCAP_ATM_SNIFFER -> NDIS WAN (*NOT* ATM!) */
560         -1              /* WTAP_ENCAP_NULL -> unsupported */
561 };
562 #define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
563
564 /* Returns 0 if we could write the specified encapsulation type,
565    an error indication otherwise. */
566 int netmon_dump_can_write_encap(int encap)
567 {
568         /* Per-packet encapsulations aren't supported. */
569         if (encap == WTAP_ENCAP_PER_PACKET)
570                 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
571
572         if (encap < 0 || (unsigned) encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
573                 return WTAP_ERR_UNSUPPORTED_ENCAP;
574
575         return 0;
576 }
577
578 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
579    failure */
580 gboolean netmon_dump_open(wtap_dumper *wdh, gboolean cant_seek, int *err)
581 {
582         /* This is a NetMon file.  We can't fill in some fields in the
583            header until all the packets have been written, so we can't
584            write to a pipe. */
585         if (cant_seek) {
586                 *err = WTAP_ERR_CANT_WRITE_TO_PIPE;
587                 return FALSE;
588         }
589
590         wdh->subtype_write = netmon_dump;
591         wdh->subtype_close = netmon_dump_close;
592
593         /* We can't fill in all the fields in the file header, as we
594            haven't yet written any packets.  As we'll have to rewrite
595            the header when we've written out all the packets, we just
596            skip over the header for now. */
597         if (fseek(wdh->fh, CAPTUREFILE_HEADER_SIZE, SEEK_SET) == -1) {
598                 *err = errno;
599                 return FALSE;
600         }
601
602         wdh->dump.netmon = g_malloc(sizeof(netmon_dump_t));
603         wdh->dump.netmon->frame_table_offset = CAPTUREFILE_HEADER_SIZE;
604         wdh->dump.netmon->got_first_record_time = FALSE;
605         wdh->dump.netmon->frame_table = NULL;
606         wdh->dump.netmon->frame_table_index = 0;
607         wdh->dump.netmon->frame_table_size = 0;
608
609         return TRUE;
610 }
611
612 /* Write a record for a packet to a dump file.
613    Returns TRUE on success, FALSE on failure. */
614 static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
615     const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err)
616 {
617         netmon_dump_t *netmon = wdh->dump.netmon;
618         struct netmonrec_1_x_hdr rec_1_x_hdr;
619         struct netmonrec_2_x_hdr rec_2_x_hdr;
620         char *hdrp;
621         size_t hdr_size;
622         size_t nwritten;
623         double t;
624         guint32 time_low, time_high;
625         struct netmon_atm_hdr atm_hdr;
626         int atm_hdrsize;
627
628         /* NetMon files have a capture start time in the file header,
629            and have times relative to that in the packet headers;
630            pick the time of the first packet as the capture start
631            time. */
632         if (!netmon->got_first_record_time) {
633                 netmon->first_record_time = phdr->ts;
634                 netmon->got_first_record_time = TRUE;
635         }
636         
637         if (wdh->encap == WTAP_ENCAP_ATM_SNIFFER)
638                 atm_hdrsize = sizeof (struct netmon_atm_hdr);
639         else
640                 atm_hdrsize = 0;
641         switch (wdh->file_type) {
642
643         case WTAP_FILE_NETMON_1_x:
644                 rec_1_x_hdr.ts_delta = htolel(
645                     (phdr->ts.tv_sec - netmon->first_record_time.tv_sec)*1000
646                   + (phdr->ts.tv_usec - netmon->first_record_time.tv_usec + 500)/1000);
647                 rec_1_x_hdr.orig_len = htoles(phdr->len + atm_hdrsize);
648                 rec_1_x_hdr.incl_len = htoles(phdr->caplen + atm_hdrsize);
649                 hdrp = (char *)&rec_1_x_hdr;
650                 hdr_size = sizeof rec_1_x_hdr;
651                 break;
652
653         case WTAP_FILE_NETMON_2_x:
654                 /*
655                  * Unfortunately, not all the platforms on which we run
656                  * support 64-bit integral types, even though most do
657                  * (even on 32-bit processors), so we do it in floating
658                  * point.
659                  */
660                 t = (phdr->ts.tv_sec - netmon->first_record_time.tv_sec)*1000000.0
661                   + (phdr->ts.tv_usec - netmon->first_record_time.tv_usec);
662                 time_high = t/4294967296.0;
663                 time_low = t - (time_high*4294967296.0);
664                 rec_2_x_hdr.ts_delta_lo = htolel(time_low);
665                 rec_2_x_hdr.ts_delta_hi = htolel(time_high);
666                 rec_2_x_hdr.orig_len = htolel(phdr->len + atm_hdrsize);
667                 rec_2_x_hdr.incl_len = htolel(phdr->caplen + atm_hdrsize);
668                 hdrp = (char *)&rec_2_x_hdr;
669                 hdr_size = sizeof rec_2_x_hdr;
670                 break;
671
672         default:
673                 /* We should never get here - our open routine
674                    should only get called for the types above. */
675                 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
676                 return FALSE;
677         }
678
679         nwritten = fwrite(hdrp, 1, hdr_size, wdh->fh);
680         if (nwritten != hdr_size) {
681                 if (nwritten == 0 && ferror(wdh->fh))
682                         *err = errno;
683                 else
684                         *err = WTAP_ERR_SHORT_WRITE;
685                 return FALSE;
686         }
687
688         if (wdh->encap == WTAP_ENCAP_ATM_SNIFFER) {
689                 /*
690                  * Write the ATM header.
691                  * We supply all-zero destination and source addresses.
692                  */
693                 memset(&atm_hdr.dest, 0, sizeof atm_hdr.dest);
694                 memset(&atm_hdr.src, 0, sizeof atm_hdr.src);
695                 atm_hdr.vpi = g_htons(pseudo_header->atm.vpi);
696                 atm_hdr.vci = g_htons(pseudo_header->atm.vci);
697                 nwritten = fwrite(&atm_hdr, 1, sizeof atm_hdr, wdh->fh);
698                 if (nwritten != sizeof atm_hdr) {
699                         if (nwritten == 0 && ferror(wdh->fh))
700                                 *err = errno;
701                         else
702                                 *err = WTAP_ERR_SHORT_WRITE;
703                         return FALSE;
704                 }
705         }
706
707         nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
708         if (nwritten != phdr->caplen) {
709                 if (nwritten == 0 && ferror(wdh->fh))
710                         *err = errno;
711                 else
712                         *err = WTAP_ERR_SHORT_WRITE;
713                 return FALSE;
714         }
715
716         /*
717          * Stash the file offset of this frame.
718          */
719         if (netmon->frame_table_size == 0) {
720                 /*
721                  * Haven't yet allocated the buffer for the frame table.
722                  */
723                 netmon->frame_table = g_malloc(1024 * sizeof *netmon->frame_table);
724                 netmon->frame_table_size = 1024;
725         } else {
726                 /*
727                  * We've allocated it; are we at the end?
728                  */
729                 if (netmon->frame_table_index >= netmon->frame_table_size) {
730                         /*
731                          * Yes - double the size of the frame table.
732                          */
733                         netmon->frame_table_size *= 2;
734                         netmon->frame_table = g_realloc(netmon->frame_table,
735                             netmon->frame_table_size * sizeof *netmon->frame_table);
736                 }
737         }
738         netmon->frame_table[netmon->frame_table_index] =
739             htolel(netmon->frame_table_offset);
740         netmon->frame_table_index++;
741         netmon->frame_table_offset += hdr_size + phdr->caplen + atm_hdrsize;
742
743         return TRUE;
744 }
745
746 /* Finish writing to a dump file.
747    Returns TRUE on success, FALSE on failure. */
748 static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
749 {
750         netmon_dump_t *netmon = wdh->dump.netmon;
751         size_t n_to_write;
752         size_t nwritten;
753         struct netmon_hdr file_hdr;
754         const char *magicp;
755         size_t magic_size;
756         struct tm *tm;
757
758         /* Write out the frame table.  "netmon->frame_table_index" is
759            the number of entries we've put into it. */
760         n_to_write = netmon->frame_table_index * sizeof *netmon->frame_table;
761         nwritten = fwrite(netmon->frame_table, 1, n_to_write, wdh->fh);
762         if (nwritten != n_to_write) {
763                 if (err != NULL) {
764                         if (nwritten == 0 && ferror(wdh->fh))
765                                 *err = errno;
766                         else
767                                 *err = WTAP_ERR_SHORT_WRITE;
768                 }
769                 return FALSE;
770         }
771
772         /* Now go fix up the file header. */
773         fseek(wdh->fh, 0, SEEK_SET);
774         memset(&file_hdr, '\0', sizeof file_hdr);
775         switch (wdh->file_type) {
776
777         case WTAP_FILE_NETMON_1_x:
778                 magicp = netmon_1_x_magic;
779                 magic_size = sizeof netmon_1_x_magic;
780                 /* current NetMon version, for 1.x, is 1.1 */
781                 file_hdr.ver_major = 1;
782                 file_hdr.ver_minor = 1;
783                 break;
784
785         case WTAP_FILE_NETMON_2_x:
786                 magicp = netmon_2_x_magic;
787                 magic_size = sizeof netmon_2_x_magic;
788                 /* current NetMon version, for 2.x, is 2.0 */
789                 file_hdr.ver_major = 2;
790                 file_hdr.ver_minor = 0;
791                 break;
792
793         default:
794                 /* We should never get here - our open routine
795                    should only get called for the types above. */
796                 if (err != NULL)
797                         *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
798                 return FALSE;
799         }
800         nwritten = fwrite(magicp, 1, magic_size, wdh->fh);
801         if (nwritten != magic_size) {
802                 if (err != NULL) {
803                         if (nwritten == 0 && ferror(wdh->fh))
804                                 *err = errno;
805                         else
806                                 *err = WTAP_ERR_SHORT_WRITE;
807                 }
808                 return FALSE;
809         }
810
811         file_hdr.network = htoles(wtap_encap[wdh->encap]);
812         tm = localtime(&netmon->first_record_time.tv_sec);
813         if (tm != NULL) {
814                 file_hdr.ts_year  = htoles(1900 + tm->tm_year);
815                 file_hdr.ts_month = htoles(tm->tm_mon + 1);
816                 file_hdr.ts_dow   = htoles(tm->tm_wday);
817                 file_hdr.ts_day   = htoles(tm->tm_mday);
818                 file_hdr.ts_hour  = htoles(tm->tm_hour);
819                 file_hdr.ts_min   = htoles(tm->tm_min);
820                 file_hdr.ts_sec   = htoles(tm->tm_sec);
821         } else {
822                 file_hdr.ts_year  = htoles(1900 + 0);
823                 file_hdr.ts_month = htoles(0 + 1);
824                 file_hdr.ts_dow   = htoles(0);
825                 file_hdr.ts_day   = htoles(0);
826                 file_hdr.ts_hour  = htoles(0);
827                 file_hdr.ts_min   = htoles(0);
828                 file_hdr.ts_sec   = htoles(0);
829         }
830         file_hdr.ts_msec  = htoles(netmon->first_record_time.tv_usec/1000);
831                 /* XXX - what about rounding? */
832         file_hdr.frametableoffset = htolel(netmon->frame_table_offset);
833         file_hdr.frametablelength =
834             htolel(netmon->frame_table_index * sizeof *netmon->frame_table);
835         nwritten = fwrite(&file_hdr, 1, sizeof file_hdr, wdh->fh);
836         if (nwritten != sizeof file_hdr) {
837                 if (err != NULL) {
838                         if (nwritten == 0 && ferror(wdh->fh))
839                                 *err = errno;
840                         else
841                                 *err = WTAP_ERR_SHORT_WRITE;
842                 }
843                 return FALSE;
844         }
845
846         return TRUE;
847 }