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