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