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