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