Move #include <time.h> into wtap-int.h instead of requiring it in
[obnox/wireshark/wip.git] / wiretap / netmon.c
1 /* netmon.c
2  *
3  * $Id: netmon.c,v 1.32 2000/08/25 21:25:39 gram 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 int netmon_read(wtap *wth, int *err);
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_TR,
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         int frame_table_size;
127         guint32 *frame_table;
128 #ifdef WORDS_BIGENDIAN
129         int i;
130 #endif
131
132         /* Read in the string that should be at the start of a Network
133          * Monitor file */
134         file_seek(wth->fh, 0, SEEK_SET);
135         errno = WTAP_ERR_CANT_READ;
136         bytes_read = file_read(magic, 1, sizeof magic, wth->fh);
137         if (bytes_read != sizeof magic) {
138                 *err = file_error(wth->fh);
139                 if (*err != 0)
140                         return -1;
141                 return 0;
142         }
143
144         if (memcmp(magic, netmon_1_x_magic, sizeof netmon_1_x_magic) != 0
145          && memcmp(magic, netmon_2_x_magic, sizeof netmon_1_x_magic) != 0) {
146                 return 0;
147         }
148
149         /* Read the rest of the header. */
150         errno = WTAP_ERR_CANT_READ;
151         bytes_read = file_read(&hdr, 1, sizeof hdr, wth->fh);
152         if (bytes_read != sizeof hdr) {
153                 *err = file_error(wth->fh);
154                 if (*err != 0)
155                         return -1;
156                 return 0;
157         }
158
159         switch (hdr.ver_major) {
160
161         case 1:
162                 file_type = WTAP_FILE_NETMON_1_x;
163                 break;
164
165         case 2:
166                 file_type = WTAP_FILE_NETMON_2_x;
167                 break;
168
169         default:
170                 g_message("netmon: major version %u unsupported", hdr.ver_major);
171                 *err = WTAP_ERR_UNSUPPORTED;
172                 return -1;
173         }
174
175         hdr.network = pletohs(&hdr.network);
176         if (hdr.network >= NUM_NETMON_ENCAPS
177             || netmon_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
178                 g_message("netmon: network type %u unknown or unsupported",
179                     hdr.network);
180                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
181                 return -1;
182         }
183
184         /* This is a netmon file */
185         wth->file_type = file_type;
186         wth->capture.netmon = g_malloc(sizeof(netmon_t));
187         wth->subtype_read = netmon_read;
188         wth->subtype_seek_read = wtap_def_seek_read;
189         wth->subtype_close = netmon_close;
190         wth->file_encap = netmon_encap[hdr.network];
191         wth->snapshot_length = 16384;   /* XXX - not available in header */
192         /*
193          * Convert the time stamp to a "time_t" and a number of
194          * milliseconds.
195          */
196         tm.tm_year = pletohs(&hdr.ts_year) - 1900;
197         tm.tm_mon = pletohs(&hdr.ts_month) - 1;
198         tm.tm_mday = pletohs(&hdr.ts_day);
199         tm.tm_hour = pletohs(&hdr.ts_hour);
200         tm.tm_min = pletohs(&hdr.ts_min);
201         tm.tm_sec = pletohs(&hdr.ts_sec);
202         tm.tm_isdst = -1;
203         wth->capture.netmon->start_secs = mktime(&tm);
204         /*
205          * XXX - what if "secs" is -1?  Unlikely, but if the capture was
206          * done in a time zone that switches between standard and summer
207          * time sometime other than when we do, and thus the time was one
208          * that doesn't exist here because a switch from standard to summer
209          * time zips over it, it could happen.
210          *
211          * On the other hand, if the capture was done in a different time
212          * zone, this won't work right anyway; unfortunately, the time
213          * zone isn't stored in the capture file (why the hell didn't
214          * they stuff a FILETIME, which is the number of 100-nanosecond
215          * intervals since 1601-01-01 00:00:00 "UTC", there, instead
216          * of stuffing a SYSTEMTIME, which is time-zone-dependent, there?).
217          */
218         wth->capture.netmon->start_usecs = pletohs(&hdr.ts_msec)*1000;
219
220         wth->capture.netmon->version_major = hdr.ver_major;
221
222         /*
223          * Get the offset of the frame index table.
224          */
225         frame_table_offset = pletohl(&hdr.frametableoffset);
226
227         /*
228          * It appears that some NetMon 2.x files don't have the
229          * first packet starting exactly 128 bytes into the file.
230          *
231          * Furthermore, it also appears that there are "holes" in
232          * the file, i.e. frame N+1 doesn't always follow immediately
233          * after frame N.
234          *
235          * Therefore, we must read the frame table, and use the offsets
236          * in it as the offsets of the frames.
237          */
238         frame_table_length = pletohl(&hdr.frametablelength);
239         frame_table_size = frame_table_length / sizeof (guint32);
240         if ((frame_table_size * sizeof (guint32)) != frame_table_length) {
241                 g_message("netmon: frame table length is %u, which is not a multiple of the size of an entry",
242                     frame_table_length);
243                 *err = WTAP_ERR_UNSUPPORTED;
244                 return -1;
245         }
246         if (frame_table_size == 0) {
247                 g_message("netmon: frame table length is %u, which means it's less than one entry in size",
248                     frame_table_length);
249                 *err = WTAP_ERR_UNSUPPORTED;
250                 return -1;
251         }
252         frame_table = g_malloc(frame_table_length);
253         errno = WTAP_ERR_CANT_READ;
254         file_seek(wth->fh, frame_table_offset, SEEK_SET);
255         bytes_read = file_read(frame_table, 1, frame_table_length, wth->fh);
256         if (bytes_read != frame_table_length) {
257                 *err = file_error(wth->fh);
258                 if (*err != 0)
259                         return -1;
260                 return 0;
261         }
262         wth->capture.netmon->frame_table_size = frame_table_size;
263         wth->capture.netmon->frame_table = frame_table;
264
265 #ifdef WORDS_BIGENDIAN
266         /*
267          * OK, now byte-swap the frame table.
268          */
269         for (i = 0; i < frame_table_size; i++)
270                 frame_table[i] = pletohl(&frame_table[i]);
271 #endif
272
273         /* Set up to start reading at the first frame. */
274         wth->capture.netmon->current_frame = 0;
275
276         return 1;
277 }
278
279 /* Read the next packet */
280 static int netmon_read(wtap *wth, int *err)
281 {
282         netmon_t *netmon = wth->capture.netmon;
283         guint32 packet_size = 0;
284         int     bytes_read;
285         union {
286                 struct netmonrec_1_x_hdr hdr_1_x;
287                 struct netmonrec_2_x_hdr hdr_2_x;
288         }       hdr;
289         int     hdr_size = 0;
290         int     data_offset;
291         time_t  secs;
292         guint32 usecs;
293         double  t;
294
295         /* Have we reached the end of the packet data? */
296         if (netmon->current_frame >= netmon->frame_table_size) {
297                 /* Yes.  We won't need the frame table any more;
298                    free it. */
299                 g_free(wth->capture.netmon->frame_table);
300                 wth->capture.netmon->frame_table = NULL;
301                 return 0;
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         data_offset = netmon->frame_table[netmon->current_frame];
309         if (wth->data_offset != data_offset) {
310                 wth->data_offset = data_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)
332                         return -1;
333                 if (bytes_read != 0) {
334                         *err = WTAP_ERR_SHORT_READ;
335                         return -1;
336                 }
337                 return 0;
338         }
339         wth->data_offset += hdr_size;
340
341         switch (netmon->version_major) {
342
343         case 1:
344                 packet_size = pletohs(&hdr.hdr_1_x.incl_len);
345                 break;
346
347         case 2:
348                 packet_size = pletohl(&hdr.hdr_2_x.incl_len);
349                 break;
350         }
351         if (packet_size > WTAP_MAX_PACKET_SIZE) {
352                 /*
353                  * Probably a corrupt capture file; don't blow up trying
354                  * to allocate space for an immensely-large packet.
355                  */
356                 g_message("netmon: File has %u-byte packet, bigger than maximum of %u",
357                     packet_size, WTAP_MAX_PACKET_SIZE);
358                 *err = WTAP_ERR_BAD_RECORD;
359                 return -1;
360         }
361         buffer_assure_space(wth->frame_buffer, packet_size);
362         data_offset = wth->data_offset;
363         errno = WTAP_ERR_CANT_READ;
364         bytes_read = file_read(buffer_start_ptr(wth->frame_buffer), 1,
365                         packet_size, wth->fh);
366
367         if (bytes_read != packet_size) {
368                 *err = file_error(wth->fh);
369                 if (*err == 0)
370                         *err = WTAP_ERR_SHORT_READ;
371                 return -1;
372         }
373         wth->data_offset += packet_size;
374
375         t = (double)netmon->start_usecs;
376         switch (netmon->version_major) {
377
378         case 1:
379                 t += ((double)pletohl(&hdr.hdr_1_x.ts_delta))*1000;
380                 break;
381
382         case 2:
383                 t += (double)pletohl(&hdr.hdr_2_x.ts_delta_lo)
384                     + (double)pletohl(&hdr.hdr_2_x.ts_delta_hi)*4294967296.0;
385                 break;
386         }
387         secs = (time_t)(t/1000000);
388         usecs = (guint32)(t - secs*1000000);
389         wth->phdr.ts.tv_sec = netmon->start_secs + secs;
390         wth->phdr.ts.tv_usec = usecs;
391         wth->phdr.caplen = packet_size;
392         switch (netmon->version_major) {
393
394         case 1:
395                 wth->phdr.len = pletohs(&hdr.hdr_1_x.orig_len);
396                 break;
397
398         case 2:
399                 wth->phdr.len = pletohl(&hdr.hdr_2_x.orig_len);
400                 break;
401         }
402         wth->phdr.pkt_encap = wth->file_encap;
403
404         return data_offset;
405 }
406
407 static void
408 netmon_close(wtap *wth)
409 {
410         if (wth->capture.netmon->frame_table != NULL)
411                 g_free(wth->capture.netmon->frame_table);
412         g_free(wth->capture.netmon);
413 }
414
415 static const int wtap_encap[] = {
416         -1,             /* WTAP_ENCAP_UNKNOWN -> unsupported */
417         1,              /* WTAP_ENCAP_ETHERNET -> NDIS Ethernet */
418         2,              /* WTAP_ENCAP_TR -> NDIS Token Ring */
419         -1,             /* WTAP_ENCAP_SLIP -> unsupported */
420         -1,             /* WTAP_ENCAP_PPP -> unsupported */
421         3,              /* WTAP_ENCAP_FDDI -> NDIS FDDI */
422         3,              /* WTAP_ENCAP_FDDI_BITSWAPPED -> NDIS FDDI */
423         -1,             /* WTAP_ENCAP_RAW_IP -> unsupported */
424         -1,             /* WTAP_ENCAP_ARCNET -> unsupported */
425         -1,             /* WTAP_ENCAP_ATM_RFC1483 -> unsupported */
426         -1,             /* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */
427         -1,             /* WTAP_ENCAP_LAPB -> unsupported*/
428         -1,             /* WTAP_ENCAP_ATM_SNIFFER -> unsupported */
429         -1              /* WTAP_ENCAP_NULL -> unsupported */
430 };
431 #define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
432
433 /* Returns 0 if we could write the specified encapsulation type,
434    an error indication otherwise. */
435 int netmon_dump_can_write_encap(int filetype, int encap)
436 {
437         /* Per-packet encapsulations aren't supported. */
438         if (encap == WTAP_ENCAP_PER_PACKET)
439                 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
440
441         if (encap < 0 || encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
442                 return WTAP_ERR_UNSUPPORTED_ENCAP;
443
444         return 0;
445 }
446
447 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
448    failure */
449 gboolean netmon_dump_open(wtap_dumper *wdh, int *err)
450 {
451         /* This is a netmon file */
452         wdh->subtype_write = netmon_dump;
453         wdh->subtype_close = netmon_dump_close;
454
455         /* We can't fill in all the fields in the file header, as we
456            haven't yet written any packets.  As we'll have to rewrite
457            the header when we've written out all the packets, we just
458            skip over the header for now. */
459         fseek(wdh->fh, CAPTUREFILE_HEADER_SIZE, SEEK_SET);
460
461         wdh->dump.netmon = g_malloc(sizeof(netmon_dump_t));
462         wdh->dump.netmon->frame_table_offset = CAPTUREFILE_HEADER_SIZE;
463         wdh->dump.netmon->got_first_record_time = FALSE;
464         wdh->dump.netmon->frame_table = NULL;
465         wdh->dump.netmon->frame_table_index = 0;
466         wdh->dump.netmon->frame_table_size = 0;
467
468         return TRUE;
469 }
470
471 /* Write a record for a packet to a dump file.
472    Returns TRUE on success, FALSE on failure. */
473 static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
474     const union wtap_pseudo_header *pseudo_header, const u_char *pd, int *err)
475 {
476         netmon_dump_t *netmon = wdh->dump.netmon;
477         struct netmonrec_1_x_hdr rec_1_x_hdr;
478         struct netmonrec_2_x_hdr rec_2_x_hdr;
479         char *hdrp;
480         int hdr_size;
481         int nwritten;
482
483         /* NetMon files have a capture start time in the file header,
484            and have times relative to that in the packet headers;
485            pick the time of the first packet as the capture start
486            time. */
487         if (!netmon->got_first_record_time) {
488                 netmon->first_record_time = phdr->ts;
489                 netmon->got_first_record_time = TRUE;
490         }
491         
492         switch (wdh->file_type) {
493
494         case WTAP_FILE_NETMON_1_x:
495                 rec_1_x_hdr.ts_delta = htolel(
496                     (phdr->ts.tv_sec - netmon->first_record_time.tv_sec)*1000
497                   + (phdr->ts.tv_usec - netmon->first_record_time.tv_usec + 500)/1000);
498                 rec_1_x_hdr.orig_len = htoles(phdr->len);
499                 rec_1_x_hdr.incl_len = htoles(phdr->caplen);
500                 hdrp = (char *)&rec_1_x_hdr;
501                 hdr_size = sizeof rec_1_x_hdr;
502                 break;
503
504         case WTAP_FILE_NETMON_2_x:
505                 /* XXX - fill in 64-bit time diff in microseconds */
506                 rec_2_x_hdr.orig_len = htolel(phdr->len);
507                 rec_2_x_hdr.incl_len = htolel(phdr->caplen);
508                 hdrp = (char *)&rec_2_x_hdr;
509                 hdr_size = sizeof rec_2_x_hdr;
510                 break;
511
512         default:
513                 /* We should never get here - our open routine
514                    should only get called for the types above. */
515                 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
516                 return FALSE;
517         }
518
519         nwritten = fwrite(hdrp, 1, hdr_size, wdh->fh);
520         if (nwritten != hdr_size) {
521                 if (nwritten < 0)
522                         *err = errno;
523                 else
524                         *err = WTAP_ERR_SHORT_WRITE;
525                 return FALSE;
526         }
527         nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
528         if (nwritten != phdr->caplen) {
529                 if (nwritten < 0)
530                         *err = errno;
531                 else
532                         *err = WTAP_ERR_SHORT_WRITE;
533                 return FALSE;
534         }
535
536         /*
537          * Stash the file offset of this frame.
538          */
539         if (netmon->frame_table_size == 0) {
540                 /*
541                  * Haven't yet allocated the buffer for the frame table.
542                  */
543                 netmon->frame_table = g_malloc(1024 * sizeof *netmon->frame_table);
544                 netmon->frame_table_size = 1024;
545         } else {
546                 /*
547                  * We've allocated it; are we at the end?
548                  */
549                 if (netmon->frame_table_index >= netmon->frame_table_size) {
550                         /*
551                          * Yes - double the size of the frame table.
552                          */
553                         netmon->frame_table_size *= 2;
554                         netmon->frame_table = g_realloc(netmon->frame_table,
555                             netmon->frame_table_size * sizeof *netmon->frame_table);
556                 }
557         }
558         netmon->frame_table[netmon->frame_table_index] =
559             htolel(netmon->frame_table_offset);
560         netmon->frame_table_index++;
561         netmon->frame_table_offset += hdr_size + phdr->caplen;
562
563         return TRUE;
564 }
565
566 /* Finish writing to a dump file.
567    Returns TRUE on success, FALSE on failure. */
568 static gboolean netmon_dump_close(wtap_dumper *wdh, int *err)
569 {
570         netmon_dump_t *netmon = wdh->dump.netmon;
571         int n_to_write;
572         int nwritten;
573         struct netmon_hdr file_hdr;
574         const char *magicp;
575         int magic_size;
576         struct tm *tm;
577
578         /* Write out the frame table.  "netmon->frame_table_index" is
579            the number of entries we've put into it. */
580         n_to_write = netmon->frame_table_index * sizeof *netmon->frame_table;
581         nwritten = fwrite(netmon->frame_table, 1, n_to_write, wdh->fh);
582         if (nwritten != n_to_write) {
583                 if (nwritten < 0)
584                         *err = errno;
585                 else
586                         *err = WTAP_ERR_SHORT_WRITE;
587                 return FALSE;
588         }
589
590         /* Now go fix up the file header. */
591         fseek(wdh->fh, 0, SEEK_SET);
592         memset(&file_hdr, '\0', sizeof file_hdr);
593         switch (wdh->file_type) {
594
595         case WTAP_FILE_NETMON_1_x:
596                 magicp = netmon_1_x_magic;
597                 magic_size = sizeof netmon_1_x_magic;
598                 /* current NetMon version, for 1.x, is 1.1 */
599                 file_hdr.ver_minor = 1;
600                 file_hdr.ver_major = 1;
601                 break;
602
603         case WTAP_FILE_NETMON_2_x:
604                 magicp = netmon_2_x_magic;
605                 magic_size = sizeof netmon_2_x_magic;
606                 /* XXX - fill in V2 stuff. */
607                 break;
608
609         default:
610                 /* We should never get here - our open routine
611                    should only get called for the types above. */
612                 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
613                 return FALSE;
614         }
615         nwritten = fwrite(magicp, 1, magic_size, wdh->fh);
616         if (nwritten != magic_size) {
617                 if (nwritten < 0)
618                         *err = errno;
619                 else
620                         *err = WTAP_ERR_SHORT_WRITE;
621                 return FALSE;
622         }
623
624         file_hdr.network = htoles(wtap_encap[wdh->encap]);
625         tm = localtime(&netmon->first_record_time.tv_sec);
626         file_hdr.ts_year = htoles(1900 + tm->tm_year);
627         file_hdr.ts_month = htoles(tm->tm_mon + 1);
628         file_hdr.ts_dow = htoles(tm->tm_wday);
629         file_hdr.ts_day = htoles(tm->tm_mday);
630         file_hdr.ts_hour = htoles(tm->tm_hour);
631         file_hdr.ts_min = htoles(tm->tm_min);
632         file_hdr.ts_sec = htoles(tm->tm_sec);
633         file_hdr.ts_msec = htoles(netmon->first_record_time.tv_usec/1000);
634                 /* XXX - what about rounding? */
635         file_hdr.frametableoffset = htolel(netmon->frame_table_offset);
636         file_hdr.frametablelength =
637             htolel(netmon->frame_table_index * sizeof *netmon->frame_table);
638         nwritten = fwrite(&file_hdr, 1, sizeof file_hdr, wdh->fh);
639         if (nwritten != sizeof file_hdr) {
640                 if (nwritten < 0)
641                         *err = errno;
642                 else
643                         *err = WTAP_ERR_SHORT_WRITE;
644                 return FALSE;
645         }
646
647         return TRUE;
648 }