In a Network Monitor capture file, get the starting offsets of frames
[obnox/wireshark/wip.git] / wiretap / netxray.c
1 /* netxray.c
2  *
3  * $Id: netxray.c,v 1.25 2000/03/22 07:06:55 guy Exp $
4  *
5  * Wiretap Library
6  * Copyright (c) 1998 by Gilbert Ramirez <gram@xiexie.org>
7  * 
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  * 
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <time.h>
30 #include <string.h>
31 #include "wtap.h"
32 #include "file_wrappers.h"
33 #include "netxray.h"
34 #include "buffer.h"
35
36 /* Capture file header, *including* magic number, is padded to 128 bytes. */
37 #define CAPTUREFILE_HEADER_SIZE 128
38
39 /* Magic number in NetXRay files. */
40 static const char netxray_magic[] = {   /* magic header */
41         'X', 'C', 'P', '\0'
42 };
43
44 /* NetXRay file header (minus magic number). */
45 struct netxray_hdr {
46         char    version[8];     /* version number */
47         guint32 start_time;     /* UNIX time when capture started */
48         guint32 nframes;        /* number of packets */
49         guint32 xxx;            /* unknown */
50         guint32 start_offset;   /* offset of first packet in capture */
51         guint32 end_offset;     /* offset after last packet in capture */
52         guint32 xxy[3];         /* unknown */
53         guint16 network;        /* datalink type */
54         guint8  xxz[6];
55         guint32 timelo;         /* lower 32 bits of time stamp of capture start */
56         guint32 timehi;         /* upper 32 bits of time stamp of capture start */
57         /*
58          * XXX - other stuff.
59          */
60 };
61
62 /* Version number strings. */
63 static const char vers_1_0[] = {
64         '0', '0', '1', '.', '0', '0', '0', '\0'
65 };
66
67 static const char vers_1_1[] = {
68         '0', '0', '1', '.', '1', '0', '0', '\0'
69 };
70
71 static const char vers_2_001[] = {
72         '0', '0', '2', '.', '0', '0', '1', '\0'
73 };
74
75 /* NetXRay 1.x data record format - followed by frame data. */
76 struct netxrayrec_1_x_hdr {
77         guint32 timelo;         /* lower 32 bits of time stamp */
78         guint32 timehi;         /* upper 32 bits of time stamp */
79         guint16 orig_len;       /* packet length */
80         guint16 incl_len;       /* capture length */
81         guint32 xxx[4];         /* unknown */
82 };
83
84 /* NetXRay 2.x data record format - followed by frame data. */
85 struct netxrayrec_2_x_hdr {
86         guint32 timelo;         /* lower 32 bits of time stamp */
87         guint32 timehi;         /* upper 32 bits of time stamp */
88         guint16 orig_len;       /* packet length */
89         guint16 incl_len;       /* capture length */
90         guint32 xxx[7];         /* unknown */
91 };
92
93 static int netxray_read(wtap *wth, int *err);
94 static void netxray_close(wtap *wth);
95 static gboolean netxray_dump_1_1(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
96         const u_char *pd, int *err);
97 static gboolean netxray_dump_close_1_1(wtap_dumper *wdh, int *err);
98
99 int netxray_open(wtap *wth, int *err)
100 {
101         int bytes_read;
102         char magic[sizeof netxray_magic];
103         struct netxray_hdr hdr;
104         double timeunit;
105         int version_major;
106         int file_type;
107         double t;
108         static const int netxray_encap[] = {
109                 WTAP_ENCAP_ETHERNET,
110                 WTAP_ENCAP_TR,
111                 WTAP_ENCAP_FDDI_BITSWAPPED,
112                 WTAP_ENCAP_ETHERNET,    /* WAN(PPP), but shaped like ethernet */
113                 WTAP_ENCAP_UNKNOWN,     /* LocalTalk */
114                 WTAP_ENCAP_UNKNOWN,     /* "DIX" - should not occur */
115                 WTAP_ENCAP_UNKNOWN,     /* ARCNET raw */
116                 WTAP_ENCAP_UNKNOWN,     /* ARCNET 878.2 */
117                 WTAP_ENCAP_UNKNOWN,     /* ATM */
118                 WTAP_ENCAP_UNKNOWN,     /* Wireless WAN */
119                 WTAP_ENCAP_UNKNOWN      /* IrDA */
120         };
121         #define NUM_NETXRAY_ENCAPS (sizeof netxray_encap / sizeof netxray_encap[0])
122
123         /* Read in the string that should be at the start of a NetXRay
124          * file */
125         file_seek(wth->fh, 0, SEEK_SET);
126         wth->data_offset = 0;
127         errno = WTAP_ERR_CANT_READ;
128         bytes_read = file_read(magic, 1, sizeof magic, wth->fh);
129         if (bytes_read != sizeof magic) {
130                 *err = file_error(wth->fh);
131                 if (*err != 0)
132                         return -1;
133                 return 0;
134         }
135         wth->data_offset += sizeof magic;
136
137         if (memcmp(magic, netxray_magic, sizeof netxray_magic) != 0) {
138                 return 0;
139         }
140
141         /* Read the rest of the header. */
142         errno = WTAP_ERR_CANT_READ;
143         bytes_read = file_read(&hdr, 1, sizeof hdr, wth->fh);
144         if (bytes_read != sizeof hdr) {
145                 *err = file_error(wth->fh);
146                 if (*err != 0)
147                         return -1;
148                 return 0;
149         }
150         wth->data_offset += sizeof hdr;
151
152         /* It appears that version 1.1 files (as produced by Windows
153          * Sniffer Pro 2.0.01) have the time stamp in microseconds,
154          * rather than the milliseconds version 1.0 files appear to have.
155          *
156          * It also appears that version 2.001 files (as produced by
157          * Windows(?) Sniffer Pro 2.50.05) have per-packet headers with
158          * some extra fields. */
159         if (memcmp(hdr.version, vers_1_0, sizeof vers_1_0) == 0) {
160                 timeunit = 1000.0;
161                 version_major = 1;
162                 file_type = WTAP_FILE_NETXRAY_1_0;
163         } else if (memcmp(hdr.version, vers_1_1, sizeof vers_1_1) == 0) {
164                 timeunit = 1000000.0;
165                 version_major = 1;
166                 file_type = WTAP_FILE_NETXRAY_1_1;
167         } else if (memcmp(hdr.version, vers_2_001, sizeof vers_2_001) == 0) {
168                 timeunit = 1000000.0;
169                 version_major = 2;
170                 file_type = WTAP_FILE_NETXRAY_2_001;
171         } else {
172                 g_message("netxray: version \"%.8s\" unsupported", hdr.version);
173                 *err = WTAP_ERR_UNSUPPORTED;
174                 return -1;
175         }
176
177         hdr.network = pletohs(&hdr.network);
178         if (hdr.network >= NUM_NETXRAY_ENCAPS
179             || netxray_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
180                 g_message("netxray: network type %u unknown or unsupported",
181                     hdr.network);
182                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
183                 return -1;
184         }
185
186         /* This is a netxray file */
187         wth->file_type = file_type;
188         wth->capture.netxray = g_malloc(sizeof(netxray_t));
189         wth->subtype_read = netxray_read;
190         wth->subtype_close = netxray_close;
191         wth->file_encap = netxray_encap[hdr.network];
192         wth->snapshot_length = 16384;   /* XXX - not available in header */
193         wth->capture.netxray->start_time = pletohl(&hdr.start_time);
194         wth->capture.netxray->timeunit = timeunit;
195         t = (double)pletohl(&hdr.timelo)
196             + (double)pletohl(&hdr.timehi)*4294967296.0;
197         t = t/timeunit;
198         wth->capture.netxray->start_timestamp = t;
199         wth->capture.netxray->version_major = version_major;
200         /*wth->frame_number = 0;*/
201         /*wth->file_byte_offset = 0x10b;*/
202
203         /* Remember the offset after the last packet in the capture (which
204          * isn't necessarily the last packet in the file), as it appears
205          * there's sometimes crud after it. */
206         wth->capture.netxray->wrapped = 0;
207         wth->capture.netxray->end_offset = pletohl(&hdr.end_offset);
208
209         /* Seek to the beginning of the data records. */
210         file_seek(wth->fh, pletohl(&hdr.start_offset), SEEK_SET);
211         wth->data_offset = pletohl(&hdr.start_offset);
212
213         return 1;
214 }
215
216 /* Read the next packet */
217 static int netxray_read(wtap *wth, int *err)
218 {
219         guint32 packet_size;
220         int     bytes_read;
221         union {
222                 struct netxrayrec_1_x_hdr hdr_1_x;
223                 struct netxrayrec_2_x_hdr hdr_2_x;
224         }       hdr;
225         int     hdr_size = 0;
226         int     data_offset;
227         double  t;
228
229 reread:
230         /* Have we reached the end of the packet data? */
231         if (wth->data_offset == wth->capture.netxray->end_offset) {
232                 /* Yes. */
233                 return 0;
234         }
235         /* Read record header. */
236         switch (wth->capture.netxray->version_major) {
237
238         case 1:
239                 hdr_size = sizeof (struct netxrayrec_1_x_hdr);
240                 break;
241
242         case 2:
243                 hdr_size = sizeof (struct netxrayrec_2_x_hdr);
244                 break;
245         }
246         errno = WTAP_ERR_CANT_READ;
247         bytes_read = file_read(&hdr, 1, hdr_size, wth->fh);
248         if (bytes_read != hdr_size) {
249                 *err = file_error(wth->fh);
250                 if (*err != 0)
251                         return -1;
252                 if (bytes_read != 0) {
253                         *err = WTAP_ERR_SHORT_READ;
254                         return -1;
255                 }
256
257                 /* We're at EOF.  Wrap? */
258                 if (!wth->capture.netxray->wrapped) {
259                         /* Yes.  Remember that we did. */
260                         wth->capture.netxray->wrapped = 1;
261                         file_seek(wth->fh, CAPTUREFILE_HEADER_SIZE, SEEK_SET);
262                         wth->data_offset = CAPTUREFILE_HEADER_SIZE;
263                         goto reread;
264                 }
265
266                 /* We've already wrapped - don't wrap again. */
267                 return 0;
268         }
269         wth->data_offset += hdr_size;
270
271         packet_size = pletohs(&hdr.hdr_1_x.incl_len);
272         buffer_assure_space(wth->frame_buffer, packet_size);
273         data_offset = wth->data_offset;
274         errno = WTAP_ERR_CANT_READ;
275         bytes_read = file_read(buffer_start_ptr(wth->frame_buffer), 1,
276                         packet_size, wth->fh);
277
278         if (bytes_read != packet_size) {
279                 *err = file_error(wth->fh);
280                 if (*err == 0)
281                         *err = WTAP_ERR_SHORT_READ;
282                 return -1;
283         }
284         wth->data_offset += packet_size;
285
286         t = (double)pletohl(&hdr.hdr_1_x.timelo)
287             + (double)pletohl(&hdr.hdr_1_x.timehi)*4294967296.0;
288         t /= wth->capture.netxray->timeunit;
289         t -= wth->capture.netxray->start_timestamp;
290         wth->phdr.ts.tv_sec = wth->capture.netxray->start_time + (long)t;
291         wth->phdr.ts.tv_usec = (unsigned long)((t-(double)(unsigned long)(t))
292                 *1.0e6);
293         wth->phdr.caplen = packet_size;
294         wth->phdr.len = pletohs(&hdr.hdr_1_x.orig_len);
295         wth->phdr.pkt_encap = wth->file_encap;
296
297         return data_offset;
298 }
299
300 static void
301 netxray_close(wtap *wth)
302 {
303         g_free(wth->capture.netxray);
304 }
305
306 static const int wtap_encap[] = {
307     -1,         /* WTAP_ENCAP_UNKNOWN -> unsupported */
308     0,          /* WTAP_ENCAP_ETHERNET -> NDIS Ethernet */
309     1,          /* WTAP_ENCAP_TR -> NDIS Token Ring */
310     -1,         /* WTAP_ENCAP_SLIP -> unsupported */
311     -1,         /* WTAP_ENCAP_PPP -> unsupported */
312     2,          /* WTAP_ENCAP_FDDI -> NDIS FDDI */
313     2,          /* WTAP_ENCAP_FDDI_BITSWAPPED -> NDIS FDDI */
314     -1,         /* WTAP_ENCAP_RAW_IP -> unsupported */
315     -1,         /* WTAP_ENCAP_ARCNET -> unsupported */
316     -1,         /* WTAP_ENCAP_ATM_RFC1483 -> unsupported */
317     -1,         /* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */
318     -1,         /* WTAP_ENCAP_LAPB -> unsupported */
319     -1,         /* WTAP_ENCAP_ATM_SNIFFER -> unsupported */
320     -1          /* WTAP_ENCAP_NULL -> unsupported */
321 };
322 #define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
323
324 /* Returns 0 if we could write the specified encapsulation type,
325    an error indication otherwise. */
326 int netxray_dump_can_write_encap(int filetype, int encap)
327 {
328     /* Per-packet encapsulations aren't supported. */
329     if (encap == WTAP_ENCAP_PER_PACKET)
330         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
331
332     if (encap < 0 || encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
333         return WTAP_ERR_UNSUPPORTED_ENCAP;
334
335     return 0;
336 }
337
338 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
339    failure */
340 gboolean netxray_dump_open_1_1(wtap_dumper *wdh, int *err)
341 {
342     /* This is a netxray file */
343     wdh->subtype_write = netxray_dump_1_1;
344     wdh->subtype_close = netxray_dump_close_1_1;
345
346     /* We can't fill in all the fields in the file header, as we
347        haven't yet written any packets.  As we'll have to rewrite
348        the header when we've written out all the packets, we just
349        skip over the header for now. */
350     fseek(wdh->fh, CAPTUREFILE_HEADER_SIZE, SEEK_SET);
351
352     wdh->private.netxray = g_malloc(sizeof(netxray_dump_t));
353     wdh->private.netxray->first_frame = TRUE;
354     wdh->private.netxray->start.tv_sec = 0;
355     wdh->private.netxray->start.tv_usec = 0;
356     wdh->private.netxray->nframes = 0;
357
358     return TRUE;
359 }
360
361 /* Write a record for a packet to a dump file.
362    Returns TRUE on success, FALSE on failure. */
363 static gboolean netxray_dump_1_1(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
364     const u_char *pd, int *err)
365 {
366     netxray_dump_t *priv = wdh->private.netxray;
367     guint32 timestamp;
368     struct netxrayrec_1_x_hdr rec_hdr;
369     int nwritten;
370
371     /* NetXRay/Windows Sniffer files have a capture start date/time
372        in the header, in a UNIX-style format, with one-second resolution,
373        and a start time stamp with microsecond resolution that's just
374        an arbitrary time stamp relative to some unknown time (boot
375        time?), and have times relative to the start time stamp in
376        the packet headers; pick the seconds value of the time stamp
377        of the first packet as the UNIX-style start date/time, and make
378        the high-resolution start time stamp 0, with the time stamp of
379        packets being the delta between the stamp of the packet and
380        the stamp of the first packet with the microseconds part 0. */
381     if (priv->first_frame) {
382         priv->first_frame = FALSE;
383         priv->start = phdr->ts;
384     }
385
386     /* build the header for each packet */
387     memset(&rec_hdr, '\0', sizeof(rec_hdr));
388     timestamp = (phdr->ts.tv_sec - priv->start.tv_sec)*1000000 +
389         phdr->ts.tv_usec;
390     rec_hdr.timelo = htolel(timestamp);
391     rec_hdr.timehi = htolel(0);
392     rec_hdr.orig_len = htoles(phdr->len);
393     rec_hdr.incl_len = htoles(phdr->caplen);
394         
395     nwritten = fwrite(&rec_hdr, 1, sizeof(rec_hdr), wdh->fh);
396     if (nwritten != sizeof(rec_hdr)) {
397         if (nwritten < 0)
398             *err = errno;
399         else
400             *err = WTAP_ERR_SHORT_WRITE;
401         return FALSE;
402     }
403
404     /* write the packet data */ 
405     nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
406     if (nwritten != phdr->caplen) {
407         if (nwritten < 0)
408             *err = errno;
409         else
410             *err = WTAP_ERR_SHORT_WRITE;
411         return FALSE;
412     }
413         
414     priv->nframes++;
415
416     return TRUE;
417 }
418
419 /* Finish writing to a dump file.
420    Returns TRUE on success, FALSE on failure. */
421 static gboolean netxray_dump_close_1_1(wtap_dumper *wdh, int *err)
422 {
423     char hdr_buf[CAPTUREFILE_HEADER_SIZE - sizeof(netxray_magic)];
424     netxray_dump_t *priv = wdh->private.netxray;
425     guint32 filelen;
426     struct netxray_hdr file_hdr;
427     int nwritten;
428
429     filelen = ftell(wdh->fh);
430
431     /* Go back to beginning */
432     fseek(wdh->fh, 0, SEEK_SET);
433
434     /* Rewrite the file header. */
435     nwritten = fwrite(netxray_magic, 1, sizeof netxray_magic, wdh->fh);
436     if (nwritten != sizeof netxray_magic) {
437         if (nwritten < 0)
438             *err = errno;
439         else
440             *err = WTAP_ERR_SHORT_WRITE;
441         return FALSE;
442     }
443
444     /* "sniffer" version ? */
445     memset(&file_hdr, '\0', sizeof file_hdr);
446     memcpy(file_hdr.version, vers_1_1, sizeof vers_1_1);
447     file_hdr.start_time = htolel(priv->start.tv_sec);
448     file_hdr.nframes = htolel(priv->nframes);
449     file_hdr.start_offset = htolel(CAPTUREFILE_HEADER_SIZE);
450     file_hdr.end_offset = htolel(filelen);
451     file_hdr.network = htoles(wtap_encap[wdh->encap]);
452     file_hdr.timelo = htolel(0);
453     file_hdr.timehi = htolel(0);
454
455     memset(hdr_buf, '\0', sizeof hdr_buf);
456     memcpy(hdr_buf, &file_hdr, sizeof(file_hdr));
457     nwritten = fwrite(hdr_buf, 1, sizeof hdr_buf, wdh->fh);
458     if (nwritten != sizeof hdr_buf) {
459         if (nwritten < 0)
460             *err = errno;
461         else
462             *err = WTAP_ERR_SHORT_WRITE;
463         return FALSE;
464     }
465         
466     return TRUE;
467 }
468