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