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